mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
save extra tags
+ port: streaming
This commit is contained in:
@@ -16,6 +16,10 @@
|
|||||||
- Recreate album hash if featured artists are discover
|
- Recreate album hash if featured artists are discover
|
||||||
- Implement checking if is clean install and skip migrations!
|
- Implement checking if is clean install and skip migrations!
|
||||||
|
|
||||||
|
|
||||||
|
<!-- CHECKPOINT -->
|
||||||
|
<!-- ALBUM PAGE! -->
|
||||||
|
|
||||||
# DONE
|
# DONE
|
||||||
- Support auth headers
|
- Support auth headers
|
||||||
- Add recently played playlist
|
- Add recently played playlist
|
||||||
|
|||||||
+12
-54
@@ -10,7 +10,8 @@ from pydantic import BaseModel, Field
|
|||||||
from app.api.apischemas import TrackHashSchema
|
from app.api.apischemas import TrackHashSchema
|
||||||
from app.lib.trackslib import get_silence_paddings
|
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
|
from app.utils.files import guess_mime_type
|
||||||
|
|
||||||
bp_tag = Tag(name="File", description="Audio files")
|
bp_tag = Tag(name="File", description="Audio files")
|
||||||
@@ -34,36 +35,12 @@ def send_track_file_legacy(path: TrackHashSchema, query: SendTrackFileQuery):
|
|||||||
filepath = query.filepath
|
filepath = query.filepath
|
||||||
msg = {"msg": "File Not Found"}
|
msg = {"msg": "File Not Found"}
|
||||||
|
|
||||||
def get_mime(filename: str) -> str:
|
track = TrackTable.get_track_by_trackhash(trackhash, filepath)
|
||||||
ext = filename.rsplit(".", maxsplit=1)[-1]
|
track_exists = track is not None and os.path.exists(track.filepath)
|
||||||
return f"audio/{ext}"
|
|
||||||
|
|
||||||
# If filepath is provide, try to send that
|
if track_exists:
|
||||||
if filepath is not None:
|
audio_type = guess_mime_type(filepath)
|
||||||
try:
|
return send_file(filepath, mimetype=audio_type, conditional=True)
|
||||||
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
|
|
||||||
|
|
||||||
return msg, 404
|
return msg, 404
|
||||||
|
|
||||||
@@ -80,31 +57,12 @@ def send_track_file(path: TrackHashSchema, query: SendTrackFileQuery):
|
|||||||
msg = {"msg": "File Not Found"}
|
msg = {"msg": "File Not Found"}
|
||||||
|
|
||||||
# If filepath is provided, try to send that
|
# If filepath is provided, try to send that
|
||||||
if filepath is not None:
|
track = TrackTable.get_track_by_trackhash(trackhash, filepath)
|
||||||
try:
|
track_exists = track is not None and os.path.exists(track.filepath)
|
||||||
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 = guess_mime_type(filepath)
|
||||||
if track_exists:
|
return send_file_as_chunks(track.filepath, audio_type)
|
||||||
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
|
|
||||||
|
|
||||||
return msg, 404
|
return msg, 404
|
||||||
|
|
||||||
|
|||||||
+35
-7
@@ -9,6 +9,7 @@ from sqlalchemy import (
|
|||||||
Row,
|
Row,
|
||||||
String,
|
String,
|
||||||
Tuple,
|
Tuple,
|
||||||
|
and_,
|
||||||
create_engine,
|
create_engine,
|
||||||
insert,
|
insert,
|
||||||
select,
|
select,
|
||||||
@@ -39,7 +40,8 @@ def todicts(tracks: list[Any]):
|
|||||||
|
|
||||||
|
|
||||||
class DbManager:
|
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.engine = create_engine(f"sqlite+pysqlite:///{fullpath}", echo=True)
|
||||||
self.conn = self.engine.connect()
|
self.conn = self.engine.connect()
|
||||||
|
|
||||||
@@ -47,7 +49,8 @@ class DbManager:
|
|||||||
return self.conn.execution_options(preserve_rowcount=True)
|
return self.conn.execution_options(preserve_rowcount=True)
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
self.conn.commit()
|
if self.commit:
|
||||||
|
self.conn.commit()
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ class Base(MappedAsDataclass, DeclarativeBase):
|
|||||||
"""
|
"""
|
||||||
Inserts multiple items into the database.
|
Inserts multiple items into the database.
|
||||||
"""
|
"""
|
||||||
with DbManager() as conn:
|
with DbManager(commit=True) as conn:
|
||||||
conn.execute(insert(cls).values(items))
|
conn.execute(insert(cls).values(items))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -177,6 +180,7 @@ class TrackTable(Base):
|
|||||||
title: Mapped[str] = mapped_column(String())
|
title: Mapped[str] = mapped_column(String())
|
||||||
track: Mapped[int] = mapped_column(Integer())
|
track: Mapped[int] = mapped_column(Integer())
|
||||||
trackhash: Mapped[str] = mapped_column(String(), index=True)
|
trackhash: Mapped[str] = mapped_column(String(), index=True)
|
||||||
|
extra: Mapped[Optional[dict[str, Any]]] = mapped_column(JSON())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_tracks_by_filepaths(cls, filepaths: list[str]):
|
def get_tracks_by_filepaths(cls, filepaths: list[str]):
|
||||||
@@ -209,23 +213,47 @@ class TrackTable(Base):
|
|||||||
tracks = tracks_to_dataclasses(result.fetchall())
|
tracks = tracks_to_dataclasses(result.fetchall())
|
||||||
return remove_duplicates(tracks, is_album_tracks=True)
|
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
|
# SECTION: HELPER FUNCTIONS
|
||||||
|
|
||||||
|
|
||||||
def album_to_dataclass(album: Row[AlbumTable]):
|
def album_to_dataclass(album: Any):
|
||||||
return AlbumModel(**album._asdict())
|
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]
|
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())
|
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]
|
return [track_to_dataclass(track) for track in tracks]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ class IndexAlbums:
|
|||||||
album["created_dates"].append(track.last_mod)
|
album["created_dates"].append(track.last_mod)
|
||||||
|
|
||||||
if track.genre:
|
if track.genre:
|
||||||
album["genres"].append(track.genre)
|
album["genres"].extend(track.genre)
|
||||||
|
|
||||||
for album in albums.values():
|
for album in albums.values():
|
||||||
album["date"] = min(album["dates"])
|
album["date"] = min(album["dates"])
|
||||||
|
|||||||
+16
-1
@@ -2,7 +2,9 @@ from dataclasses import dataclass
|
|||||||
import os
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from pprint import pprint
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from PIL import Image, UnidentifiedImageError
|
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
|
*[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__
|
tags = tags.__dict__
|
||||||
|
|
||||||
# delete all tag properties that start with _ (tinytag internals)
|
# delete all tag properties that start with _ (tinytag internals)
|
||||||
@@ -332,7 +348,6 @@ def get_tags(filepath: str):
|
|||||||
"comment",
|
"comment",
|
||||||
"composer",
|
"composer",
|
||||||
"disc_total",
|
"disc_total",
|
||||||
"extra",
|
|
||||||
"samplerate",
|
"samplerate",
|
||||||
"track_total",
|
"track_total",
|
||||||
"year",
|
"year",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class Track:
|
|||||||
title: str
|
title: str
|
||||||
track: int
|
track: int
|
||||||
trackhash: str
|
trackhash: str
|
||||||
|
extra: dict
|
||||||
|
|
||||||
_pos: int = 0
|
_pos: int = 0
|
||||||
_ati: str = ""
|
_ati: str = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user