mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
Merge branch 'master' of github.com:swingmx/swingmusic
This commit is contained in:
@@ -110,7 +110,7 @@ def run(*args, **kwargs):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
multiprocessing.freeze_support()
|
multiprocessing.freeze_support()
|
||||||
multiprocessing.set_start_method("spawn")
|
multiprocessing.set_start_method("fork")
|
||||||
run()
|
run()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import shutil
|
|||||||
from time import time
|
from time import time
|
||||||
from flask_openapi3 import Tag
|
from flask_openapi3 import Tag
|
||||||
from flask_openapi3 import APIBlueprint
|
from flask_openapi3 import APIBlueprint
|
||||||
|
import sqlalchemy.exc
|
||||||
from swingmusic.api.auth import admin_required
|
from swingmusic.api.auth import admin_required
|
||||||
|
|
||||||
from swingmusic.db.userdata import FavoritesTable, PlaylistTable, ScrobbleTable
|
from swingmusic.db.userdata import FavoritesTable, PlaylistTable, ScrobbleTable
|
||||||
@@ -96,6 +97,9 @@ def backup():
|
|||||||
|
|
||||||
|
|
||||||
class RestoreBackup:
|
class RestoreBackup:
|
||||||
|
# TODO: BACKUP AND RESTORE COLLECTIONS & MIXES!
|
||||||
|
# TODO: IMPROVE UX WHEN WAITING FOR RESTORE TO COMPLETE!
|
||||||
|
|
||||||
def __init__(self, backup_dir: Path):
|
def __init__(self, backup_dir: Path):
|
||||||
self.backup_dir = backup_dir
|
self.backup_dir = backup_dir
|
||||||
self.backup_file = backup_dir / "data.json"
|
self.backup_file = backup_dir / "data.json"
|
||||||
@@ -114,8 +118,12 @@ class RestoreBackup:
|
|||||||
existing_hashes = set(fav.hash for fav in existing_favorites)
|
existing_hashes = set(fav.hash for fav in existing_favorites)
|
||||||
new_favorites = [fav for fav in favorites if fav["hash"] not in existing_hashes]
|
new_favorites = [fav for fav in favorites if fav["hash"] not in existing_hashes]
|
||||||
|
|
||||||
if new_favorites:
|
for fav in new_favorites:
|
||||||
FavoritesTable.insert_many(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]):
|
def restore_playlists(self, playlists: list[dict]):
|
||||||
existing_playlists = PlaylistTable.get_all()
|
existing_playlists = PlaylistTable.get_all()
|
||||||
@@ -124,8 +132,15 @@ class RestoreBackup:
|
|||||||
playlist for playlist in playlists if playlist["name"] not in existing_names
|
playlist for playlist in playlists if playlist["name"] not in existing_names
|
||||||
]
|
]
|
||||||
|
|
||||||
if new_playlists:
|
for playlist in new_playlists:
|
||||||
PlaylistTable.insert_many(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]):
|
def restore_scrobbles(self, scrobbles: list[dict]):
|
||||||
existing_scrobbles = ScrobbleTable.get_all(0)
|
existing_scrobbles = ScrobbleTable.get_all(0)
|
||||||
@@ -139,8 +154,13 @@ class RestoreBackup:
|
|||||||
if f"{scrobble['trackhash']}.{scrobble['timestamp']}" not in existing_hashes
|
if f"{scrobble['trackhash']}.{scrobble['timestamp']}" not in existing_hashes
|
||||||
]
|
]
|
||||||
|
|
||||||
if new_scrobbles:
|
for scrobble in new_scrobbles:
|
||||||
ScrobbleTable.insert_many(new_scrobbles)
|
try:
|
||||||
|
ScrobbleTable.add(scrobble)
|
||||||
|
except sqlalchemy.exc.IntegrityError:
|
||||||
|
print("Integrity error, skipping scrobble:")
|
||||||
|
print(scrobble)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RestoreBackupBody(BaseModel):
|
class RestoreBackupBody(BaseModel):
|
||||||
|
|||||||
@@ -219,8 +219,11 @@ class FavoritesTable(Base):
|
|||||||
# guard against hash collisions for different item types
|
# guard against hash collisions for different item types
|
||||||
item["hash"] = f"{item['type']}_{item['hash']}"
|
item["hash"] = f"{item['type']}_{item['hash']}"
|
||||||
|
|
||||||
item["timestamp"] = int(datetime.datetime.now().timestamp())
|
if item.get("timestamp") is None:
|
||||||
item["userid"] = get_current_userid()
|
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))
|
return next(cls.execute(insert(cls).values(item), commit=True))
|
||||||
|
|
||||||
@@ -337,7 +340,9 @@ class ScrobbleTable(Base):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, item: dict[str, Any]):
|
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)
|
return cls.insert_one(item)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ class BecauseYouListened(HomepageRoutine):
|
|||||||
MixesPlugin().get_because_items(list(entry.values()))
|
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[
|
HomepageStore.entries[self.store_keys[0]].items[
|
||||||
user.id
|
user.id
|
||||||
] = because_you_listened_to_artist
|
] = because_you_listened_to_artist
|
||||||
|
|||||||
@@ -531,7 +531,23 @@ class MixesPlugin(Plugin):
|
|||||||
artists: dict[str, list[dict[str, str | int]]] = {}
|
artists: dict[str, list[dict[str, str | int]]] = {}
|
||||||
albums: 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"]
|
mix_artisthash = mix.extra["artisthash"]
|
||||||
artists.setdefault(mix_artisthash, [])
|
artists.setdefault(mix_artisthash, [])
|
||||||
albums.setdefault(mix_artisthash, [])
|
albums.setdefault(mix_artisthash, [])
|
||||||
@@ -582,11 +598,10 @@ class MixesPlugin(Plugin):
|
|||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
artisthash = mixes[0].extra["artisthash"]
|
|
||||||
because_you_listened_to_artist = {
|
because_you_listened_to_artist = {
|
||||||
"title": "Because you listened to "
|
"title": "Because you listened to "
|
||||||
+ ArtistStore.artistmap[artisthash].artist.name,
|
+ pivot_artist.name,
|
||||||
"items": albums[artisthash][:15],
|
"items": albums[pivot_artist.artisthash][:15],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Flatten list of artists and remove duplicates by artisthash
|
# Flatten list of artists and remove duplicates by artisthash
|
||||||
@@ -601,7 +616,7 @@ class MixesPlugin(Plugin):
|
|||||||
|
|
||||||
artists_you_might_like = {
|
artists_you_might_like = {
|
||||||
"title": "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
|
return because_you_listened_to_artist, artists_you_might_like
|
||||||
|
|||||||
Reference in New Issue
Block a user