From 3593b205ebc9620b858c80c59d1a764c501cb7c4 Mon Sep 17 00:00:00 2001 From: cwilvx Date: Mon, 24 Jun 2024 20:48:13 +0300 Subject: [PATCH] save extra tags + port: streaming --- TODO.md | 4 +++ app/api/stream.py | 66 +++++++++------------------------------------ app/db/__init__.py | 42 ++++++++++++++++++++++++----- app/lib/tagger.py | 2 +- app/lib/taglib.py | 17 +++++++++++- app/models/track.py | 1 + 6 files changed, 69 insertions(+), 63 deletions(-) diff --git a/TODO.md b/TODO.md index 7f5cc591..76547381 100644 --- a/TODO.md +++ b/TODO.md @@ -16,6 +16,10 @@ - Recreate album hash if featured artists are discover - Implement checking if is clean install and skip migrations! + + + + # DONE - Support auth headers - Add recently played playlist diff --git a/app/api/stream.py b/app/api/stream.py index d5a79535..3eaee2a6 100644 --- a/app/api/stream.py +++ b/app/api/stream.py @@ -10,7 +10,8 @@ from pydantic import BaseModel, Field from app.api.apischemas import TrackHashSchema from app.lib.trackslib import get_silence_paddings -from app.store.tracks import TrackStore +# from app.store.tracks import TrackStore +from app.db import TrackTable from app.utils.files import guess_mime_type bp_tag = Tag(name="File", description="Audio files") @@ -34,36 +35,12 @@ def send_track_file_legacy(path: TrackHashSchema, query: SendTrackFileQuery): filepath = query.filepath msg = {"msg": "File Not Found"} - def get_mime(filename: str) -> str: - ext = filename.rsplit(".", maxsplit=1)[-1] - return f"audio/{ext}" + track = TrackTable.get_track_by_trackhash(trackhash, filepath) + track_exists = track is not None and os.path.exists(track.filepath) - # If filepath is provide, try to send that - if filepath is not None: - try: - track = TrackStore.get_tracks_by_filepaths([filepath])[0] - except IndexError: - track = None - - track_exists = track is not None and os.path.exists(track.filepath) - - if track_exists: - audio_type = get_mime(filepath) - return send_file(filepath, mimetype=audio_type, conditional=True) - - # Else, find file by trackhash - tracks = TrackStore.get_tracks_by_trackhashes([trackhash]) - - for track in tracks: - if track is None: - return msg, 404 - - audio_type = get_mime(track.filepath) - - try: - return send_file(track.filepath, mimetype=audio_type, conditional=True) - except (FileNotFoundError, OSError) as e: - return msg, 404 + if track_exists: + audio_type = guess_mime_type(filepath) + return send_file(filepath, mimetype=audio_type, conditional=True) return msg, 404 @@ -80,31 +57,12 @@ def send_track_file(path: TrackHashSchema, query: SendTrackFileQuery): msg = {"msg": "File Not Found"} # If filepath is provided, try to send that - if filepath is not None: - try: - track = TrackStore.get_tracks_by_filepaths([filepath])[0] - except IndexError: - track = None + track = TrackTable.get_track_by_trackhash(trackhash, filepath) + track_exists = track is not None and os.path.exists(track.filepath) - track_exists = track is not None and os.path.exists(track.filepath) - - if track_exists: - audio_type = guess_mime_type(filepath) - return send_file_as_chunks(track.filepath, audio_type) - - # Else, find file by trackhash - tracks = TrackStore.get_tracks_by_trackhashes([trackhash]) - - for track in tracks: - if track is None: - return msg, 404 - - audio_type = guess_mime_type(track.filepath) - - try: - return send_file_as_chunks(track.filepath, audio_type) - except (FileNotFoundError, OSError) as e: - return msg, 404 + if track_exists: + audio_type = guess_mime_type(filepath) + return send_file_as_chunks(track.filepath, audio_type) return msg, 404 diff --git a/app/db/__init__.py b/app/db/__init__.py index dbea0ad6..24adab64 100644 --- a/app/db/__init__.py +++ b/app/db/__init__.py @@ -9,6 +9,7 @@ from sqlalchemy import ( Row, String, Tuple, + and_, create_engine, insert, select, @@ -39,7 +40,8 @@ def todicts(tracks: list[Any]): class DbManager: - def __init__(self): + def __init__(self, commit: bool = False): + self.commit = commit self.engine = create_engine(f"sqlite+pysqlite:///{fullpath}", echo=True) self.conn = self.engine.connect() @@ -47,7 +49,8 @@ class DbManager: return self.conn.execution_options(preserve_rowcount=True) def __exit__(self, exc_type, exc_val, exc_tb): - self.conn.commit() + if self.commit: + self.conn.commit() self.conn.close() @@ -57,7 +60,7 @@ class Base(MappedAsDataclass, DeclarativeBase): """ Inserts multiple items into the database. """ - with DbManager() as conn: + with DbManager(commit=True) as conn: conn.execute(insert(cls).values(items)) @classmethod @@ -177,6 +180,7 @@ class TrackTable(Base): title: Mapped[str] = mapped_column(String()) track: Mapped[int] = mapped_column(Integer()) trackhash: Mapped[str] = mapped_column(String(), index=True) + extra: Mapped[Optional[dict[str, Any]]] = mapped_column(JSON()) @classmethod def get_tracks_by_filepaths(cls, filepaths: list[str]): @@ -209,23 +213,47 @@ class TrackTable(Base): tracks = tracks_to_dataclasses(result.fetchall()) return remove_duplicates(tracks, is_album_tracks=True) + @classmethod + def get_track_by_trackhash(cls, hash: str, filepath: str = ""): + with DbManager() as conn: + if filepath: + result = conn.execute( + select(TrackTable) + .where( + and_( + TrackTable.trackhash == hash, + TrackTable.filepath == filepath, + ) + ) + .order_by(TrackTable.bitrate.desc()) + ) + else: + result = conn.execute( + select(TrackTable).where(TrackTable.trackhash == hash) + ) + + track = result.fetchone() + + if track: + return track_to_dataclass(track) + # SECTION: HELPER FUNCTIONS -def album_to_dataclass(album: Row[AlbumTable]): +def album_to_dataclass(album: Any): return AlbumModel(**album._asdict()) -def albums_to_dataclasses(albums: list[Row[AlbumTable]]): +def albums_to_dataclasses(albums: Any): return [album_to_dataclass(album) for album in albums] -def track_to_dataclass(track: Row[TrackTable]): +def track_to_dataclass(track: Any): return TrackModel(**track._asdict()) -def tracks_to_dataclasses(tracks: list[Row[TrackTable]]): +def tracks_to_dataclasses(tracks: Any): return [track_to_dataclass(track) for track in tracks] diff --git a/app/lib/tagger.py b/app/lib/tagger.py index 38101f80..95e1a510 100644 --- a/app/lib/tagger.py +++ b/app/lib/tagger.py @@ -62,7 +62,7 @@ class IndexAlbums: album["created_dates"].append(track.last_mod) if track.genre: - album["genres"].append(track.genre) + album["genres"].extend(track.genre) for album in albums.values(): album["date"] = min(album["dates"]) diff --git a/app/lib/taglib.py b/app/lib/taglib.py index 68cfd519..6c1bc9c5 100644 --- a/app/lib/taglib.py +++ b/app/lib/taglib.py @@ -2,7 +2,9 @@ from dataclasses import dataclass import os from io import BytesIO from pathlib import Path +from pprint import pprint import re +import sys import pendulum from PIL import Image, UnidentifiedImageError @@ -318,6 +320,20 @@ def get_tags(filepath: str): *[a["name"] for a in tags.artists], tags.album, tags.title ) + more_extra = { + "audio_offset": tags.audio_offset, + "bitdepth": tags.bitdepth, + "composer": tags.composer, + "channels": tags.channels, + "comment": tags.comment, + "disc_total": tags.disc_total, + "filesize": tags.filesize, + "samplerate": tags.samplerate, + "track_total": tags.track_total, + } + + tags.extra = {**tags.extra, **more_extra} + tags = tags.__dict__ # delete all tag properties that start with _ (tinytag internals) @@ -332,7 +348,6 @@ def get_tags(filepath: str): "comment", "composer", "disc_total", - "extra", "samplerate", "track_total", "year", diff --git a/app/models/track.py b/app/models/track.py index d4c638bc..a2261190 100644 --- a/app/models/track.py +++ b/app/models/track.py @@ -42,6 +42,7 @@ class Track: title: str track: int trackhash: str + extra: dict _pos: int = 0 _ati: str = ""