🏷️ remove models.track, add models.constants

This commit is contained in:
oskvr37
2025-01-27 17:42:39 +01:00
parent d7592afd16
commit 0eb68e2d03
13 changed files with 134 additions and 118 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import unittest
from tiddl.models import Track
from tiddl.models.resource import Track
from tiddl.utils import TidalResource, formatTrack
+23 -13
View File
@@ -4,8 +4,18 @@ import json
from pathlib import Path
from requests import Session
from tiddl import models
from .exceptions import AuthError, ApiError
from tiddl.exceptions import AuthError, ApiError
from tiddl.models.api import (
AlbumItems,
ArtistAlbumsItems,
Favorites,
PlaylistItems,
SessionResponse,
TrackStream,
)
from tiddl.models.constants import TrackQuality
from tiddl.models.resource import Track, Album, Playlist
from tiddl.models.search import Search
DEBUG = False
API_URL = "https://api.tidal.com/v1"
@@ -52,14 +62,14 @@ class TidalApi:
return data
def getSession(self):
return models.SessionResponse(
return SessionResponse(
**self._request(
"sessions",
)
)
def getTrackStream(self, id: str | int, quality: models.TrackQuality):
return models.TrackStream(
def getTrackStream(self, id: str | int, quality: TrackQuality):
return TrackStream(
**self._request(
f"tracks/{id}/playbackinfo",
{
@@ -71,7 +81,7 @@ class TidalApi:
)
def getTrack(self, id: str | int):
return models.Track(
return Track(
**self._request(f"tracks/{id}", {"countryCode": self.country_code})
)
@@ -83,7 +93,7 @@ class TidalApi:
if onlyNonAlbum:
params.update({"filter": "EPSANDSINGLES"})
return models.AristAlbumsItems(
return ArtistAlbumsItems(
**self._request(
f"artists/{id}/albums",
params,
@@ -91,7 +101,7 @@ class TidalApi:
)
def getAlbum(self, id: str | int):
return models.Album(
return Album(
**self._request(f"albums/{id}", {"countryCode": self.country_code})
)
@@ -102,7 +112,7 @@ class TidalApi:
logging.warning(f"Too big page, max page size is {MAX_LIMIT}")
limit = MAX_LIMIT
return models.AlbumItems(
return AlbumItems(
**self._request(
f"albums/{id}/items",
{"countryCode": self.country_code, "limit": limit, "offset": offset},
@@ -110,7 +120,7 @@ class TidalApi:
)
def getPlaylist(self, uuid: str):
return models.Playlist(
return Playlist(
**self._request(
f"playlists/{uuid}",
{"countryCode": self.country_code},
@@ -118,7 +128,7 @@ class TidalApi:
)
def getPlaylistItems(self, uuid: str, limit=PLAYLIST_LIMIT, offset=0):
return models.PlaylistItems(
return PlaylistItems(
**self._request(
f"playlists/{uuid}/items",
{"countryCode": self.country_code, "limit": limit, "offset": offset},
@@ -126,7 +136,7 @@ class TidalApi:
)
def getFavorites(self):
return models.Favorites(
return Favorites(
**self._request(
f"users/{self.user_id}/favorites/ids",
{"countryCode": self.country_code},
@@ -134,7 +144,7 @@ class TidalApi:
)
def search(self, query: str):
return models.Search(
return Search(
**self._request(
"search", {"countryCode": self.country_code, "query": query}
)
+3 -1
View File
@@ -8,10 +8,12 @@ from .url import UrlGroup
from ..ctx import Context, passContext
from tiddl.download import downloadTrackStream
from tiddl.models import TrackArg, ARG_TO_QUALITY, Track, PlaylistItems, Album
from tiddl.utils import formatTrack, trackExists, TidalResource
from tiddl.metadata import addMetadata, Cover
from tiddl.exceptions import ApiError, AuthError
from tiddl.models.constants import TrackArg, ARG_TO_QUALITY
from tiddl.models.resource import Track, Album
from tiddl.models.api import PlaylistItems
@click.command("download")
+1 -1
View File
@@ -1,7 +1,7 @@
from pydantic import BaseModel
from pathlib import Path
from tiddl.models import TrackArg
from tiddl.models.constants import TrackArg
CONFIG_PATH = Path.home() / "tiddl.json"
+1 -1
View File
@@ -5,7 +5,7 @@ from pydantic import BaseModel
from base64 import b64decode
from xml.etree.ElementTree import fromstring
from tiddl.models import TrackStream
from tiddl.models.api import TrackStream
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -7,7 +7,7 @@ from mutagen.flac import FLAC as MutagenFLAC, Picture
from mutagen.easymp4 import EasyMP4 as MutagenEasyMP4
from mutagen.mp4 import MP4Cover, MP4 as MutagenMP4
from tiddl.models import Track
from tiddl.models.resource import Track
logger = logging.getLogger(__name__)
-16
View File
@@ -1,16 +0,0 @@
from typing import TypedDict, Literal
from .api import *
from .track import *
from .search import *
TrackArg = Literal["low", "normal", "high", "master"]
ARG_TO_QUALITY: dict[TrackArg, TrackQuality] = {
"low": "LOW",
"normal": "HIGH",
"high": "LOSSLESS",
"master": "HI_RES_LOSSLESS",
}
QUALITY_TO_ARG = {v: k for k, v in ARG_TO_QUALITY.items()}
+28 -3
View File
@@ -1,8 +1,17 @@
from pydantic import BaseModel
from typing import Optional, List, Literal, Union
from .track import Track
from .resource import Video, Album
from .resource import Video, Album, Track, TrackQuality
__all__ = [
"Client",
"SessionResponse",
"ArtistAlbumsItems",
"AlbumItems",
"PlaylistItems",
"Favorites",
"TrackStream",
]
class Client(BaseModel):
@@ -27,7 +36,7 @@ class Items(BaseModel):
totalNumberOfItems: int
class AristAlbumsItems(Items):
class ArtistAlbumsItems(Items):
items: List[Album]
@@ -80,3 +89,19 @@ class Favorites(BaseModel):
VIDEO: List[str]
TRACK: List[str]
ARTIST: List[str]
class TrackStream(BaseModel):
trackId: int
assetPresentation: Literal["FULL"]
audioMode: Literal["STEREO"]
audioQuality: TrackQuality
manifestMimeType: Literal["application/dash+xml", "application/vnd.tidal.bts"]
manifestHash: str
manifest: str
albumReplayGain: float
albumPeakAmplitude: float
trackReplayGain: float
trackPeakAmplitude: float
bitDepth: Optional[int] = None
sampleRate: Optional[int] = None
+13
View File
@@ -0,0 +1,13 @@
from typing import Literal
TrackQuality = Literal["LOW", "HIGH", "LOSSLESS", "HI_RES_LOSSLESS"]
TrackArg = Literal["low", "normal", "high", "master"]
ARG_TO_QUALITY: dict[TrackArg, TrackQuality] = {
"low": "LOW",
"normal": "HIGH",
"high": "LOSSLESS",
"master": "HI_RES_LOSSLESS",
}
QUALITY_TO_ARG = {v: k for k, v in ARG_TO_QUALITY.items()}
+52 -1
View File
@@ -1,6 +1,57 @@
from pydantic import BaseModel
from datetime import datetime
from typing import Optional, List, Literal, Dict
from .constants import TrackQuality
__all__ = ["Track", "Video", "Album", "Playlist"]
class Track(BaseModel):
class Artist(BaseModel):
id: int
name: str
type: str
picture: Optional[str] = None
class Album(BaseModel):
id: int
title: str
cover: Optional[str] = None
vibrantColor: Optional[str] = None
videoCover: Optional[str] = None
id: int
title: str
duration: int
replayGain: float
peak: float
allowStreaming: bool
streamReady: bool
adSupportedStreamReady: bool
djReady: bool
stemReady: bool
streamStartDate: Optional[datetime] = None
premiumStreamingOnly: bool
trackNumber: int
volumeNumber: int
version: Optional[str] = None
popularity: int
copyright: str
bpm: Optional[int] = None
url: str
isrc: str
editable: bool
explicit: bool
audioQuality: TrackQuality
audioModes: List[str]
mediaMetadata: Dict[str, List[str]]
# for real, artist can be None?
artist: Optional[Artist] = None
artists: List[Artist]
album: Album
mixes: Dict[str, str]
class Video(BaseModel):
@@ -53,7 +104,7 @@ class Album(BaseModel):
picture: Optional[str] = None
class MediaMetadata(BaseModel):
tags: List[Literal['LOSSLESS', 'HIRES_LOSSLESS']]
tags: List[Literal["LOSSLESS", "HIRES_LOSSLESS"]]
id: int
title: str
+6 -7
View File
@@ -1,16 +1,10 @@
from pydantic import BaseModel
from typing import Optional, List, Literal, Dict, Union
from .track import Track
from .resource import Playlist, Album, Video
from .resource import Track, Playlist, Album, Video
from .api import Items
class SearchAlbum(Album):
# TODO: remove the artist field instead of making it None
artist: None = None
class Artist(BaseModel):
class Role(BaseModel):
@@ -40,10 +34,15 @@ class Artist(BaseModel):
class Search(BaseModel):
class Artists(Items):
items: List[Artist]
class Albums(Items):
class SearchAlbum(Album):
# TODO: remove the artist field instead of making it None
artist: None = None
items: List[SearchAlbum]
class Playlists(Items):
-71
View File
@@ -1,71 +0,0 @@
from pydantic import BaseModel
from datetime import datetime
from typing import Optional, List, Dict, Literal
TrackQuality = Literal["LOW", "HIGH", "LOSSLESS", "HI_RES_LOSSLESS"]
ManifestMimeType = Literal["application/dash+xml", "application/vnd.tidal.bts"]
class TrackStream(BaseModel):
trackId: int
assetPresentation: Literal["FULL"]
audioMode: Literal["STEREO"]
audioQuality: TrackQuality
manifestMimeType: ManifestMimeType
manifestHash: str
manifest: str
albumReplayGain: float
albumPeakAmplitude: float
trackReplayGain: float
trackPeakAmplitude: float
bitDepth: Optional[int] = None
sampleRate: Optional[int] = None
class TrackArtist(BaseModel):
id: int
name: str
type: str
picture: Optional[str] = None
class TrackAlbum(BaseModel):
id: int
title: str
cover: Optional[str] = None
vibrantColor: Optional[str] = None
videoCover: Optional[str] = None
class Track(BaseModel):
id: int
title: str
duration: int
replayGain: float
peak: float
allowStreaming: bool
streamReady: bool
adSupportedStreamReady: bool
djReady: bool
stemReady: bool
streamStartDate: Optional[datetime] = None
premiumStreamingOnly: bool
trackNumber: int
volumeNumber: int
version: Optional[str] = None
popularity: int
copyright: str
bpm: Optional[int] = None
url: str
isrc: str
editable: bool
explicit: bool
audioQuality: TrackQuality
audioModes: List[str]
mediaMetadata: Dict[str, List[str]]
# for real, artist can be None?
artist: Optional[TrackArtist] = None
artists: List[TrackArtist]
album: TrackAlbum
mixes: Dict[str, str]
+5 -2
View File
@@ -6,7 +6,8 @@ from pathlib import Path
from typing import Literal, get_args
from tiddl.models import Track, TrackQuality, QUALITY_TO_ARG
from tiddl.models.constants import TrackQuality, QUALITY_TO_ARG
from tiddl.models.resource import Track
ResourceTypeLiteral = Literal["track", "album", "playlist", "artist"]
@@ -49,7 +50,9 @@ def sanitizeString(string: str) -> str:
return re.sub(pattern, "", string)
def formatTrack(template: str, track: Track, album_artist="", playlist_title="", playlist_index=0) -> str:
def formatTrack(
template: str, track: Track, album_artist="", playlist_title="", playlist_index=0
) -> str:
artist = sanitizeString(track.artist.name) if track.artist else ""
features = [
sanitizeString(track_artist.name)