document + rename stuff

This commit is contained in:
cwilvx
2024-07-07 16:07:27 +03:00
parent 32a2684ea2
commit 2ba5d6c1d7
11 changed files with 72 additions and 72 deletions
+1 -1
View File
@@ -56,8 +56,8 @@
then artist B is similar to A with the same weight, unless overwritten. then artist B is similar to A with the same weight, unless overwritten.
- Figure out how to update album/artist tables instead of deleting all rows when the app starts - Figure out how to update album/artist tables instead of deleting all rows when the app starts
- Move get all filtering and sorting operations to the database since all sort keys are table columns - Move get all filtering and sorting operations to the database since all sort keys are table columns
- Replace the DbManager class with cls.execute()
- Paginate the following endpoints: - Paginate the following endpoints:
1. Folder tracks 1. Folder tracks
2. Playlist tracks 2. Playlist tracks
+1 -4
View File
@@ -76,6 +76,7 @@ def create_api():
CORS(app, origins="*", supports_credentials=True) CORS(app, origins="*", supports_credentials=True)
# RESPONSE COMPRESSION # RESPONSE COMPRESSION
# Only compress JSON responses
Compress(app) Compress(app)
app.config["COMPRESS_MIMETYPES"] = [ app.config["COMPRESS_MIMETYPES"] = [
"application/json", "application/json",
@@ -84,10 +85,6 @@ def create_api():
# JWT # JWT
jwt = JWTManager(app) jwt = JWTManager(app)
# @jwt.user_identity_loader
# def user_identity_lookup(user):
# return user
@jwt.user_lookup_loader @jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data): def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"] identity = jwt_data["sub"]
+20 -12
View File
@@ -9,14 +9,12 @@ from sqlalchemy import (
from sqlalchemy.engine import Engine from sqlalchemy.engine import Engine
from sqlalchemy import event from sqlalchemy import event
from sqlalchemy.orm import ( from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass, Session
DeclarativeBase,
MappedAsDataclass,
Session
)
from app.db.engine import DbEngine from app.db.engine import DbEngine
# Enable foreign key constraints for SQLite
@event.listens_for(Engine, "connect") @event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record): def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor() cursor = dbapi_connection.cursor()
@@ -25,9 +23,12 @@ def set_sqlite_pragma(dbapi_connection, connection_record):
class DbManager: class DbManager:
""" """
def __init__(self, commit: bool = False): def __init__(self, commit: bool = False):
self.commit = commit self.commit = commit
self.conn = DbEngine.engine.connect() self.conn = DbEngine.engine.connect()
with Session(DbEngine.engine) as session: with Session(DbEngine.engine) as session:
session.connection session.connection
@@ -42,9 +43,15 @@ class DbManager:
class Base(MappedAsDataclass, DeclarativeBase): class Base(MappedAsDataclass, DeclarativeBase):
"""
Base class for all database models.
It has methods common to all tables. eg. `insert_one`, `insert_many`, `remove_all`, `remove_one`, `all`, `count`.
"""
@classmethod @classmethod
def execute(cls, stmt: Any, commit: bool = False): def execute(cls, stmt: Any, commit: bool = False):
with DbManager(commit=commit) as conn: with DbEngine.manager(commit=commit) as conn:
return conn.execute(stmt) return conn.execute(stmt)
@classmethod @classmethod
@@ -52,8 +59,7 @@ class Base(MappedAsDataclass, DeclarativeBase):
""" """
Inserts multiple items into the database. Inserts multiple items into the database.
""" """
with DbManager(commit=True) as conn: return cls.execute(insert(cls).values(items), commit=True)
return conn.execute(insert(cls).values(items))
@classmethod @classmethod
def insert_one(cls, item: dict[str, Any]): def insert_one(cls, item: dict[str, Any]):
@@ -64,12 +70,11 @@ class Base(MappedAsDataclass, DeclarativeBase):
@classmethod @classmethod
def remove_all(cls): def remove_all(cls):
with DbManager(commit=True) as conn: return cls.execute(delete(cls), commit=True)
conn.execute(delete(cls))
@classmethod @classmethod
def remove_one(cls, id: int): def remove_one(cls, id: int):
cls.execute(delete(cls).where(cls.id == id), commit=True) return cls.execute(delete(cls).where(cls.id == id), commit=True)
@classmethod @classmethod
def all(cls): def all(cls):
@@ -80,5 +85,8 @@ class Base(MappedAsDataclass, DeclarativeBase):
return cls.execute(select(func.count()).select_from(cls)).scalar() return cls.execute(select(func.count()).select_from(cls)).scalar()
def create_all(): def create_all_tables():
"""
Creates all the tables that build on the Base class.
"""
Base().metadata.create_all(DbEngine.engine) Base().metadata.create_all(DbEngine.engine)
+26 -1
View File
@@ -1,5 +1,30 @@
from contextlib import contextmanager
from sqlalchemy import Engine from sqlalchemy import Engine
class DbEngine: class DbEngine:
engine: Engine = None """
The database engine instance.
"""
engine: Engine
@classmethod
@contextmanager
def manager(cls, commit: bool):
"""
This context manager manages access to the database.
When the context manager is entered, it returns a connection object that can be used to execute SQL statements.
If the `commit` parameter is set to `True`, the context manager will commit the transaction when it exits.
"""
try:
conn = cls.engine.connect()
yield conn.execution_options(preserve_rowcount=True)
if commit:
conn.commit()
finally:
conn.close()
+4 -1
View File
@@ -23,6 +23,9 @@ from typing import Any, Iterable, Optional
def create_all(): def create_all():
""" """
Create all the tables defined in this file. Create all the tables defined in this file.
NOTE: We need this function because the MasterBase does not collect
the tables defined here (as they are grand-children of the MasterBase)
""" """
Base.metadata.create_all(DbEngine.engine) Base.metadata.create_all(DbEngine.engine)
@@ -317,7 +320,7 @@ class AlbumTable(Base):
# NOTE: The artist dict keys need to in the same order they appear in the db for this to work! # NOTE: The artist dict keys need to in the same order they appear in the db for this to work!
select(AlbumTable).where(AlbumTable.artisthashes.contains(artist)) select(AlbumTable).where(AlbumTable.artisthashes.contains(artist))
) )
albums[artist] = (albums_to_dataclasses(result.fetchall())) albums[artist] = albums_to_dataclasses(result.fetchall())
return albums return albums
+2 -2
View File
@@ -90,10 +90,10 @@ class SQLiteManager:
if self.test_db_path: if self.test_db_path:
db_path = self.test_db_path db_path = self.test_db_path
else: else:
db_path = settings.Db.get_app_db_path() db_path = settings.DbPaths.get_app_db_path()
if self.userdata_db: if self.userdata_db:
db_path = settings.Db.get_userdata_db_path() db_path = settings.DbPaths.get_userdata_db_path()
self.conn = sqlite3.connect( self.conn = sqlite3.connect(
db_path, db_path,
+1 -9
View File
@@ -2,17 +2,11 @@
Contains methods relating to albums. Contains methods relating to albums.
""" """
from dataclasses import asdict
from typing import Any
from itertools import groupby
from app.models.track import Track from app.models.track import Track
from app.store.albums import AlbumStore
from app.store.tracks import TrackStore
def remove_duplicate_on_merge_versions(tracks: list[Track]) -> list[Track]: def remove_duplicate_on_merge_versions(tracks: list[Track]):
""" """
Removes duplicate tracks when merging versions of the same album. Removes duplicate tracks when merging versions of the same album.
""" """
@@ -21,8 +15,6 @@ def remove_duplicate_on_merge_versions(tracks: list[Track]) -> list[Track]:
def sort_by_track_no(tracks: list[Track]): def sort_by_track_no(tracks: list[Track]):
# tracks = [asdict(t) for t in tracks]
for t in tracks: for t in tracks:
track = str(t.track).zfill(3) track = str(t.track).zfill(3)
t._pos = int(f"{t.disc}{track}") t._pos = int(f"{t.disc}{track}")
-31
View File
@@ -145,34 +145,3 @@ class CheckArtistImages:
if url is not None: if url is not None:
return DownloadImage(url, name=f"{artist['artisthash']}.webp") return DownloadImage(url, name=f"{artist['artisthash']}.webp")
# def fetch_album_bio(title: str, albumartist: str) -> str | None: """ Returns the album bio for a given album. """
# last_fm_url = "http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={}&artist={}&album={
# }&format=json".format( settings.Paths.LAST_FM_API_KEY, albumartist, title )
# try:
# response = requests.get(last_fm_url)
# data = response.json()
# except:
# return None
# try:
# bio = data["album"]["wiki"]["summary"].split('<a href="https://www.last.fm/')[0]
# except KeyError:
# bio = None
# return bio
# class FetchAlbumBio:
# """
# Returns the album bio for a given album.
# """
# def __init__(self, title: str, albumartist: str):
# self.title = title
# self.albumartist = albumartist
# def __call__(self):
# return fetch_album_bio(self.title, self.albumartist)
+1 -1
View File
@@ -141,7 +141,7 @@ SUPPORTED_FILES = tuple(f".{file}" for file in FILES)
# ===== SQLite ===== # ===== SQLite =====
class Db: class DbPaths:
APP_DB_NAME = "swing.db" APP_DB_NAME = "swing.db"
USER_DATA_DB_NAME = "userdata.db" USER_DATA_DB_NAME = "userdata.db"
+6 -8
View File
@@ -5,14 +5,12 @@ Applies migrations.
from sqlalchemy import create_engine from sqlalchemy import create_engine
from app.db.userdata import UserTable from app.db.userdata import UserTable
from app.db.sqlite.auth import SQLiteAuthMethods as authdb
from app.migrations import apply_migrations from app.migrations import apply_migrations
from app.settings import Db from app.settings import DbPaths
from app.db import create_all
from app.db.libdata import create_all as create_all_libdata
from app.db.engine import DbEngine from app.db.engine import DbEngine
from app.db import create_all_tables
from app.db.libdata import create_all as create_user_tables
def run_migrations(): def run_migrations():
@@ -27,14 +25,14 @@ def setup_sqlite():
Create Sqlite databases and tables. Create Sqlite databases and tables.
""" """
DbEngine.engine = create_engine( DbEngine.engine = create_engine(
f"sqlite+pysqlite:///{Db.get_app_db_path()}", f"sqlite+pysqlite:///{DbPaths.get_app_db_path()}",
echo=False, echo=False,
max_overflow=20, max_overflow=20,
pool_size=10, pool_size=10,
) )
create_all() create_all_tables()
create_all_libdata() create_user_tables()
if not UserTable.get_all(): if not UserTable.get_all():
UserTable.insert_default_user() UserTable.insert_default_user()
+10 -2
View File
@@ -23,7 +23,6 @@ from app.api import create_api
from app.arg_handler import ProcessArgs from app.arg_handler import ProcessArgs
from app.lib.tagger import IndexEverything from app.lib.tagger import IndexEverything
from app.lib.watchdogg import Watcher as WatchDog from app.lib.watchdogg import Watcher as WatchDog
from app.periodic_scan import run_periodic_scans
from app.plugins.register import register_plugins from app.plugins.register import register_plugins
from app.settings import FLASKVARS, TCOLOR, Info from app.settings import FLASKVARS, TCOLOR, Info
from app.setup import load_into_mem, run_setup from app.setup import load_into_mem, run_setup
@@ -32,8 +31,15 @@ from app.utils.filesystem import get_home_res_path
from app.utils.paths import getClientFilesExtensions from app.utils.paths import getClientFilesExtensions
from app.utils.threading import background from app.utils.threading import background
mimetypes.add_type("text/css", ".css") # Load mimetypes for the web client's static files
# Loading mimetypes should happen automatically but
# sometimes the mimetypes are not loaded correctly
# eg. when the Registry is messed up on Windows.
# See the following issues:
# https://github.com/swingmx/swingmusic/issues/137
mimetypes.add_type("text/css", ".css")
mimetypes.add_type("text/javascript", ".js") mimetypes.add_type("text/javascript", ".js")
mimetypes.add_type("text/plain", ".txt") mimetypes.add_type("text/plain", ".txt")
mimetypes.add_type("text/html", ".html") mimetypes.add_type("text/html", ".html")
@@ -166,6 +172,8 @@ def serve_client_files(path: str):
gzipped_path = path + ".gz" gzipped_path = path + ".gz"
user_agent = request.headers.get("User-Agent") user_agent = request.headers.get("User-Agent")
# INFO: Safari doesn't support gzip encoding
# See issue: https://github.com/swingmx/swingmusic/issues/155
is_safari = user_agent.find("Safari") >= 0 and user_agent.find("Chrome") < 0 is_safari = user_agent.find("Safari") >= 0 and user_agent.find("Chrome") < 0
if is_safari: if is_safari: