Skip to content

Commit

Permalink
Implemented song page
Browse files Browse the repository at this point in the history
  • Loading branch information
blastbeng committed Sep 26, 2024
1 parent 76ce78c commit 17778ba
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 66 deletions.
113 changes: 77 additions & 36 deletions spotisub/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,12 @@ def insert_playlist_relation(conn, subsonic_song_id,
conn.execute(stmt)


def select_all_playlists(missing_only=False, page=None,
limit=None, order=None, asc=None, search=None):
def select_all_playlists(conn_ext=None, missing_only=False, page=None,
limit=None, order=None, asc=None, search=None, song_uuid = None):
"""select playlists from database"""
records = []
stmt = None
with dbms.db_engine.connect() as conn:
with conn_ext if conn_ext is not None else dbms.db_engine.connect() as conn:
stmt = select(
dbms.subsonic_spotify_relation.c.uuid,
dbms.subsonic_spotify_relation.c.subsonic_song_id,
Expand All @@ -318,6 +318,7 @@ def select_all_playlists(missing_only=False, page=None,
dbms.spotify_song.c.title.label('spotify_song_title'),
dbms.spotify_song.c.spotify_uri.label('spotify_song_uri'),
dbms.spotify_song.c.album_uuid.label('spotify_album_uuid'),
dbms.spotify_album.c.spotify_uri.label('spotify_album_uri'),
dbms.spotify_album.c.name.label('spotify_album_name'),
func.group_concat(dbms.spotify_artist.c.name).label(
'spotify_artist_names'),
Expand All @@ -340,6 +341,9 @@ def select_all_playlists(missing_only=False, page=None,
stmt = stmt.where(
dbms.subsonic_spotify_relation.c.subsonic_song_id == None,
dbms.subsonic_spotify_relation.c.subsonic_artist_id == None)
if song_uuid is not None:
stmt = stmt.where(
dbms.spotify_song.c.uuid == song_uuid)
if search is not None:
stmt = stmt.filter(or_(dbms.spotify_song.c.title.ilike(f'%{search}%'),
dbms.spotify_album.c.name.ilike(
Expand All @@ -359,45 +363,52 @@ def select_all_playlists(missing_only=False, page=None,
records = cursor.fetchall()

cursor.close()
conn.close()

return records
count = count_playlists(conn, missing_only=missing_only, search=search, song_uuid = song_uuid)
if conn_ext is None:
conn.close()

return records, count

def count_playlists(missing_only=False, search=None):

def count_playlists(conn, missing_only=False, search=None, song_uuid=None):
"""select playlists from database"""
count = 0
with dbms.db_engine.connect() as conn:

query = """select count(*) from (select subsonic_spotify_relation.uuid from
subsonic_spotify_relation
join spotify_song
on subsonic_spotify_relation.spotify_song_uuid = spotify_song.uuid
join spotify_album
on spotify_song.album_uuid = spotify_album.uuid
join spotify_song_artist_relation
on spotify_song.uuid = spotify_song_artist_relation.song_uuid
join spotify_artist
on spotify_song_artist_relation.artist_uuid = spotify_artist.uuid """
if missing_only or search is not None:
query = query + " WHERE "
if missing_only:
query = query + """ subsonic_spotify_relation.subsonic_song_id is null
and subsonic_spotify_relation.subsonic_artist_id is null """
if search is not None:
if missing_only:
query = query + " and "
query = query + """ Lower(spotify_song.title) LIKE Lower('""" + search + """')
OR Lower(spotify_album.name) LIKE Lower('""" + search + """')
OR Lower(spotify_artist.name) LIKE Lower('""" + search + """')
OR Lower(subsonic_spotify_relation.subsonic_playlist_name) LIKE
Lower('""" + search + """') """

query = query + """group by subsonic_spotify_relation.spotify_song_uuid,
subsonic_spotify_relation.subsonic_playlist_id);"""

count = conn.execute(text(query)).scalar()
conn.close()
query = """select count(*) from (select subsonic_spotify_relation.uuid from
subsonic_spotify_relation
join spotify_song
on subsonic_spotify_relation.spotify_song_uuid = spotify_song.uuid
join spotify_album
on spotify_song.album_uuid = spotify_album.uuid
join spotify_song_artist_relation
on spotify_song.uuid = spotify_song_artist_relation.song_uuid
join spotify_artist
on spotify_song_artist_relation.artist_uuid = spotify_artist.uuid """
where = ""
if missing_only:
where = where + """ subsonic_spotify_relation.subsonic_song_id is null
and subsonic_spotify_relation.subsonic_artist_id is null """
if search is not None:
if where != "":
where = where + " and "
where = where + """ Lower(spotify_song.title) LIKE Lower('""" + search + """')
OR Lower(spotify_album.name) LIKE Lower('""" + search + """')
OR Lower(spotify_artist.name) LIKE Lower('""" + search + """')
OR Lower(subsonic_spotify_relation.subsonic_playlist_name) LIKE
Lower('""" + search + """') """
if song_uuid is not None:
if where != "":
where = where + " and "
where = where + " spotify_song.uuid = '" + song_uuid + "' "

if where != "":
query = query + " where " + where

query = query + """group by subsonic_spotify_relation.spotify_song_uuid,
subsonic_spotify_relation.subsonic_playlist_id);"""

count = conn.execute(text(query)).scalar()

return count

Expand Down Expand Up @@ -466,6 +477,24 @@ def select_spotify_song_by_uri(conn, spotify_uri: str):

return value

def select_spotify_song_by_uuid(conn, uuid: str):
"""select spotify song by uri"""
value = None
stmt = select(
dbms.spotify_song.c.uuid,
dbms.spotify_song.c.spotify_uri,
dbms.spotify_song.c.title).where(
dbms.spotify_song.c.uuid == uuid)
stmt.compile()
cursor = conn.execute(stmt)
records = cursor.fetchall()

for row in records:
value = row
cursor.close()

return value


def insert_spotify_artist(conn, artist_spotify):
"""insert spotify artist"""
Expand Down Expand Up @@ -587,6 +616,18 @@ def get_album_and_songs(uuid: str, page=None,
conn.close()
return artist, songs, count

def get_song_and_playlists(uuid: str, page=None,
limit=None, order=None, asc=None):
songs = None
artist = None
count = 0
with dbms.db_engine.connect() as conn:
song = select_spotify_song_by_uuid(conn, uuid)
playlists, count = select_all_playlists(
conn_ext=conn, page=page, limit=limit, order=order, asc=asc, song_uuid=uuid)
conn.close()
return song, playlists, count


def select_spotify_artist_by_uuid(conn, uuid: str):
"""select spotify artist by uri"""
Expand Down
8 changes: 8 additions & 0 deletions spotisub/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@ class SpotifyApiException(Exception):
"Please set up your Spotify API Keys"


class SpotifyDataException(Exception):
"Error loading data from Spotify"


class SubsonicOfflineException(Exception):
"Subsonic is Offline"


class SubsonicDataException(Exception):
"Subsonic is Offline"
70 changes: 62 additions & 8 deletions spotisub/helpers/subsonic_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from spotisub import utils
from spotisub.exceptions import SubsonicOfflineException
from spotisub.exceptions import SpotifyApiException
from spotisub.exceptions import SpotifyDataException
from spotisub.classes import ComparisonHelper
from spotisub.helpers import musicbrainz_helper

Expand Down Expand Up @@ -55,6 +56,7 @@
playlist_cache = ExpiringDict(max_len=500, max_age_seconds=300)
spotify_artist_cache = ExpiringDict(max_len=500, max_age_seconds=300)
spotify_album_cache = ExpiringDict(max_len=500, max_age_seconds=300)
spotify_song_cache = ExpiringDict(max_len=500, max_age_seconds=300)


def check_pysonic_connection():
Expand Down Expand Up @@ -383,15 +385,11 @@ def match_with_subsonic_track(
return comparison_helper


def count_playlists(missing_only=False, search=None):
return database.count_playlists(missing_only=missing_only, search=search)


def select_all_playlists(missing_only=False, page=None,
limit=None, order=None, asc=None, search=None):
"""get list of playlists and songs"""
try:
playlist_songs = database.select_all_playlists(
playlist_songs, count = database.select_all_playlists(
missing_only=missing_only,
page=page,
limit=limit,
Expand All @@ -411,7 +409,7 @@ def select_all_playlists(missing_only=False, page=None,
return select_all_playlists(
missing_only=missing_only, page=page, limit=limit)

return playlist_songs
return playlist_songs, count
except SubsonicOfflineException as ex:
raise ex

Expand Down Expand Up @@ -514,14 +512,17 @@ def load_artist(uuid, spotipy_helper, page=None,
spotify_artist = spotify_artist_cache[uuid]

if spotify_artist is None:
raise SpotifyApiException
raise SpotifyDataException
artist = {}
artist["name"] = artist_db.name
artist["genres"] = ""
artist["url"] = ""
artist["image"] = ""
artist["popularity"] = ""
if "genres" in spotify_artist:
artist["genres"] = ", ".join(spotify_artist["genres"])
if "popularity" in spotify_artist:
artist["popularity"] = str(spotify_artist["popularity"]) + "%"
if "external_urls" in spotify_artist and "spotify" in spotify_artist["external_urls"]:
artist["url"] = spotify_artist["external_urls"]["spotify"]
if "images" in spotify_artist and len(spotify_artist["images"]) > 0:
Expand All @@ -545,14 +546,67 @@ def load_album(uuid, spotipy_helper, page=None,
spotify_album = spotify_album_cache[uuid]

if spotify_album is None:
raise SpotifyApiException
raise SpotifyDataException
album = {}
album["name"] = album_db.name
album["url"] = ""
album["image"] = ""
album["release_date"] = ""
if "release_date" in spotify_album:
album["release_date"] = spotify_album["release_date"]
if "external_urls" in spotify_album and "spotify" in spotify_album["external_urls"]:
album["url"] = spotify_album["external_urls"]["spotify"]
if "images" in spotify_album and len(spotify_album["images"]) > 0:
album["image"] = spotify_album["images"][0]["url"]

return album, songs, count


def load_song(uuid, spotipy_helper, page=None,
limit=None, order=None, asc=None):
song_db, songs, count = database.get_song_and_playlists(
uuid, page=page, limit=limit, order=order, asc=asc)
sp = None

spotify_song = None

if uuid not in spotify_song_cache:
sp = sp if sp is not None else spotipy_helper.get_spotipy_client()
spotify_song = sp.track(song_db.spotify_uri)
spotify_song_cache[uuid] = spotify_song
else:
spotify_song = spotify_song_cache[uuid]

if spotify_song is None:
raise SpotifyDataException

song = {}
song["name"] = song_db.title
song["url"] = ""
song["image"] = ""
song["popularity"] = ""
song["preview"] = ""

if "preview_url" in spotify_song:
song["preview_url"] = spotify_song["preview_url"]
if "popularity" in spotify_song:
song["popularity"] = str(spotify_song["popularity"]) + "%"
if "external_urls" in spotify_song and "spotify" in spotify_song["external_urls"]:
song["url"] = spotify_song["external_urls"]["spotify"]

if len(songs) > 0:
spotify_album = None
if songs[0].spotify_album_uuid not in spotify_album_cache:
sp = sp if sp is not None else spotipy_helper.get_spotipy_client()
spotify_album = sp.album(songs[0].spotify_album_uri)
spotify_album_cache[songs[0].spotify_album_uuid] = spotify_album
else:
spotify_album = spotify_album_cache[songs[0].spotify_album_uuid]

if spotify_album is None:
raise SpotifyDataException

if "images" in spotify_album and len(spotify_album["images"]) > 0:
song["image"] = spotify_album["images"][0]["url"]

return song, songs, count
42 changes: 38 additions & 4 deletions spotisub/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,8 @@ def playlists(missing_only=0, page=1, limit=25,
title = 'Missing' if missing_only == 1 else 'Manage'
try:
missing_bool = True if missing_only == 1 else False
playlists = subsonic_helper.select_all_playlists(
playlists, song_count = subsonic_helper.select_all_playlists(
missing_only=missing_bool, page=page - 1, limit=limit, order=order, asc=(asc == 1), search=search)
song_count = subsonic_helper.count_playlists(
missing_only=missing_bool, search=search)
total_pages = math.ceil(song_count / limit)
pagination_array, prev_page, next_page = utils.get_pagination(
page, total_pages)
Expand All @@ -119,6 +117,43 @@ def playlists(missing_only=0, page=1, limit=25,
title=title,
errors=["Unable to communicate with Spotify.", "Please check your configuration."])

@spotisub.route('/song/<string:uuid>/')
@spotisub.route('/song/<string:uuid>/<int:page>/')
@spotisub.route('/song/<string:uuid>/<int:page>/<int:limit>/')
@spotisub.route('/song/<string:uuid>/<int:page>/<int:limit>/<string:order>/')
@spotisub.route('/song/<string:uuid>/<int:page>/<int:limit>/<string:order>/<int:asc>/')
@login_required
def song(uuid=None, page=1, limit=25, order='spotify_song.title', asc=1):
title = 'Song'
try:
spotipy_helper.get_secrets()
song, songs, song_count = subsonic_helper.load_song(
uuid, spotipy_helper, page=page-1, limit=limit, order=order, asc=(asc == 1))
total_pages = math.ceil(song_count / limit)
pagination_array, prev_page, next_page = utils.get_pagination(
page, total_pages)
return render_template('song.html',
title=title,
song=song,
uuid=uuid,
songs=songs,
pagination_array=pagination_array,
prev_page=prev_page,
next_page=next_page,
current_page=page,
total_pages=total_pages,
limit=limit,
result_size=song_count,
order=order,
asc=asc)
except SubsonicOfflineException:
return render_template('errors/404.html',
title=title,
errors=["Unable to communicate with Subsonic.", "Please check your configuration and make sure your instance is online."])
except (SpotifyException, SpotifyApiException) as e:
return render_template('errors/404.html',
title=title,
errors=["Unable to communicate with Spotify.", "Please check your configuration."])

@spotisub.route('/album/<string:uuid>/')
@spotisub.route('/album/<string:uuid>/<int:page>/')
Expand Down Expand Up @@ -197,7 +232,6 @@ def artist(uuid=None, page=1, limit=25, order='spotify_song.title', asc=1):
title=title,
errors=["Unable to communicate with Spotify.", "Please check your configuration."])


@spotisub.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
Expand Down
Loading

0 comments on commit 17778ba

Please sign in to comment.