Files
swingmusic-extended/app/api/favorites.py
T
cwilvx 4a9f804e70 combine userdata and swing db into one
+ port populate to new db interface
+ add genrehashes and hash info to tracks
+ properly structure new db table files
+ move helpers to dedicated utils file
+ move settings from db to config file
+ move artists, albums, auth and favorites endpoint to new db interface
+ use folder store to index filepaths
+ paginate favorite pages
+ 56 moretiny changes 😅
2024-06-30 15:06:33 +03:00

266 lines
7.5 KiB
Python

from typing import List, TypeVar
from flask_jwt_extended import current_user
from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint
from pydantic import BaseModel, Field
from app.api.apischemas import GenericLimitSchema
from app.db.libdata import ArtistTable
from app.db.libdata import AlbumTable, TrackTable
from app.db.userdata import FavoritesTable
from app.models import FavType
from app.settings import Defaults
from app.utils.bisection import use_bisection
from app.serializers.track import serialize_track, serialize_tracks
from app.serializers.artist import (
serialize_for_card as serialize_artist,
serialize_for_cards,
)
from app.utils.dates import timestamp_to_time_passed
from app.serializers.album import serialize_for_card, serialize_for_card_many
bp_tag = Tag(name="Favorites", description="Your favorite items")
api = APIBlueprint("favorites", __name__, url_prefix="/favorites", abp_tags=[bp_tag])
T = TypeVar("T")
def remove_none(items: List[T]) -> List[T]:
return [i for i in items if i is not None]
class FavoritesAddBody(BaseModel):
hash: str = Field(
description="The hash of the item",
min_length=Defaults.HASH_LENGTH,
max_length=Defaults.HASH_LENGTH,
example=Defaults.API_ALBUMHASH,
)
type: str = Field(description="The type of the item", example=FavType.album)
@api.post("/add")
def toggle_favorite(body: FavoritesAddBody):
"""
Adds a favorite to the database.
"""
FavoritesTable.insert_item({"hash": body.hash, "type": body.type})
if body.type == FavType.track:
TrackTable.set_is_favorite(body.hash, True)
elif body.type == FavType.album:
AlbumTable.set_is_favorite(body.hash, True)
elif body.type == FavType.artist:
ArtistTable.set_is_favorite(body.hash, True)
return {"msg": "Added to favorites"}
@api.post("/remove")
def remove_favorite(body: FavoritesAddBody):
"""
Removes a favorite from the database.
"""
FavoritesTable.remove_item({"hash": body.hash, "type": body.type})
if body.type == FavType.track:
TrackTable.set_is_favorite(body.hash, False)
elif body.type == FavType.album:
AlbumTable.set_is_favorite(body.hash, False)
elif body.type == FavType.artist:
ArtistTable.set_is_favorite(body.hash, False)
return {"msg": "Removed from favorites"}
class GetAllOfTypeQuery(GenericLimitSchema):
"""
Extending this class will give you a model with the `limit` field
"""
start: int = Field(
description="Where to start from",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT,
)
@api.get("/albums")
def get_favorite_albums(query: GetAllOfTypeQuery):
"""
Get favorite albums
"""
fav_albums, total = FavoritesTable.get_fav_albums(query.start, query.limit)
fav_albums.reverse()
return {"albums": serialize_for_card_many(fav_albums), "total": total}
@api.get("/tracks")
def get_favorite_tracks(query: GetAllOfTypeQuery):
"""
Get favorite tracks
"""
tracks, total = FavoritesTable.get_fav_tracks(query.start, query.limit)
return {"tracks": serialize_tracks(tracks), "total": total}
@api.get("/artists")
def get_favorite_artists(query: GetAllOfTypeQuery):
"""
Get favorite artists
"""
artists, total = FavoritesTable.get_fav_artists(
start=query.start,
limit=query.limit,
)
artists.reverse()
return {"artists": [serialize_artist(a) for a in artists], "total": total}
class GetAllFavoritesQuery(BaseModel):
"""
Extending this class will give you a model with the `limit` field
"""
track_limit: int = Field(
description="The number of tracks to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT,
)
album_limit: int = Field(
description="The number of albums to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT,
)
artist_limit: int = Field(
description="The number of artists to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT,
)
@api.get("")
def get_all_favorites(query: GetAllFavoritesQuery):
"""
Returns all the favorites in the database.
"""
track_limit = query.track_limit
album_limit = query.album_limit
artist_limit = query.artist_limit
# largest is x2 to accound for broken hashes if any
largest = max(track_limit, album_limit, artist_limit)
favs = FavoritesTable.get_all()
favs.reverse()
tracks = []
albums = []
artists = []
track_master_hash = TrackTable.get_all_hashes()
album_master_hash = AlbumTable.get_all_hashes()
artist_master_hash = ArtistTable.get_all_hashes()
# INFO: Filter out invalid hashes (file not found or tags edited)
for fav in favs:
hash = fav.hash
type = fav.type
if type == FavType.track:
tracks.append(hash) if hash in track_master_hash else None
if type == FavType.artist:
artists.append(hash) if hash in artist_master_hash else None
if type == FavType.album:
albums.append(hash) if hash in album_master_hash else None
count = {
"tracks": len(tracks),
"albums": len(albums),
"artists": len(artists),
}
tracks = TrackTable.get_tracks_by_trackhashes(tracks, limit=track_limit)
albums = AlbumTable.get_albums_by_albumhashes(albums, limit=album_limit)
artists = ArtistTable.get_artists_by_artisthashes(artists, limit=artist_limit)
recents = []
# first_n = favs
for fav in favs:
if len(recents) >= largest:
break
if fav.type == FavType.album:
album = next((a for a in albums if a.albumhash == fav.hash), None)
if album is None:
continue
album = serialize_for_card(album)
album["help_text"] = "album"
album["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append(
{
"type": "album",
"item": album,
}
)
if fav.type == FavType.artist:
artist = next((a for a in artists if a.artisthash == fav.hash), None)
if artist is None:
continue
artist = serialize_artist(artist)
artist["help_text"] = "artist"
artist["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append(
{
"type": "artist",
"item": artist,
}
)
if fav.type == FavType.track:
track = next((t for t in tracks if t.trackhash == fav.hash), None)
if track is None:
continue
track = serialize_track(track)
track["help_text"] = "track"
track["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append({"type": "track", "item": track})
return {
"recents": recents[:album_limit],
"tracks": serialize_tracks(tracks[:track_limit]),
"albums": serialize_for_card_many(albums[:album_limit]),
"artists": serialize_for_cards(artists[:artist_limit]),
"count": count,
}
@api.get("/check")
def check_favorite(query: FavoritesAddBody):
"""
Checks if a favorite exists in the database.
"""
itemhash = query.hash
itemtype = query.type
return {"is_favorite": FavoritesTable.check_exists(itemhash, itemtype)}