mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
modularize src
+ merge main.py and manage.py + move start logic to swingmusic/__main__.py + add a run.py on the project root
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
from typing import List, TypeVar
|
||||
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from swingmusic.api.apischemas import GenericLimitSchema
|
||||
from swingmusic.db.userdata import FavoritesTable
|
||||
from swingmusic.lib.extras import get_extra_info
|
||||
from swingmusic.models import FavType
|
||||
from swingmusic.settings import Defaults
|
||||
|
||||
from swingmusic.store.albums import AlbumStore
|
||||
from swingmusic.store.artists import ArtistStore
|
||||
from swingmusic.store.tracks import TrackStore
|
||||
|
||||
from swingmusic.serializers.track import serialize_track, serialize_tracks
|
||||
from swingmusic.serializers.artist import (
|
||||
serialize_for_card as serialize_artist,
|
||||
serialize_for_cards,
|
||||
)
|
||||
from swingmusic.utils.dates import timestamp_to_time_passed
|
||||
from swingmusic.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,
|
||||
)
|
||||
type: str = Field(description="The type of the item")
|
||||
|
||||
|
||||
def toggle_fav(type: str, hash: str):
|
||||
"""
|
||||
Toggles a favorite item.
|
||||
"""
|
||||
if type == FavType.track:
|
||||
entry = TrackStore.trackhashmap.get(hash)
|
||||
if entry is not None:
|
||||
entry.toggle_favorite_user()
|
||||
|
||||
elif type == FavType.album:
|
||||
entry = AlbumStore.albummap.get(hash)
|
||||
|
||||
if entry is not None:
|
||||
entry.toggle_favorite_user()
|
||||
elif type == FavType.artist:
|
||||
entry = ArtistStore.artistmap.get(hash)
|
||||
|
||||
if entry is not None:
|
||||
entry.toggle_favorite_user()
|
||||
|
||||
return {"msg": "Added to favorites"}
|
||||
|
||||
|
||||
@api.post("/add")
|
||||
def toggle_favorite(body: FavoritesAddBody):
|
||||
"""
|
||||
Adds a favorite to the database.
|
||||
"""
|
||||
extra = get_extra_info(body.hash, body.type)
|
||||
|
||||
try:
|
||||
FavoritesTable.insert_item(
|
||||
{"hash": body.hash, "type": body.type, "extra": extra}
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"msg": "Failed! An error occured"}, 500
|
||||
|
||||
toggle_fav(body.type, body.hash)
|
||||
|
||||
return {"msg": "Added to favorites"}
|
||||
|
||||
|
||||
@api.post("/remove")
|
||||
def remove_favorite(body: FavoritesAddBody):
|
||||
"""
|
||||
Removes a favorite from the database.
|
||||
"""
|
||||
try:
|
||||
FavoritesTable.remove_item({"hash": body.hash, "type": body.type})
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"msg": "Failed! An error occured"}, 500
|
||||
|
||||
toggle_fav(body.type, body.hash)
|
||||
|
||||
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",
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
|
||||
@api.get("/albums")
|
||||
def get_favorite_albums(query: GetAllOfTypeQuery):
|
||||
"""
|
||||
Get favorite albums
|
||||
|
||||
Note: Only the first request will return the total number of favorites.
|
||||
Others will return -1
|
||||
"""
|
||||
fav_albums, total = FavoritesTable.get_fav_albums(query.start, query.limit)
|
||||
albums = AlbumStore.get_albums_by_hashes(a.hash for a in fav_albums)
|
||||
|
||||
return {"albums": serialize_for_card_many(albums), "total": total}
|
||||
|
||||
|
||||
@api.get("/tracks")
|
||||
def get_favorite_tracks(query: GetAllOfTypeQuery):
|
||||
"""
|
||||
Get favorite tracks
|
||||
|
||||
Note: Only the first request will return the total number of favorites.
|
||||
Others will return -1
|
||||
"""
|
||||
tracks, total = FavoritesTable.get_fav_tracks(query.start, query.limit)
|
||||
tracks = TrackStore.get_tracks_by_trackhashes([t.hash for t in tracks])
|
||||
|
||||
return {"tracks": serialize_tracks(tracks), "total": total}
|
||||
|
||||
|
||||
@api.get("/artists")
|
||||
def get_favorite_artists(query: GetAllOfTypeQuery):
|
||||
"""
|
||||
Get favorite artists
|
||||
|
||||
Note: Only the first request will return the total number of favorites.
|
||||
Others will return -1
|
||||
"""
|
||||
artists, total = FavoritesTable.get_fav_artists(
|
||||
start=query.start,
|
||||
limit=query.limit,
|
||||
)
|
||||
|
||||
artists = ArtistStore.get_artists_by_hashes(a.hash for a in artists)
|
||||
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",
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
album_limit: int = Field(
|
||||
description="The number of albums to return",
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
artist_limit: int = Field(
|
||||
description="The number of artists to return",
|
||||
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(with_user=True)
|
||||
favs = sorted(favs, key=lambda x: x.timestamp, reverse=True)
|
||||
|
||||
tracks = []
|
||||
albums = []
|
||||
artists = []
|
||||
|
||||
track_master_hash = TrackStore.trackhashmap.keys()
|
||||
album_master_hash = AlbumStore.albummap.keys()
|
||||
artist_master_hash = ArtistStore.artistmap.keys()
|
||||
|
||||
# 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 = TrackStore.get_tracks_by_trackhashes(tracks[:track_limit])
|
||||
albums = AlbumStore.get_albums_by_hashes(albums[:album_limit])
|
||||
artists = ArtistStore.get_artists_by_hashes(artists[:artist_limit])
|
||||
|
||||
recents = []
|
||||
|
||||
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)}
|
||||
Reference in New Issue
Block a user