diff --git a/swingmusic/__main__.py b/swingmusic/__main__.py index d0236a39..a4acb5fe 100644 --- a/swingmusic/__main__.py +++ b/swingmusic/__main__.py @@ -110,7 +110,7 @@ def run(*args, **kwargs): def main(): multiprocessing.freeze_support() - multiprocessing.set_start_method("spawn") + multiprocessing.set_start_method("fork") run() if __name__ == "__main__": diff --git a/swingmusic/api/backup_and_restore.py b/swingmusic/api/backup_and_restore.py index 771544c1..3fe3fea8 100644 --- a/swingmusic/api/backup_and_restore.py +++ b/swingmusic/api/backup_and_restore.py @@ -7,6 +7,7 @@ import shutil from time import time from flask_openapi3 import Tag from flask_openapi3 import APIBlueprint +import sqlalchemy.exc from swingmusic.api.auth import admin_required from swingmusic.db.userdata import FavoritesTable, PlaylistTable, ScrobbleTable @@ -96,6 +97,9 @@ def backup(): class RestoreBackup: + # TODO: BACKUP AND RESTORE COLLECTIONS & MIXES! + # TODO: IMPROVE UX WHEN WAITING FOR RESTORE TO COMPLETE! + def __init__(self, backup_dir: Path): self.backup_dir = backup_dir self.backup_file = backup_dir / "data.json" @@ -114,8 +118,12 @@ class RestoreBackup: existing_hashes = set(fav.hash for fav in existing_favorites) new_favorites = [fav for fav in favorites if fav["hash"] not in existing_hashes] - if new_favorites: - FavoritesTable.insert_many(new_favorites) + for fav in new_favorites: + try: + FavoritesTable.insert_item(fav) + except sqlalchemy.exc.IntegrityError: + print("Integrity error, skipping favorite") + print(fav) def restore_playlists(self, playlists: list[dict]): existing_playlists = PlaylistTable.get_all() @@ -124,8 +132,15 @@ class RestoreBackup: playlist for playlist in playlists if playlist["name"] not in existing_names ] - if new_playlists: - PlaylistTable.insert_many(new_playlists) + for playlist in new_playlists: + try: + if playlist.get("_score") is not None: + del playlist["_score"] + + PlaylistTable.add_one(playlist) + except sqlalchemy.exc.IntegrityError: + print("Integrity error, skipping playlist:") + print(playlist) def restore_scrobbles(self, scrobbles: list[dict]): existing_scrobbles = ScrobbleTable.get_all(0) @@ -139,8 +154,13 @@ class RestoreBackup: if f"{scrobble['trackhash']}.{scrobble['timestamp']}" not in existing_hashes ] - if new_scrobbles: - ScrobbleTable.insert_many(new_scrobbles) + for scrobble in new_scrobbles: + try: + ScrobbleTable.add(scrobble) + except sqlalchemy.exc.IntegrityError: + print("Integrity error, skipping scrobble:") + print(scrobble) + class RestoreBackupBody(BaseModel): diff --git a/swingmusic/db/userdata.py b/swingmusic/db/userdata.py index 525d7a33..1ae4db45 100644 --- a/swingmusic/db/userdata.py +++ b/swingmusic/db/userdata.py @@ -219,8 +219,11 @@ class FavoritesTable(Base): # guard against hash collisions for different item types item["hash"] = f"{item['type']}_{item['hash']}" - item["timestamp"] = int(datetime.datetime.now().timestamp()) - item["userid"] = get_current_userid() + if item.get("timestamp") is None: + item["timestamp"] = int(datetime.datetime.now().timestamp()) + + if item.get("userid") is None: + item["userid"] = get_current_userid() return next(cls.execute(insert(cls).values(item), commit=True)) @@ -337,7 +340,9 @@ class ScrobbleTable(Base): @classmethod def add(cls, item: dict[str, Any]): - item["userid"] = get_current_userid() + if item.get("userid") is None: + item["userid"] = get_current_userid() + return cls.insert_one(item) @classmethod diff --git a/swingmusic/lib/recipes/because.py b/swingmusic/lib/recipes/because.py index 453d2489..e8f241d3 100644 --- a/swingmusic/lib/recipes/because.py +++ b/swingmusic/lib/recipes/because.py @@ -29,6 +29,9 @@ class BecauseYouListened(HomepageRoutine): MixesPlugin().get_because_items(list(entry.values())) ) + if not because_you_listened_to_artist or not artists_you_might_like: + continue + HomepageStore.entries[self.store_keys[0]].items[ user.id ] = because_you_listened_to_artist diff --git a/swingmusic/plugins/mixes.py b/swingmusic/plugins/mixes.py index dbcc6122..a5bb1799 100644 --- a/swingmusic/plugins/mixes.py +++ b/swingmusic/plugins/mixes.py @@ -531,7 +531,23 @@ class MixesPlugin(Plugin): artists: dict[str, list[dict[str, str | int]]] = {} albums: dict[str, list[dict[str, str | int]]] = {} - for mix in mixes: + pivot_artist = None + pivot_artist_index = None + + # Get pivot artist + for index, mix in enumerate(mixes): + artist = ArtistStore.artistmap.get(mix.extra["artisthash"]) + if not artist: + continue + + pivot_artist = artist.artist + pivot_artist_index = index + break + + if not pivot_artist: + return None, None + + for mix in mixes[pivot_artist_index:]: mix_artisthash = mix.extra["artisthash"] artists.setdefault(mix_artisthash, []) albums.setdefault(mix_artisthash, []) @@ -582,11 +598,10 @@ class MixesPlugin(Plugin): reverse=True, ) - artisthash = mixes[0].extra["artisthash"] because_you_listened_to_artist = { "title": "Because you listened to " - + ArtistStore.artistmap[artisthash].artist.name, - "items": albums[artisthash][:15], + + pivot_artist.name, + "items": albums[pivot_artist.artisthash][:15], } # Flatten list of artists and remove duplicates by artisthash @@ -601,7 +616,7 @@ class MixesPlugin(Plugin): artists_you_might_like = { "title": "Artists you might like", - "items": artists[artisthash][:15], + "items": artists[pivot_artist.artisthash][:15], } return because_you_listened_to_artist, artists_you_might_like