From e3a61c109bbd249dbc1f733882e4b4fa72043fcf Mon Sep 17 00:00:00 2001 From: mungai-njoroge Date: Tue, 22 Aug 2023 15:36:43 +0300 Subject: [PATCH] implement pinning playlists + update migrations to add pinned attr to plalist + misc --- app/api/playlist.py | 76 +++++++++++++++---------------- app/db/sqlite/playlists.py | 52 ++++++++++----------- app/lib/taglib.py | 4 +- app/migrations/v1_3_0/__init__.py | 3 +- app/models/playlist.py | 4 +- 5 files changed, 68 insertions(+), 71 deletions(-) diff --git a/app/api/playlist.py b/app/api/playlist.py index c43e3cd..1b163d7 100644 --- a/app/api/playlist.py +++ b/app/api/playlist.py @@ -97,7 +97,12 @@ def insert_playlist(name: str, image: str = None): "name": name, "trackhashes": json.dumps([]), "settings": json.dumps( - {"has_gif": False, "banner_pos": 50, "square_img": True if image else False} + { + "has_gif": False, + "banner_pos": 50, + "square_img": True if image else False, + "pinned": False, + } ), } @@ -170,12 +175,12 @@ def add_item_to_playlist(playlist_id: str): itemtype = None try: - itemhash = data["itemhash"] + itemhash: str = data["itemhash"] except KeyError: itemhash = None - if itemtype == "track": - trackhashes = [itemhash] + if itemtype == "tracks": + trackhashes = itemhash.split(",") elif itemtype == "folder": trackhashes = get_path_trackhashes(itemhash) elif itemtype == "album": @@ -190,8 +195,6 @@ def add_item_to_playlist(playlist_id: str): if insert_count == 0: return {"error": "Item already exists in playlist"}, 409 - PL.update_last_updated(int(playlist_id)) - return {"msg": "Done"}, 200 @@ -220,13 +223,6 @@ def get_playlist(playlistid: str): if not playlist.has_image: playlist.images = get_first_4_images(tracks) - if len(playlist.images) > 2: - # swap 3rd image with first (3rd image is the visible image in UI) - playlist.images[2], playlist.images[0] = ( - playlist.images[0], - playlist.images[2], - ) - playlist.clear_lists() return {"info": playlist, "tracks": tracks if not no_tracks else []} @@ -288,6 +284,28 @@ def update_playlist_info(playlistid: str): } +@api.route("/playlist//pin_unpin", methods=["GET"]) +def pin_unpin_playlist(playlistid: str): + """ + Pins or unpins a playlist. + """ + playlist = PL.get_playlist_by_id(int(playlistid)) + + if playlist is None: + return {"error": "Playlist not found"}, 404 + + settings = playlist.settings + + try: + settings["pinned"] = not settings["pinned"] + except KeyError: + settings["pinned"] = True + + PL.update_settings(int(playlistid), settings) + + return {"msg": "Done"}, 200 + + @api.route("/playlist//remove-img", methods=["GET"]) def remove_playlist_image(playlistid: str): """ @@ -308,7 +326,6 @@ def remove_playlist_image(playlistid: str): playlist.images = get_first_4_images(trackhashes=playlist.trackhashes) playlist.last_updated = date_string_to_time_passed(playlist.last_updated) - PL.update_last_updated(pid) return {"playlist": playlist}, 200 @@ -334,24 +351,6 @@ def remove_playlist(): return {"msg": "Done"}, 200 -@api.route("/playlist//set-image-pos", methods=["POST"]) -def update_image_position(pid: int): - data = request.get_json() - message = {"msg": "No data provided"} - - if data is None: - return message, 400 - - try: - pos = data["pos"] - except KeyError: - return message, 400 - - PL.update_banner_pos(pid, pos) - - return {"msg": "Image position saved"}, 200 - - @api.route("/playlist//remove-tracks", methods=["POST"]) def remove_tracks_from_playlist(pid: int): data = request.get_json() @@ -366,7 +365,6 @@ def remove_tracks_from_playlist(pid: int): tracks = data["tracks"] PL.remove_tracks_from_playlist(pid, tracks) - PL.update_last_updated(pid) return {"msg": "Done"}, 200 @@ -404,30 +402,29 @@ def save_item_as_playlist(): if itemtype is None or playlist_name is None or itemhash is None: return msg - if itemtype == "track": - trackhashes = [itemhash] + if itemtype == "tracks": + trackhashes = itemhash.split(",") elif itemtype == "folder": trackhashes = get_path_trackhashes(itemhash) elif itemtype == "album": trackhashes = get_album_trackhashes(itemhash) elif itemtype == "artist": trackhashes = get_artist_trackhashes(itemhash) - elif itemtype == "queue": - trackhashes = itemhash.split(",") else: trackhashes = [] if len(trackhashes) == 0: return {"error": "No tracks founds"}, 404 - image = itemhash + ".webp" if itemtype != "folder" and itemtype != "queue" else None + image = itemhash + ".webp" if itemtype != "folder" and itemtype != "tracks" else None playlist = insert_playlist(playlist_name, image) if playlist is None: return {"error": "Playlist could not be created"}, 500 - if itemtype != "folder" and itemtype != "queue": + # save image + if itemtype != "folder" and itemtype != "tracks": filename = itemhash + ".webp" base_path = ( @@ -444,6 +441,5 @@ def save_item_as_playlist(): ) PL.add_tracks_to_playlist(playlist.id, trackhashes) - PL.update_last_updated(playlist.id) return {"playlist": playlist}, 201 diff --git a/app/db/sqlite/playlists.py b/app/db/sqlite/playlists.py index 0777ad3..85f2681 100644 --- a/app/db/sqlite/playlists.py +++ b/app/db/sqlite/playlists.py @@ -13,6 +13,14 @@ class SQLitePlaylistMethods: This class contains methods for interacting with the playlists table. """ + @staticmethod + def update_last_updated(playlist_id: int): + """Updates the last updated date of a playlist.""" + sql = """UPDATE playlists SET last_updated = ? WHERE id = ?""" + + with SQLiteManager(userdata_db=True) as cur: + cur.execute(sql, (create_new_date(), playlist_id)) + @staticmethod def insert_one_playlist(playlist: dict): # banner_pos, @@ -92,8 +100,8 @@ class SQLitePlaylistMethods: # FIXME: Extract the "add_track_to_playlist" method to use it for both the artisthash and trackhash lists. - @staticmethod - def add_item_to_json_list(playlist_id: int, field: str, items: set[str]): + @classmethod + def add_item_to_json_list(cls, playlist_id: int, field: str, items: set[str]): """ Adds a string item to a json dumped list using a playlist id and field name. Takes the playlist ID, a field name, an item to add to the field. @@ -118,6 +126,8 @@ class SQLitePlaylistMethods: cur.execute(sql, (json.dumps(db_items), playlist_id)) return len(items) + cls.update_last_updated(playlist_id) + @classmethod def add_tracks_to_playlist(cls, playlist_id: int, trackhashes: list[str]): """ @@ -125,8 +135,8 @@ class SQLitePlaylistMethods: """ return cls.add_item_to_json_list(playlist_id, "trackhashes", trackhashes) - @staticmethod - def update_playlist(playlist_id: int, playlist: dict): + @classmethod + def update_playlist(cls, playlist_id: int, playlist: dict): sql = """UPDATE playlists SET image = ?, last_updated = ?, @@ -145,13 +155,16 @@ class SQLitePlaylistMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, params) - @staticmethod - def update_last_updated(playlist_id: int): - """Updates the last updated date of a playlist.""" - sql = """UPDATE playlists SET last_updated = ? WHERE id = ?""" + cls.update_last_updated(playlist_id) + + @classmethod + def update_settings(cls, playlist_id: int, settings: dict): + sql = """UPDATE playlists SET settings = ? WHERE id = ?""" with SQLiteManager(userdata_db=True) as cur: - cur.execute(sql, (create_new_date(), playlist_id)) + cur.execute(sql, (json.dumps(settings), playlist_id)) + + cls.update_last_updated(playlist_id) @staticmethod def delete_playlist(pid: str): @@ -160,21 +173,6 @@ class SQLitePlaylistMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (pid,)) - @staticmethod - def update_banner_pos(playlistid: int, pos: int): - playlist = SQLitePlaylistMethods.get_playlist_by_id(playlistid) - - if playlist is None: - return - - playlist.settings["banner_pos"] = pos - settings_str = json.dumps(playlist.settings) - - sql = """UPDATE playlists SET settings = ? WHERE id = ?""" - - with SQLiteManager(userdata_db=True) as cur: - cur.execute(sql, (settings_str, playlistid)) - @staticmethod def remove_banner(playlistid: int): sql = """UPDATE playlists SET image = NULL WHERE id = ?""" @@ -182,8 +180,8 @@ class SQLitePlaylistMethods: with SQLiteManager(userdata_db=True) as cur: cur.execute(sql, (playlistid,)) - @staticmethod - def remove_tracks_from_playlist(playlistid: int, tracks: list[dict[str, int]]): + @classmethod + def remove_tracks_from_playlist(cls, playlistid: int, tracks: list[dict[str, int]]): """ Removes tracks from a playlist by trackhash and position. """ @@ -211,3 +209,5 @@ class SQLitePlaylistMethods: trackhashes.remove(track["trackhash"]) cur.execute(sql, (json.dumps(trackhashes), playlistid)) + + cls.update_last_updated(playlistid) diff --git a/app/lib/taglib.py b/app/lib/taglib.py index e409ecd..007464c 100644 --- a/app/lib/taglib.py +++ b/app/lib/taglib.py @@ -40,8 +40,8 @@ def extract_thumb(filepath: str, webp_path: str, overwrite=False) -> bool: img.resize((tsize, tsize), Image.ANTIALIAS).save(lg_img_path, "webp") img.resize((sm_tsize, sm_tsize), Image.ANTIALIAS).save(sm_img_path, "webp") - if not overwrite and os.path.exists(lg_img_path): - img_size = os.path.getsize(lg_img_path) + if not overwrite and os.path.exists(sm_img_path): + img_size = os.path.getsize(sm_img_path) if img_size > 0: return True diff --git a/app/migrations/v1_3_0/__init__.py b/app/migrations/v1_3_0/__init__.py index 39a51de..7a7b8d7 100644 --- a/app/migrations/v1_3_0/__init__.py +++ b/app/migrations/v1_3_0/__init__.py @@ -69,8 +69,6 @@ class AddSettingsToPlaylistTable(Migration): @staticmethod def migrate(): - # existing_playlists = [] - select_playlists_sql = "SELECT * FROM playlists" with SQLiteManager(userdata_db=True) as cur: @@ -117,6 +115,7 @@ class AddSettingsToPlaylistTable(Migration): "has_gif": False, "banner_pos": playlist[1], "square_img": False, + "pinned": False, } ), } diff --git a/app/models/playlist.py b/app/models/playlist.py index 21807dd..cf6d757 100644 --- a/app/models/playlist.py +++ b/app/models/playlist.py @@ -22,13 +22,15 @@ class Playlist: duration: int = 0 has_image: bool = False images: list[str] = dataclasses.field(default_factory=list) + pinned: bool = False def __post_init__(self): self.trackhashes = json.loads(str(self.trackhashes)) self.count = len(self.trackhashes) if isinstance(self.settings, str): - self.settings = json.loads(self.settings) + self.settings = dict(json.loads(self.settings)) + self.pinned = self.settings.get("pinned", False) self.has_image = ( Path(settings.Paths.get_playlist_img_path()) / str(self.image)