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 😅
This commit is contained in:
cwilvx
2024-06-30 15:06:33 +03:00
parent 1a66194c6c
commit 4a9f804e70
53 changed files with 1719 additions and 1353 deletions
+5 -3
View File
@@ -11,9 +11,9 @@ from flask_openapi3 import OpenAPI
from flask_jwt_extended import JWTManager
from app.config import UserConfig
from app.db.userdata import UserTable
from app.settings import Info as AppInfo
from .plugins import lyrics as lyrics_plugin
from app.db.sqlite.auth import SQLiteAuthMethods as authdb
from app.api import (
album,
artist,
@@ -92,8 +92,10 @@ def create_api():
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
userid = identity["id"]
user = authdb.get_user_by_id(userid)
return user.todict()
user = UserTable.get_by_id(userid)
if user:
return user.todict()
# Register all the API blueprints
with app.app_context():
+27 -40
View File
@@ -12,15 +12,14 @@ from flask_openapi3 import APIBlueprint
from app.api.apischemas import AlbumHashSchema, AlbumLimitSchema, ArtistHashSchema
from app.config import UserConfig
from app.db import AlbumTable as AlbumDb, TrackTable as TrackDb
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.models import FavType, Track
from app.store.albums import AlbumStore
from app.store.tracks import TrackStore
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
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
@@ -52,9 +51,22 @@ def get_album_tracks_and_info(body: AlbumHashSchema):
album.type = album.check_type(
tracks=tracks, singleTrackAsSingle=UserConfig().showAlbumsAsSingles
)
album.populate_versions()
return {"info": album, "tracks": tracks}
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("/<albumhash>/tracks")
@@ -68,7 +80,7 @@ def get_album_tracks(path: AlbumHashSchema):
tracks = TrackDb.get_tracks_by_albumhash(path.albumhash)
tracks = sort_by_track_no(tracks)
return tracks
return serialize_tracks(tracks)
class GetMoreFromArtistsBody(AlbumLimitSchema):
@@ -138,30 +150,14 @@ def get_album_versions(body: GetAlbumVersionsBody):
artisthash = body.artisthash
albums = AlbumDb.get_albums_by_base_title(base_title)
print(albums)
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)
# albums = AlbumStore.get_albums_by_artisthash(artisthash)
# albums = [
# a
# for a in albums
# if create_hash(a.base_title) == create_hash(base_title)
# and create_hash(og_album_title) != create_hash(a.og_title)
# ]
# for a in albums:
# tracks = TrackStore.get_tracks_by_albumhash(a.albumhash)
# a.get_date_from_tracks(tracks)
return albums
return serialize_for_card_many(albums)
class GetSimilarAlbumsQuery(ArtistHashSchema, AlbumLimitSchema):
@@ -178,24 +174,15 @@ def get_similar_albums(query: GetSimilarAlbumsQuery):
artisthash = query.artisthash
limit = query.limit
similar_artists = lastfmdb.get_similar_artists_for(artisthash)
similar_artists = SimilarArtistTable.get_by_hash(artisthash)
if similar_artists is None:
return {"albums": []}
return []
artisthashes = similar_artists.get_artist_hash_set()
artists = ArtistTable.get_artists_by_artisthashes(artisthashes)
if len(artisthashes) == 0:
return {"albums": []}
albums = AlbumDb.get_albums_by_artisthashes([a.artisthash for a in artists])
sample = random.sample(albums, min(len(albums), limit))
albums = [AlbumStore.get_albums_by_artisthash(a) for a in artisthashes]
albums = [a for sublist in albums for a in sublist]
albums = list({a.albumhash: a for a in albums}.values())
try:
albums = random.sample(albums, min(len(albums), limit))
except ValueError:
pass
return [serialize_for_card(a) for a in albums[:limit]]
return serialize_for_card_many(sample[:limit])
+12 -91
View File
@@ -2,12 +2,11 @@
Contains all the artist(s) routes.
"""
from itertools import groupby
import math
import random
from datetime import datetime
from itertools import groupby
from flask_jwt_extended import current_user
from flask_openapi3 import APIBlueprint, Tag
from pydantic import Field
from app.api.apischemas import (
@@ -18,18 +17,13 @@ from app.api.apischemas import (
)
from app.config import UserConfig
from app.db import AlbumTable, ArtistTable, TrackTable
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as fmdb
from app.models import Album, FavType
from app.db.libdata import ArtistTable
from app.db.libdata import AlbumTable, TrackTable
from app.db.userdata import SimilarArtistTable
from app.serializers.album import serialize_for_card_many
from app.serializers.track import serialize_tracks
from app.store.albums import AlbumStore
from app.store.artists import ArtistStore
from app.store.tracks import TrackStore
bp_tag = Tag(name="Artist", description="Single artist")
api = APIBlueprint("artist", __name__, url_prefix="/artist", abp_tags=[bp_tag])
@@ -45,8 +39,6 @@ def get_artist(path: ArtistHashSchema, query: TrackLimitSchema):
limit = query.limit
artist = ArtistTable.get_artist_by_hash(artisthash)
print(artist)
if artist is None:
return {"error": "Artist not found"}, 404
@@ -56,8 +48,6 @@ def get_artist(path: ArtistHashSchema, query: TrackLimitSchema):
if artist.albumcount == 0 and tcount < 10:
limit = tcount
# artist.is_favorite = favdb.check_is_favorite(artisthash, FavType.artist)
try:
year = datetime.fromtimestamp(artist.date).year
except ValueError:
@@ -106,7 +96,7 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
t.albumhash for t in tracks if t.albumhash not in {a.albumhash for a in albums}
}
albums.extend(AlbumTable.get_albums_by_hash(missing_albumhashes))
albums.extend(AlbumTable.get_albums_by_albumhashes(missing_albumhashes))
albumdict = {a.albumhash: a for a in albums}
config = UserConfig()
@@ -117,43 +107,6 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
if album:
album.check_type(list(tracks), config.showAlbumsAsSingles)
# all_albums = AlbumStore.get_albums_by_artisthash(artisthash)
# start: check for missing albums. ie. compilations and features
# all_tracks = TrackStore.get_tracks_by_artisthash(artisthash)
# track_albums = set(t.albumhash for t in all_tracks)
# missing_album_hashes = track_albums.difference(set(a.albumhash for a in all_albums))
# if len(missing_album_hashes) > 0:
# missing_albums = AlbumStore.get_albums_by_hashes(list(missing_album_hashes))
# all_albums.extend(missing_albums)
# end check
# def get_album_tracks(albumhash: str):
# tracks = [t for t in all_tracks if t.albumhash == albumhash]
# if len(tracks) > 0:
# return tracks
# return TrackStore.get_tracks_by_albumhash(albumhash)
# for a in all_albums:
# a.check_type()
# album_tracks = get_album_tracks(a.albumhash)
# if len(album_tracks) == 0:
# continue
# a.get_date_from_tracks(album_tracks)
# if a.date == 0:
# AlbumStore.remove_album_by_hash(a.albumhash)
# continue
# a.is_single(album_tracks)
albums = [a for a in albumdict.values()]
all_albums = sorted(albums, key=lambda a: str(a.date), reverse=True)
@@ -174,29 +127,6 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
else:
res["albums"].append(album)
# def remove_EPs_and_singles(albums_: list[Album]):
# albums_ = [a for a in albums_ if not a.type == "single"]
# albums_ = [a for a in albums_ if not a.type == "ep"]
# return albums_
# albums = filter(lambda a: artisthash in missing_albumhashes, all_albums)
# albums = list(albums)
# albums = remove_EPs_and_singles(albums)
# compilations = [a for a in albums if a.is_compilation]
# for c in compilations:
# albums.remove(c)
# appearances = filter(lambda a: artisthash not in a.albumartists_hashes, all_albums)
# appearances = list(appearances)
# appearances = remove_EPs_and_singles(appearances)
# artist = ArtistStore.get_artist_by_hash(artisthash)
# if artist is None:
# return {"error": "Artist not found"}, 404
if return_all:
limit = len(all_albums)
@@ -215,8 +145,8 @@ def get_all_artist_tracks(path: ArtistHashSchema):
Returns all artists by a given artist.
"""
tracks = TrackStore.get_tracks_by_artisthash(path.artisthash)
# tracks = TrackStore.get_tracks_by_artisthash(path.artisthash)
tracks = TrackTable.get_tracks_by_artisthash(path.artisthash)
return serialize_tracks(tracks)
@@ -226,23 +156,14 @@ def get_similar_artists(path: ArtistHashSchema, query: ArtistLimitSchema):
Get similar artists.
"""
limit = query.limit
artist = ArtistStore.get_artist_by_hash(path.artisthash)
if artist is None:
return {"error": "Artist not found"}, 404
result = fmdb.get_similar_artists_for(artist.artisthash)
result = SimilarArtistTable.get_by_hash(path.artisthash)
if result is None:
return {"artists": []}
return []
similar = ArtistStore.get_artists_by_hashes(result.get_artist_hash_set())
similar = ArtistTable.get_artists_by_artisthashes(result.get_artist_hash_set())
if len(similar) > limit:
similar = random.sample(similar, limit)
similar = random.sample(similar, min(limit, len(similar)))
return similar[:limit]
# TODO: Rewrite this file using generators where possible
+53 -43
View File
@@ -14,7 +14,8 @@ from pydantic import BaseModel, Field
from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint
from app.db.sqlite.auth import SQLiteAuthMethods as authdb
# from app.db.sqlite.auth import SQLiteAuthMethods as authdb
from app.db.userdata import UserTable
from app.utils.auth import check_password, hash_password
from app.config import UserConfig
@@ -65,7 +66,7 @@ def login(body: LoginBody):
Authenticate using username and password
"""
user = authdb.get_user_by_username(body.username)
user = UserTable.get_by_username(body.username)
if user is None:
return {"msg": "User not found"}, 404
@@ -87,42 +88,41 @@ def login(body: LoginBody):
pair_token = dict()
@api.get("/getpaircode")
def get_pair():
"""
Get a new pair code to log in to thee Swing Music mobile app
"""
# INFO: if user is already logged in, create a new pair code
token = create_new_token(get_jwt_identity())
key = token["accesstoken"][-6:]
global pair_token
pair_token = {
key: token,
}
return {"code": key}
class PairDeviceQuery(BaseModel):
code: str = Field("", description="The code")
@api.get("/pair")
@api.post("/pair")
@jwt_required(optional=True)
def pair_device(query: PairDeviceQuery):
def pair_with_code(body: PairDeviceQuery):
"""
Pair the Swing Music mobile app with this server
Send a code to get an access token. Send an authenticated request without the code to generate a new token.
Get an access token by sending a pair code. NOTE: A code can only be used once!
"""
# INFO: if user is already logged in, create a new pair code
if current_user:
token = create_new_token(get_jwt_identity())
key = token["accesstoken"][-6:]
global pair_token
token = pair_token.get(body.code, None)
global pair_token
pair_token = {
key: token,
}
if token:
pair_token = {}
return token
return {"code": key}
# INFO: if there's a pair code, return the token
if query.code:
token = pair_token.get(query.code, None)
if token:
# INFO: reset pair_token
pair_token = {}
return token
return {"msg": "Invalid code"}, 400
return {"msg": "No code provided"}, 400
return {"msg": "Invalid code"}, 400
@api.post("/refresh")
@@ -133,6 +133,8 @@ def refresh():
>>> Headers:
>>> Authorization: Bearer <refresh_token>
Won't work with cookies!!!
"""
user = get_jwt_identity()
return create_new_token(user)
@@ -153,7 +155,6 @@ def update_profile(body: UpdateProfileBody):
"""
user = {
"id": body.id,
"email": body.email,
"username": body.username,
"password": body.password,
"roles": body.roles,
@@ -172,7 +173,8 @@ def update_profile(body: UpdateProfileBody):
if "admin" not in current_user["roles"]:
return {"msg": "Only admins can update roles"}, 403
all_users = authdb.get_all_users()
# all_users = authdb.get_all_users()
all_users = UserTable.get_all()
if "admin" not in body.roles:
# check if we're removing the last admin
admins = [user for user in all_users if "admin" in user.roles]
@@ -195,7 +197,9 @@ def update_profile(body: UpdateProfileBody):
clean_user = {k: v for k, v in user.items() if v}
try:
return authdb.update_user(clean_user)
# return authdb.update_user(clean_user)
UserTable.update_one(clean_user)
return UserTable.get_by_id(user["id"]).todict()
except sqlite3.IntegrityError:
return {"msg": "Username already exists"}, 400
@@ -216,11 +220,18 @@ def create_user(body: UpdateProfileBody):
}
# check if user already exists
if authdb.get_user_by_username(user["username"]):
if UserTable.get_by_username(user["username"]):
return {"msg": "Username already exists"}, 400
userid = authdb.insert_user(user)
return authdb.get_user_by_id(userid).todict()
UserTable.insert_one(user)
user = UserTable.get_by_username(user["username"])
if user:
return user.todict()
return {
"msg": "Failed to create user",
}, 500
@api.post("/profile/guest/create")
@@ -230,14 +241,14 @@ def create_guest_user():
Create a guest user
"""
# check if guest user already exists
guest_user = authdb.get_user_by_username("guest")
guest_user = UserTable.get_by_username("guest")
if guest_user:
return {
"msg": "Guest user already exists",
}, 400
userid = authdb.insert_guest_user()
userid = UserTable.insert_guest_user()
if userid:
return {
@@ -264,12 +275,12 @@ def delete_user(body: DeleteUseBody):
return {"msg": "Sorry! you cannot delete yourselfu"}, 400
# prevent deleting the only admin
users = authdb.get_all_users()
users = UserTable.get_all()
admins = [user for user in users if "admin" in user.roles]
if len(admins) == 1 and admins[0].username == body.username:
return {"msg": "Cannot delete the only admin"}, 400
authdb.delete_user_by_username(body.username)
UserTable.remove_by_username(body.username)
return {"msg": f"User {body.username} deleted"}
@@ -308,8 +319,7 @@ def get_all_users(query: GetAllUsersQuery):
"users": [],
}
users = authdb.get_all_users()
users = UserTable.get_all()
is_admin = current_user and "admin" in current_user["roles"]
settings["enableGuest"] = [
user for user in users if user.username == "guest"
@@ -355,8 +365,8 @@ def get_all_users(query: GetAllUsersQuery):
if query.simplified:
res["users"] = [user.todict_simplified() for user in users]
res["users"] = [user.todict() for user in users]
else:
res["users"] = [user.todict() for user in users]
return res
+73 -97
View File
@@ -6,18 +6,19 @@ 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.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
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.serializers.album import serialize_for_card, serialize_for_card_many
from app.store.albums import AlbumStore
from app.store.tracks import TrackStore
from app.store.artists import ArtistStore
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])
@@ -41,17 +42,18 @@ class FavoritesAddBody(BaseModel):
@api.post("/add")
def add_favorite(body: FavoritesAddBody):
def toggle_favorite(body: FavoritesAddBody):
"""
Adds a favorite to the database.
"""
itemhash = body.hash
itemtype = body.type
FavoritesTable.insert_item({"hash": body.hash, "type": body.type})
favdb.insert_one_favorite(itemtype, itemhash)
if itemtype == FavType.track:
TrackStore.make_track_fav(itemhash)
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"}
@@ -61,80 +63,62 @@ def remove_favorite(body: FavoritesAddBody):
"""
Removes a favorite from the database.
"""
itemhash = body.hash
itemtype = body.type
FavoritesTable.remove_item({"hash": body.hash, "type": body.type})
favdb.delete_favorite(itemtype, itemhash)
if itemtype == FavType.track:
TrackStore.remove_track_from_fav(itemhash)
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: GenericLimitSchema):
def get_favorite_albums(query: GetAllOfTypeQuery):
"""
Get favorite albums
"""
limit = query.limit
albums = favdb.get_fav_albums()
albumhashes = [a[1] for a in albums]
albumhashes.reverse()
fav_albums, total = FavoritesTable.get_fav_albums(query.start, query.limit)
fav_albums.reverse()
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
fav_albums = use_bisection(src_albums, "albumhash", albumhashes)
fav_albums = remove_none(fav_albums)
if limit == 0:
limit = len(albums)
return {"albums": serialize_for_card_many(fav_albums[:limit])}
return {"albums": serialize_for_card_many(fav_albums), "total": total}
@api.get("/tracks")
def get_favorite_tracks(query: GenericLimitSchema):
def get_favorite_tracks(query: GetAllOfTypeQuery):
"""
Get favorite tracks
"""
limit = query.limit
userid = current_user['id']
tracks = favdb.get_fav_tracks(userid)
trackhashes = [t[1] for t in tracks]
trackhashes.reverse()
src_tracks = sorted(TrackStore.tracks, key=lambda x: x.trackhash)
tracks = use_bisection(src_tracks, "trackhash", trackhashes)
tracks = remove_none(tracks)
if limit == 0:
limit = len(tracks)
return {"tracks": serialize_tracks(tracks[:limit])}
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: GenericLimitSchema):
def get_favorite_artists(query: GetAllOfTypeQuery):
"""
Get favorite artists
"""
limit = query.limit
artists, total = FavoritesTable.get_fav_artists(
start=query.start,
limit=query.limit,
)
artists.reverse()
artists = favdb.get_fav_artists()
artisthashes = [a[1] for a in artists]
artisthashes.reverse()
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
artists = use_bisection(src_artists, "artisthash", artisthashes)
artists = remove_none(artists)
if limit == 0:
limit = len(artists)
return {"artists": artists[:limit]}
return {"artists": [serialize_artist(a) for a in artists], "total": total}
class GetAllFavoritesQuery(BaseModel):
@@ -173,27 +157,29 @@ def get_all_favorites(query: GetAllFavoritesQuery):
# largest is x2 to accound for broken hashes if any
largest = max(track_limit, album_limit, artist_limit)
favs = favdb.get_all()
favs = FavoritesTable.get_all()
favs.reverse()
tracks = []
albums = []
artists = []
track_master_hash = set(t.trackhash for t in TrackStore.tracks)
album_master_hash = set(a.albumhash for a in AlbumStore.albums)
artist_master_hash = set(a.artisthash for a in ArtistStore.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:
# INFO: hash is [1], type is [2], timestamp is [3]
hash = fav[1]
if fav[2] == FavType.track:
hash = fav.hash
type = fav.type
if type == FavType.track:
tracks.append(hash) if hash in track_master_hash else None
if fav[2] == FavType.artist:
if type == FavType.artist:
artists.append(hash) if hash in artist_master_hash else None
if fav[2] == FavType.album:
if type == FavType.album:
albums.append(hash) if hash in album_master_hash else None
count = {
@@ -202,35 +188,26 @@ def get_all_favorites(query: GetAllFavoritesQuery):
"artists": len(artists),
}
src_tracks = sorted(TrackStore.tracks, key=lambda x: x.trackhash)
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
tracks = use_bisection(src_tracks, "trackhash", tracks, limit=track_limit)
albums = use_bisection(src_albums, "albumhash", albums, limit=album_limit)
artists = use_bisection(src_artists, "artisthash", artists, limit=artist_limit)
tracks = remove_none(tracks)
albums = remove_none(albums)
artists = remove_none(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:
# INFO: hash is [1], type is [2], timestamp is [3]
if len(recents) >= largest:
break
if fav[2] == FavType.album:
album = next((a for a in albums if a.albumhash == fav[1]), None)
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[3])
album["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append(
{
@@ -239,15 +216,15 @@ def get_all_favorites(query: GetAllFavoritesQuery):
}
)
if fav[2] == FavType.artist:
artist = next((a for a in artists if a.artisthash == fav[1]), None)
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[3])
artist["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append(
{
@@ -256,15 +233,15 @@ def get_all_favorites(query: GetAllFavoritesQuery):
}
)
if fav[2] == FavType.track:
track = next((t for t in tracks if t.trackhash == fav[1]), None)
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[3])
track["time"] = timestamp_to_time_passed(fav.timestamp)
recents.append({"type": "track", "item": track})
@@ -284,6 +261,5 @@ def check_favorite(query: FavoritesAddBody):
"""
itemhash = query.hash
itemtype = query.type
exists = favdb.check_is_favorite(itemhash, itemtype)
return {"is_favorite": exists}
return {"is_favorite": FavoritesTable.check_exists(itemhash, itemtype)}
+5 -11
View File
@@ -10,11 +10,10 @@ from pydantic import BaseModel, Field
from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint
from showinfm import show_in_file_manager
from memory_profiler import profile
from app import settings
from app.db import TrackTable
from app.db.sqlite.settings import SettingsSQLMethods as db
from app.config import UserConfig
from app.db.libdata import TrackTable
from app.lib.folderslib import GetFilesAndDirs, get_folders
from app.serializers.track import serialize_track
from app.utils.wintools import is_windows, win_replace_slash
@@ -40,8 +39,9 @@ def get_folder_tree(body: FolderTree):
req_dir = body.folder
tracks_only = body.tracks_only
root_dirs = db.get_root_dirs()
root_dirs.sort()
config = UserConfig()
root_dirs = config.rootDirs
try:
if req_dir == "$home" and root_dirs[0] == "$home":
@@ -72,12 +72,6 @@ def get_folder_tree(body: FolderTree):
return res
# return {
# "path": req_dir,
# "tracks": tracks,
# "folders": sorted(folders, key=lambda i: i.name),
# }
def get_all_drives(is_win: bool = False):
"""
+5 -4
View File
@@ -6,7 +6,8 @@ from pydantic import BaseModel, Field
from datetime import datetime
from app.api.apischemas import GenericLimitSchema
from app.db import AlbumTable, ArtistTable
from app.db.libdata import ArtistTable
from app.db.libdata import AlbumTable
from app.store.albums import AlbumStore
from app.store.artists import ArtistStore
@@ -61,11 +62,11 @@ def get_all_items(path: GetAllItemsPath, query: GetAllItemsQuery):
is_artists = path.itemtype == "artists"
if is_albums:
items, total = AlbumTable.get_all(query.start, query.limit)
items = AlbumTable.get_all()
elif is_artists:
items, total = ArtistTable.get_all(query.start, query.limit)
items = ArtistTable.get_all()
# print(items)
total = len(items)
start = query.start
limit = query.limit
+51 -71
View File
@@ -1,3 +1,4 @@
from dataclasses import asdict
from typing import Any
from flask import request
from flask_openapi3 import Tag
@@ -6,12 +7,13 @@ from pydantic import BaseModel, Field
from app.api.auth import admin_required
from app.db.sqlite.plugins import PluginsMethods as pdb
from app.db.sqlite.settings import SettingsSQLMethods as sdb
from app.db.sqlite.tracks import SQLiteTrackMethods as trackdb
from app.db.userdata import PluginTable
from app.lib import populate
from app.lib.tagger import index_everything
from app.lib.watchdogg import Watcher as WatchDog
from app.logger import log
from app.settings import Info, Paths, SessionVarKeys, set_flag
from app.settings import Info, Paths, SessionVarKeys
from app.store.albums import AlbumStore
from app.store.artists import ArtistStore
from app.store.tracks import TrackStore
@@ -48,42 +50,43 @@ def reload_everything(instance_key: str):
except Exception as e:
log.error(e)
# CHECKPOINT: TEST SETTINGS API ENDPOINTS
@background
def rebuild_store(db_dirs: list[str]):
"""
Restarts watchdog and rebuilds the music library.
"""
instance_key = get_random_str()
# @background
# def rebuild_store(db_dirs: list[str]):
# """
# Restarts watchdog and rebuilds the music library.
# """
# instance_key = get_random_str()
log.info("Rebuilding library...")
trackdb.remove_tracks_not_in_folders(db_dirs)
reload_everything(instance_key)
# log.info("Rebuilding library...")
# trackdb.remove_tracks_not_in_folders(db_dirs)
# reload_everything(instance_key)
try:
populate.Populate(instance_key=instance_key)
except populate.PopulateCancelledError as e:
print(e)
reload_everything(instance_key)
return
# try:
# populate.Populate(instance_key=instance_key)
# except populate.PopulateCancelledError as e:
# print(e)
# reload_everything(instance_key)
# return
WatchDog().restart()
# WatchDog().restart()
log.info("Rebuilding library... ✅")
# log.info("Rebuilding library... ✅")
# I freaking don't know what this function does anymore
def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]):
"""
Params:
new_: will be added to the database
removed_: will be removed from the database
db_dirs_: will be used to remove tracks that
are outside these directories from the database and store.
"""
sdb.remove_root_dirs(removed_)
sdb.add_root_dirs(new_)
rebuild_store(db_dirs_)
# # I freaking don't know what this function does anymore
# def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]):
# """
# Params:
# new_: will be added to the database
# removed_: will be removed from the database
# db_dirs_: will be used to remove tracks that
# are outside these directories from the database and store.
# """
# sdb.remove_root_dirs(removed_)
# sdb.add_root_dirs(new_)
# rebuild_store(db_dirs_)
class AddRootDirsBody(BaseModel):
@@ -106,7 +109,8 @@ def add_root_dirs(body: AddRootDirsBody):
new_dirs = body.new_dirs
removed_dirs = body.removed
db_dirs = sdb.get_root_dirs()
config = UserConfig()
db_dirs = config.rootDirs
home = "$home"
db_home = any([d == home for d in db_dirs]) # if $home is in db
@@ -114,13 +118,16 @@ def add_root_dirs(body: AddRootDirsBody):
# handle $home case
if db_home and incoming_home:
return {"msg": "Not changed!"}
return {"msg": "Not changed!"}, 304
# if $home is the current root dir or the incoming root dir
# is $home, remove all root dirs
if db_home or incoming_home:
sdb.remove_root_dirs(db_dirs)
config.rootDirs = []
if incoming_home:
finalize([home], [], [Paths.USER_HOME_DIR])
config.rootDirs = [home]
index_everything()
return {"root_dirs": [home]}
# ---
@@ -136,11 +143,10 @@ def add_root_dirs(body: AddRootDirsBody):
pass
db_dirs.extend(new_dirs)
db_dirs = [dir_ for dir_ in db_dirs if dir_ != home]
config.rootDirs = [dir_ for dir_ in db_dirs if dir_ != home]
finalize(new_dirs, removed_dirs, db_dirs)
return {"root_dirs": db_dirs}
index_everything()
return {"root_dirs": config.rootDirs}
@api.get("/get-root-dirs")
@@ -148,9 +154,7 @@ def get_root_dirs():
"""
Get root directories
"""
dirs = sdb.get_root_dirs()
return {"dirs": dirs}
return {"dirs": UserConfig().rootDirs}
# maps settings to their parser flags
@@ -170,35 +174,12 @@ def get_all_settings():
"""
Get all settings
"""
config = asdict(UserConfig())
plugins = PluginTable.get_all()
config["plugins"] = plugins
config["version"] = Info.SWINGMUSIC_APP_VERSION
settings = sdb.get_all_settings()
plugins = pdb.get_all_plugins()
key_list = list(mapp.keys())
s = {}
for key in key_list:
val_index = key_list.index(key)
try:
s[key] = settings[val_index]
if type(s[key]) == int:
s[key] = bool(s[key])
if type(s[key]) == str:
s[key] = str(s[key]).split(",")
except IndexError:
s[key] = None
root_dirs = sdb.get_root_dirs()
s["root_dirs"] = root_dirs
s["plugins"] = plugins
s["version"] = Info.SWINGMUSIC_APP_VERSION
return {
"settings": s,
}
return config
@background
@@ -245,7 +226,6 @@ def set_setting(body: SetSettingBody):
value = str(value).split(",")
value = set(value)
set_flag(flag, value)
reload_all_for_set_setting()
# if value is a set, convert it to a string
+1 -1
View File
@@ -11,7 +11,7 @@ from app.api.apischemas import TrackHashSchema
from app.lib.trackslib import get_silence_paddings
# from app.store.tracks import TrackStore
from app.db import TrackTable
from app.db.libdata import TrackTable
from app.utils.files import guess_mime_type
bp_tag = Tag(name="File", description="Audio files")