mirror of https://github.com/nathom/streamrip.git
148 lines
4.4 KiB
Python
148 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
from ..exceptions import NonStreamable
|
|
from .album_metadata import AlbumMetadata
|
|
from .util import safe_get, typed
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class TrackInfo:
|
|
id: str
|
|
quality: int
|
|
|
|
bit_depth: Optional[int] = None
|
|
explicit: bool = False
|
|
sampling_rate: Optional[int | float] = None
|
|
work: Optional[str] = None
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class TrackMetadata:
|
|
info: TrackInfo
|
|
|
|
title: str
|
|
album: AlbumMetadata
|
|
artist: str
|
|
tracknumber: int
|
|
discnumber: int
|
|
composer: str | None
|
|
|
|
@classmethod
|
|
def from_qobuz(cls, album: AlbumMetadata, resp: dict) -> TrackMetadata | None:
|
|
title = typed(resp["title"].strip(), str)
|
|
streamable = typed(resp.get("streamable", False), bool)
|
|
|
|
if not streamable:
|
|
return None
|
|
|
|
version = typed(resp.get("version"), str | None)
|
|
work = typed(resp.get("work"), str | None)
|
|
if version is not None and version not in title:
|
|
title = f"{title} ({version})"
|
|
if work is not None and work not in title:
|
|
title = f"{work}: {title}"
|
|
|
|
composer = typed(resp.get("composer", {}).get("name"), str | None)
|
|
tracknumber = typed(resp.get("track_number", 1), int)
|
|
discnumber = typed(resp.get("media_number", 1), int)
|
|
artist = typed(
|
|
safe_get(
|
|
resp,
|
|
"performer",
|
|
"name",
|
|
),
|
|
str,
|
|
)
|
|
track_id = str(resp["id"])
|
|
bit_depth = typed(resp.get("maximum_bit_depth"), int | None)
|
|
sampling_rate = typed(resp.get("maximum_sampling_rate"), int | float | None)
|
|
# Is the info included?
|
|
explicit = False
|
|
|
|
info = TrackInfo(
|
|
id=track_id,
|
|
quality=album.info.quality,
|
|
bit_depth=bit_depth,
|
|
explicit=explicit,
|
|
sampling_rate=sampling_rate,
|
|
work=work,
|
|
)
|
|
return cls(
|
|
info=info,
|
|
title=title,
|
|
album=album,
|
|
artist=artist,
|
|
tracknumber=tracknumber,
|
|
discnumber=discnumber,
|
|
composer=composer,
|
|
)
|
|
|
|
@classmethod
|
|
def from_deezer(cls, album: AlbumMetadata, resp) -> TrackMetadata:
|
|
raise NotImplemented
|
|
|
|
@classmethod
|
|
def from_soundcloud(cls, album: AlbumMetadata, resp: dict) -> TrackMetadata:
|
|
track = resp
|
|
track_id = track["id"]
|
|
bit_depth, sampling_rate = None, None
|
|
explicit = typed(
|
|
safe_get(track, "publisher_metadata", "explicit", default=False), bool
|
|
)
|
|
|
|
title = typed(track["title"].strip(), str)
|
|
artist = typed(track["user"]["username"], str)
|
|
tracknumber = 1
|
|
|
|
info = TrackInfo(
|
|
id=track_id,
|
|
quality=album.info.quality,
|
|
bit_depth=bit_depth,
|
|
explicit=explicit,
|
|
sampling_rate=sampling_rate,
|
|
work=None,
|
|
)
|
|
return cls(
|
|
info=info,
|
|
title=title,
|
|
album=album,
|
|
artist=artist,
|
|
tracknumber=tracknumber,
|
|
discnumber=0,
|
|
composer=None,
|
|
)
|
|
|
|
@classmethod
|
|
def from_tidal(cls, album: AlbumMetadata, resp) -> TrackMetadata:
|
|
raise NotImplemented
|
|
|
|
@classmethod
|
|
def from_resp(cls, album: AlbumMetadata, source, resp) -> TrackMetadata | None:
|
|
if source == "qobuz":
|
|
return cls.from_qobuz(album, resp)
|
|
if source == "tidal":
|
|
return cls.from_tidal(album, resp)
|
|
if source == "soundcloud":
|
|
return cls.from_soundcloud(album, resp)
|
|
if source == "deezer":
|
|
return cls.from_deezer(album, resp)
|
|
raise Exception
|
|
|
|
def format_track_path(self, format_string: str) -> str:
|
|
# Available keys: "tracknumber", "artist", "albumartist", "composer", "title",
|
|
# and "explicit", "albumcomposer"
|
|
none_text = "Unknown"
|
|
info = {
|
|
"title": self.title,
|
|
"tracknumber": self.tracknumber,
|
|
"artist": self.artist,
|
|
"albumartist": self.album.albumartist,
|
|
"albumcomposer": self.album.albumcomposer or none_text,
|
|
"composer": self.composer or none_text,
|
|
"explicit": " (Explicit) " if self.info.explicit else "",
|
|
}
|
|
return format_string.format(**info)
|