diff --git a/.gitignore b/.gitignore index b2412028..8b104bf5 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ logs.txt testdata.py test.py nohup.out +*s.json diff --git a/app/api/plugins/mixes.py b/app/api/plugins/mixes.py index 168b6a1c..3fe3f08b 100644 --- a/app/api/plugins/mixes.py +++ b/app/api/plugins/mixes.py @@ -78,8 +78,7 @@ def get_mix(query: MixQuery): return {"msg": "Mix not found"}, 404 if mixtype == "custom_mixes": - plugin = MixesPlugin() - mix = plugin.get_track_mix(mix) + mix = MixesPlugin.get_track_mix(mix) if not mix: return {"msg": "Mix not found"}, 404 diff --git a/app/api/scrobble/__init__.py b/app/api/scrobble/__init__.py index 834fe06f..212e7973 100644 --- a/app/api/scrobble/__init__.py +++ b/app/api/scrobble/__init__.py @@ -72,6 +72,8 @@ def log_track(body: LogTrackBody): return {"msg": "Track not found."}, 404 scrobble_data = dict(body) + # REVIEW: Do we need to store the extra info in the database? + # OR .... can we just write it to the backup file on demand? scrobble_data["extra"] = get_extra_info(body.trackhash, "track") ScrobbleTable.add(scrobble_data) diff --git a/app/lib/home/__init__.py b/app/lib/home/__init__.py index a762a66d..00446544 100644 --- a/app/lib/home/__init__.py +++ b/app/lib/home/__init__.py @@ -1,303 +1,30 @@ -from app.store.albums import AlbumStore -from app.store.artists import ArtistStore -from app.store.folder import FolderStore -from app.store.tracks import TrackStore - -from app.models.logger import TrackLog -from app.lib.playlistlib import get_first_4_images -from app.utils.dates import timestamp_to_time_passed -from app.lib.home.recentlyadded import get_recently_added_playlist -from app.db.userdata import FavoritesTable, MixTable, PlaylistTable -from app.lib.home.recentlyplayed import get_recently_played_playlist - -from app.serializers.track import serialize_track -from app.serializers.album import album_serializer -from app.serializers.artist import serialize_for_card -from app.serializers.playlist import serialize_for_card as serialize_playlist +from app.db.userdata import MixTable +from app.plugins.mixes import MixesPlugin -def create_items(entries: list[TrackLog], limit: int): - custom_playlists = [ - {"name": "recentlyadded", "handler": get_recently_added_playlist}, - {"name": "recentlyplayed", "handler": get_recently_played_playlist}, - ] +def find_mix(mixid: str, sourcehash: str): + """ + Find a mix in the homepage store or the db. + """ + from app.store.homepage import HomepageStore - items = [] - added = set() + mixtype = "custom_mixes" if mixid[0] == "t" else "artist_mixes" - for entry in entries: - if len(items) >= limit: - break + # INFO: Try getting the mix from the homepage store + mix = HomepageStore.get_mix(mixtype, mixid) + if mix and mix["sourcehash"] == sourcehash: + return mix - if entry.source in added: - continue + # INFO: Get the mix from the db + mix = MixTable.get_by_sourcehash(sourcehash) - added.add(entry.source) + if not mix: + return None - if entry.type == "mix": - if not entry.type_src: - continue + if mixtype == "custom_mixes": + mix = MixesPlugin.get_track_mix(mix) - # INFO: Get mix from homepage store - from app.store.homepage import HomepageStore - mix = HomepageStore.find_mix(entry.type_src) + if not mix: + return None - if not mix and entry.type_src.startswith("t"): - # mix is a track mix (not saved in the db) - continue - - if not mix: - # INFO: Get mix from db - mix = MixTable.get_by_mixid(entry.type_src) - - if not mix: - continue - - items.append( - { - "type": "mix", - "hash": entry.type_src, - "timestamp": entry.timestamp, - } - ) - continue - - if entry.type == "album": - album = AlbumStore.albummap.get(entry.type_src) - - if album is None: - continue - - item = { - "type": "album", - "hash": entry.type_src, - "timestamp": entry.timestamp, - } - - items.append(item) - continue - - if entry.type == "artist": - artist = ArtistStore.artistmap.get(entry.type_src) - - if artist is None: - continue - - items.append( - { - "type": "artist", - "hash": entry.type_src, - "timestamp": entry.timestamp, - } - ) - - continue - - if entry.type == "folder": - folder = entry.type_src - - if not folder: - continue - - if not folder.endswith("/"): - folder += "/" - - is_home_dir = entry.type_src == "$home" - - if is_home_dir: - folder = os.path.expanduser("~") - - item = { - "type": "folder", - "hash": entry.type_src, - "timestamp": entry.timestamp, - } - - items.append(item) - continue - - if entry.type == "playlist": - is_custom = entry.type_src in [i["name"] for i in custom_playlists] - - if is_custom: - items.append( - { - "type": "playlist", - "hash": entry.type_src, - "timestamp": entry.timestamp, - "is_custom": True, - } - ) - continue - - playlist = PlaylistTable.get_by_id(entry.type_src) - if playlist is None: - continue - - item = { - "type": "playlist", - "hash": entry.type_src, - "timestamp": entry.timestamp, - } - - items.append(item) - continue - - if entry.type == "favorite": - items.append( - { - "type": "favorite", - "timestamp": entry.timestamp, - } - ) - continue - - t = TrackStore.trackhashmap.get(entry.trackhash) - - if t is None: - continue - - item = { - "type": "track", - "hash": entry.trackhash, - "timestamp": entry.timestamp, - } - items.append(item) - - return items - - -def recover_items(items: list[dict]): - custom_playlists = [ - {"name": "recentlyadded", "handler": get_recently_added_playlist}, - {"name": "recentlyplayed", "handler": get_recently_played_playlist}, - ] - recovered = [] - - for item in items: - recovered_item = None - - if item["type"] == "album": - album = AlbumStore.get_album_by_hash(item["hash"]) - if album is None: - continue - - album = album_serializer( - album, - to_remove={ - "genres", - "date", - "count", - "duration", - "albumartists_hashes", - "og_title", - }, - ) - - recovered_item = { - "type": "album", - "item": album, - } - elif item["type"] == "artist": - artist = ArtistStore.get_artist_by_hash(item["hash"]) - if artist is None: - continue - - recovered_item = { - "type": "artist", - "item": serialize_for_card(artist), - } - elif item["type"] == "folder": - count = FolderStore.count_tracks_containing_paths([item["hash"]]) - - recovered_item = { - "type": "folder", - "item": { - "path": item["hash"], - "count": count[0]["trackcount"], - }, - } - elif item["type"] == "playlist": - if item.get("is_custom"): - playlist, _ = next( - i["handler"]() - for i in custom_playlists - if i["name"] == item["hash"] - ) - playlist.images = [i["image"] for i in playlist.images] - - playlist = serialize_playlist( - playlist, to_remove={"settings", "duration"} - ) - recovered_item = { - "type": "playlist", - "item": playlist, - } - else: - playlist = PlaylistTable.get_by_id(item["hash"]) - if playlist is None: - continue - - tracks = TrackStore.get_tracks_by_trackhashes(playlist.trackhashes) - playlist.clear_lists() - - if not playlist.has_image: - images = get_first_4_images(tracks) - images = [i["image"] for i in images] - playlist.images = images - - recovered_item = { - "type": "playlist", - "item": serialize_playlist(playlist), - } - elif item["type"] == "favorite": - recovered_item = { - "type": "favorite", - "item": { - "count": FavoritesTable.count(), - }, - } - elif item["type"] == "track": - track = TrackStore.trackhashmap.get(item["hash"]) - if track is None: - continue - - recovered_item = { - "type": "track", - "item": serialize_track(track.get_best()), - } - - elif item["type"] == "mix": - from app.store.homepage import HomepageStore - mix = HomepageStore.find_mix(item["hash"]) - if mix is None: - mix = MixTable.get_by_mixid(item["hash"]) - - if mix is None: - continue - - mix = mix.to_dict() - - recovered_item = { - "type": "mix", - "item": mix, - } - - if recovered_item is not None: - helptext = item.get("help_text") or item.get("type") - secondary_text = item.get("secondary_text") - - if "secondary_text" in item: - secondary_text = item["secondary_text"] - elif "timestamp" in item: - secondary_text = timestamp_to_time_passed(item["timestamp"]) - - if helptext: - recovered_item["item"]["help_text"] = helptext - - if secondary_text: - recovered_item["item"]["time"] = secondary_text - - recovered.append(recovered_item) - - return recovered + return mix.to_dict() diff --git a/app/lib/home/create_items.py b/app/lib/home/create_items.py index d10d134d..a6492898 100644 --- a/app/lib/home/create_items.py +++ b/app/lib/home/create_items.py @@ -1,10 +1,11 @@ -from app.db.userdata import MixTable, PlaylistTable +import os +from app.db.userdata import PlaylistTable +from app.lib.home import find_mix from app.lib.home.recentlyadded import get_recently_added_playlist from app.lib.home.recentlyplayed import get_recently_played_playlist from app.models.logger import TrackLog from app.store.albums import AlbumStore from app.store.artists import ArtistStore -from app.store.homepage import HomepageStore from app.store.tracks import TrackStore @@ -30,17 +31,16 @@ def create_items(entries: list[TrackLog], limit: int): if not entry.type_src: continue - # INFO: Get mix from homepage store - mix = HomepageStore.find_mix(entry.type_src) + splits = entry.type_src.split(".") - if not mix and entry.type_src.startswith("t"): - # mix is a track mix (not saved in the db) + try: + mixid = splits[0] + sourcehash = splits[1] + except IndexError: continue - if not mix: - # INFO: Get mix from db - mix = MixTable.get_by_mixid(entry.type_src) - + # INFO: Get mix from homepage store + mix = find_mix(mixid, sourcehash) if not mix: continue @@ -155,4 +155,4 @@ def create_items(entries: list[TrackLog], limit: int): } items.append(item) - return items \ No newline at end of file + return items diff --git a/app/lib/home/recover_items.py b/app/lib/home/recover_items.py new file mode 100644 index 00000000..8e381482 --- /dev/null +++ b/app/lib/home/recover_items.py @@ -0,0 +1,151 @@ +from app.db.userdata import FavoritesTable, MixTable, PlaylistTable +from app.lib.home import find_mix +from app.lib.home.recentlyadded import get_recently_added_playlist +from app.lib.home.recentlyplayed import get_recently_played_playlist +from app.lib.playlistlib import get_first_4_images +from app.serializers.album import album_serializer +from app.serializers.artist import serialize_for_card +from app.serializers.playlist import serialize_for_card as serialize_playlist +from app.serializers.track import serialize_track +from app.store.albums import AlbumStore +from app.store.artists import ArtistStore +from app.store.folder import FolderStore +from app.store.tracks import TrackStore +from app.utils.dates import timestamp_to_time_passed + + +def recover_items(items: list[dict]): + custom_playlists = [ + {"name": "recentlyadded", "handler": get_recently_added_playlist}, + {"name": "recentlyplayed", "handler": get_recently_played_playlist}, + ] + recovered = [] + + for item in items: + recovered_item = None + + if item["type"] == "album": + album = AlbumStore.get_album_by_hash(item["hash"]) + if album is None: + continue + + album = album_serializer( + album, + to_remove={ + "genres", + "date", + "count", + "duration", + "albumartists_hashes", + "og_title", + }, + ) + + recovered_item = { + "type": "album", + "item": album, + } + elif item["type"] == "artist": + artist = ArtistStore.get_artist_by_hash(item["hash"]) + if artist is None: + continue + + recovered_item = { + "type": "artist", + "item": serialize_for_card(artist), + } + elif item["type"] == "folder": + count = FolderStore.count_tracks_containing_paths([item["hash"]]) + + recovered_item = { + "type": "folder", + "item": { + "path": item["hash"], + "count": count[0]["trackcount"], + }, + } + elif item["type"] == "playlist": + if item.get("is_custom"): + playlist, _ = next( + i["handler"]() + for i in custom_playlists + if i["name"] == item["hash"] + ) + playlist.images = [i["image"] for i in playlist.images] + + playlist = serialize_playlist( + playlist, to_remove={"settings", "duration"} + ) + recovered_item = { + "type": "playlist", + "item": playlist, + } + else: + playlist = PlaylistTable.get_by_id(item["hash"]) + if playlist is None: + continue + + tracks = TrackStore.get_tracks_by_trackhashes(playlist.trackhashes) + playlist.clear_lists() + + if not playlist.has_image: + images = get_first_4_images(tracks) + images = [i["image"] for i in images] + playlist.images = images + + recovered_item = { + "type": "playlist", + "item": serialize_playlist(playlist), + } + elif item["type"] == "favorite": + recovered_item = { + "type": "favorite", + "item": { + "count": FavoritesTable.count(), + }, + } + elif item["type"] == "track": + track = TrackStore.trackhashmap.get(item["hash"]) + if track is None: + continue + + recovered_item = { + "type": "track", + "item": serialize_track(track.get_best()), + } + + elif item["type"] == "mix": + try: + splits = item["hash"].split(".") + mixid = splits[0] + sourcehash = splits[1] + except IndexError: + continue + + mix = find_mix(mixid, sourcehash) + if mix is None: + continue + + recovered_item = { + "type": "mix", + "item": mix, + } + + if recovered_item is not None: + helptext = item.get("help_text") or item.get("type") + secondary_text = item.get("secondary_text") + + if "secondary_text" in item: + secondary_text = item["secondary_text"] + elif "timestamp" in item: + secondary_text = timestamp_to_time_passed(item["timestamp"]) + + if helptext: + recovered_item["item"]["help_text"] = helptext + + if secondary_text: + recovered_item["item"]["time"] = secondary_text + + recovered.append(recovered_item) + + return recovered diff --git a/app/lib/recipes/artistmixes.py b/app/lib/recipes/artistmixes.py index 3ee9d6a1..3de2bcdc 100644 --- a/app/lib/recipes/artistmixes.py +++ b/app/lib/recipes/artistmixes.py @@ -25,7 +25,7 @@ class ArtistMixes(HomepageRoutine): custom_mixes = [] for _mix in mixes: - custom_mix = mix.get_track_mix(_mix) + custom_mix = MixesPlugin.get_track_mix(_mix) if custom_mix: custom_mixes.append(custom_mix) diff --git a/app/plugins/mixes.py b/app/plugins/mixes.py index 7bf316f3..8691abc6 100644 --- a/app/plugins/mixes.py +++ b/app/plugins/mixes.py @@ -237,7 +237,8 @@ class MixesPlugin(Plugin): print([m.title for m in mixes]) return mixes - def get_mix_description(self, tracks: list[Track], artishash: str): + @classmethod + def get_mix_description(cls, tracks: list[Track], artishash: str): """ Constructs a description for a mix by putting together the first n=4 artists in the mix tracklist. @@ -434,17 +435,18 @@ class MixesPlugin(Plugin): The resulting mix is definitely expected to be of low quality. - TODO: Implement this! + TODO: Maybe implement this! """ pass - def get_track_mix(self, mix: Mix): + @classmethod + def get_track_mix(cls, mix: Mix): """ Given a mix, returns the excess tracks as a custom mix. """ # INFO: If the mix can't have more than 20 tracks, return None - if len(mix.tracks) <= self.MIX_TRACKS_LENGTH + 20: + if len(mix.tracks) <= cls.MIX_TRACKS_LENGTH + 20: return None og_track = TrackStore.trackhashmap.get(mix.tracks[0]) @@ -454,20 +456,20 @@ class MixesPlugin(Plugin): og_track = og_track.get_best() tracks = [og_track] + TrackStore.get_tracks_by_trackhashes( - mix.tracks[self.MIX_TRACKS_LENGTH :] + mix.tracks[cls.MIX_TRACKS_LENGTH :] ) trackmix = Mix( id=f"t{mix.userid}{mix.extra['artisthash']}", title=og_track.title, - description=self.get_mix_description(tracks, mix.extra["artisthash"]), + description=cls.get_mix_description(tracks, mix.extra["artisthash"]), tracks=[t.trackhash for t in tracks], sourcehash=create_hash(*[t.trackhash for t in tracks]), userid=mix.userid, extra={ "type": "track", "og_sourcehash": mix.sourcehash, - "images": self.get_custom_mix_images(tracks), + "images": cls.get_custom_mix_images(tracks), "artists": None, "albums": None, }, @@ -479,8 +481,8 @@ class MixesPlugin(Plugin): return trackmix - def get_custom_mix_images(self, tracks: list[Track]): - + @classmethod + def get_custom_mix_images(cls, tracks: list[Track]): first_album = tracks[0].albumhash first_img = { "image": first_album + ".webp", @@ -517,7 +519,8 @@ class MixesPlugin(Plugin): return images - def get_because_items(self, mixes: list[Mix]): + @staticmethod + def get_because_items(mixes: list[Mix]): """ Given a list of mixes, returns a list of artists that are similar to the artists in the mixes. diff --git a/app/store/homepageentries.py b/app/store/homepageentries.py index 7343a884..276ccd69 100644 --- a/app/store/homepageentries.py +++ b/app/store/homepageentries.py @@ -1,7 +1,7 @@ from abc import ABC from typing import Any -from app.lib.home import recover_items +from app.lib.home.recover_items import recover_items from app.models.mix import Mix class HomepageEntry(ABC):