""" Contains all the album routes. """ from itertools import groupby import random from flask_jwt_extended import current_user from pydantic import Field from flask_openapi3 import Tag from flask_openapi3 import APIBlueprint from app.api.apischemas import AlbumHashSchema, AlbumLimitSchema, ArtistHashSchema from app.config import UserConfig from app.db.libdata import ArtistTable from app.db.libdata import AlbumTable as AlbumDb, TrackTable as TrackDb from app.db.userdata import SimilarArtistTable from app.settings import Defaults from app.utils.hashing import create_hash from app.lib.albumslib import sort_by_track_no from app.serializers.album import serialize_for_card, serialize_for_card_many from app.serializers.track import serialize_track, serialize_tracks from app.db.sqlite.albumcolors import SQLiteAlbumMethods as adb from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as lastfmdb get_albums_by_albumartist = adb.get_albums_by_albumartist check_is_fav = favdb.check_is_favorite bp_tag = Tag(name="Album", description="Single album") api = APIBlueprint("album", __name__, url_prefix="/album", abp_tags=[bp_tag]) # NOTE: Don't use "/" as it will cause redirects (failure) @api.post("") def get_album_tracks_and_info(body: AlbumHashSchema): """ Get album and tracks Returns album info and tracks for the given albumhash. """ albumhash = body.albumhash album = AlbumDb.get_album_by_albumhash(albumhash) if album is None: return {"error": "Album not found"}, 404 tracks = TrackDb.get_tracks_by_albumhash(albumhash) album.trackcount = len(tracks) album.duration = sum(t.duration for t in tracks) album.type = album.check_type( tracks=tracks, singleTrackAsSingle=UserConfig().showAlbumsAsSingles ) track_total = sum({int(t.extra.get("track_total", 1) or 1) for t in tracks}) return { "info": album, "extra": { # INFO: track_total is the sum of a set of track_total values from each track # ASSUMPTIONS # 1. All the tracks have the correct track totals # 2. Tracks with the same track total are from the same disc "track_total": track_total, "avg_bitrate": sum(t.bitrate for t in tracks) // len(tracks), }, "copyright": tracks[0].copyright, "tracks": serialize_tracks(tracks, remove_disc=False), } @api.get("//tracks") def get_album_tracks(path: AlbumHashSchema): """ Get album tracks Returns all the tracks in the given album, sorted by disc and track number. NOTE: No album info is returned. """ tracks = TrackDb.get_tracks_by_albumhash(path.albumhash) tracks = sort_by_track_no(tracks) return serialize_tracks(tracks) class GetMoreFromArtistsBody(AlbumLimitSchema): albumartists: list = Field( description="The artist hashes to get more albums from", example='[{"name": "Khalid", "artisthash": "94ca2dba1c"}]', ) base_title: str = Field( description="The base title of the album to exclude from the results.", example=Defaults.API_ALBUMNAME, default=None, ) @api.post("/from-artist") def get_more_from_artist(body: GetMoreFromArtistsBody): """ Get more from artist Returns more albums from the given artist hashes. """ albumartists = body.albumartists limit = body.limit base_title = body.base_title all_albums = AlbumDb.get_albums_by_artisthashes(albumartists) # filter out albums with the same base title all_albums = filter( lambda a: create_hash(a.base_title) != create_hash(base_title), all_albums ) all_albums = list(all_albums) if not len(all_albums): return [] # group by first albumartist's artisthash groups = groupby(all_albums, lambda a: a.albumartists[0]["artisthash"]) return [ {"artisthash": g[0], "albums": serialize_for_card_many(list(g[1])[:limit])} for g in groups ] class GetAlbumVersionsBody(ArtistHashSchema): og_album_title: str = Field( description="The original album title (album.og_title)", example=Defaults.API_ALBUMNAME, ) base_title: str = Field( description="The base title of the album to exclude from the results.", example=Defaults.API_ALBUMNAME, ) @api.post("/other-versions") def get_album_versions(body: GetAlbumVersionsBody): """ Get other versions Returns other versions of the given album. """ og_album_title = body.og_album_title base_title = body.base_title artisthash = body.artisthash albums = AlbumDb.get_albums_by_base_title(base_title) albums = [ a for a in albums if a.og_title != og_album_title and artisthash in {a["artisthash"] for a in a.albumartists} ] print(albums) return serialize_for_card_many(albums) class GetSimilarAlbumsQuery(ArtistHashSchema, AlbumLimitSchema): pass @api.get("/similar") def get_similar_albums(query: GetSimilarAlbumsQuery): """ Get similar albums Returns similar albums to the given album. """ artisthash = query.artisthash limit = query.limit similar_artists = SimilarArtistTable.get_by_hash(artisthash) if similar_artists is None: return [] artisthashes = similar_artists.get_artist_hash_set() artists = ArtistTable.get_artists_by_artisthashes(artisthashes) albums = AlbumDb.get_albums_by_artisthashes([a.artisthash for a in artists]) sample = random.sample(albums, min(len(albums), limit)) return serialize_for_card_many(sample[:limit])