diff --git a/tiddl/api.py b/tiddl/api.py index d385ca6..4d81c12 100644 --- a/tiddl/api.py +++ b/tiddl/api.py @@ -26,7 +26,7 @@ from tiddl.models.api import ( TrackStream, Video, VideoStream, - Lyrics + Lyrics, ) from tiddl.models.constants import TrackQuality @@ -124,9 +124,7 @@ class TidalApi: Album, f"albums/{album_id}", {"countryCode": self.country_code} ) - def getAlbumItems( - self, album_id: str | int, limit=LIMITS.ALBUM_ITEMS, offset=0 - ): + def getAlbumItems(self, album_id: str | int, limit=LIMITS.ALBUM_ITEMS, offset=0): return self.fetch( AlbumItems, f"albums/{album_id}/items", @@ -192,9 +190,7 @@ class TidalApi: {"countryCode": self.country_code}, ) - def getPlaylistItems( - self, playlist_uuid: str, limit=LIMITS.PLAYLIST, offset=0 - ): + def getPlaylistItems(self, playlist_uuid: str, limit=LIMITS.PLAYLIST, offset=0): return self.fetch( PlaylistItems, f"playlists/{playlist_uuid}/items", @@ -215,9 +211,7 @@ class TidalApi: ) def getSession(self): - return self.fetch( - SessionResponse, "sessions", expire_after=DO_NOT_CACHE - ) + return self.fetch(SessionResponse, "sessions", expire_after=DO_NOT_CACHE) def getLyrics(self, track_id: str | int): return self.fetch( diff --git a/tiddl/cli/__init__.py b/tiddl/cli/__init__.py index a35685f..b9b356e 100644 --- a/tiddl/cli/__init__.py +++ b/tiddl/cli/__init__.py @@ -34,18 +34,14 @@ def cli(ctx: Context, verbose: bool, quiet: bool, no_cache: bool): ) ) - LEVEL = ( - logging.DEBUG if verbose else logging.ERROR if quiet else logging.INFO - ) + LEVEL = logging.DEBUG if verbose else logging.ERROR if quiet else logging.INFO rich_handler = RichHandler(console=ctx.obj.console, rich_tracebacks=True) rich_handler.setLevel(LEVEL) if LEVEL == logging.DEBUG: rich_handler.setFormatter( - logging.Formatter( - "[%(name)s.%(funcName)s] %(message)s", datefmt="[%X]" - ) + logging.Formatter("[%(name)s.%(funcName)s] %(message)s", datefmt="[%X]") ) logging.basicConfig( diff --git a/tiddl/cli/download/__init__.py b/tiddl/cli/download/__init__.py index fa17a8e..4e7a689 100644 --- a/tiddl/cli/download/__init__.py +++ b/tiddl/cli/download/__init__.py @@ -111,7 +111,7 @@ def DownloadCommand( SINGLES_FILTER: SinglesFilter, EMBED_LYRICS: bool, DOWNLOAD_VIDEO: bool, - SCAN_PATH: str | None + SCAN_PATH: str | None, ): """Download resources""" DOWNLOAD_VIDEO = DOWNLOAD_VIDEO or ctx.obj.config.download.download_video @@ -127,7 +127,7 @@ def DownloadCommand( THREADS_COUNT, DO_NOT_SKIP, SINGLES_FILTER, - EMBED_LYRICS + EMBED_LYRICS, ) ) @@ -216,13 +216,15 @@ def DownloadCommand( if isinstance(item, Track): if track_stream.audioQuality == "HI_RES_LOSSLESS": - path = asyncio.run(convertFileExtension( - source_file=path, - extension=".flac", - remove_source=True, - is_video=False, - copy_audio=True, # extract flac from m4a container - )) + path = asyncio.run( + convertFileExtension( + source_file=path, + extension=".flac", + remove_source=True, + is_video=False, + copy_audio=True, # extract flac from m4a container + ) + ) if not cover_data and item.album.cover: cover_data = Cover(item.album.cover).content @@ -233,18 +235,27 @@ def DownloadCommand( lyrics_subtitles = "" try: - addMetadata(path, item, cover_data, credits, album_artist=album_artist, lyrics=lyrics_subtitles) + addMetadata( + path, + item, + cover_data, + credits, + album_artist=album_artist, + lyrics=lyrics_subtitles, + ) except Exception as e: logging.error(f"Can not add metadata to: {path}, {e}") elif isinstance(item, Video): - path = asyncio.run(convertFileExtension( - source_file=path, - extension=".mp4", - remove_source=True, - is_video=True, - copy_audio=True, - )) + path = asyncio.run( + convertFileExtension( + source_file=path, + extension=".mp4", + remove_source=True, + is_video=True, + copy_audio=True, + ) + ) try: addVideoMetadata(path, item) @@ -273,14 +284,20 @@ def DownloadCommand( path = Path(PATH) if PATH else ctx.obj.config.download.path path /= f"{filename}.*" - scan_path = Path(SCAN_PATH or ctx.obj.config.download.scan_path) / f"{filename}.*" if (SCAN_PATH or ctx.obj.config.download.scan_path) else path # Scan scan_path if set, else scans 'path'. + scan_path = ( + Path(SCAN_PATH or ctx.obj.config.download.scan_path) / f"{filename}.*" + if (SCAN_PATH or ctx.obj.config.download.scan_path) + else path + ) # Scan scan_path if set, else scans 'path'. # Respect DOWNLOAD_VIDEO = FALSE over DO_NOT_SKIP (as it's for the file exists check) if isinstance(item, Video) and not DOWNLOAD_VIDEO: logging.warning(f"Video '{item.title}' skipped as DOWNLOAD_VIDEO is false") return - if not DO_NOT_SKIP: # check if item is already downloaded (unless DO_NOT_SKIP is set, then override anything) + if ( + not DO_NOT_SKIP + ): # check if item is already downloaded (unless DO_NOT_SKIP is set, then override anything) if isinstance(item, Track): if trackExists(item.audioQuality, DOWNLOAD_QUALITY, scan_path): logging.warning(f"Track '{item.title}' skipped - exists") diff --git a/tiddl/cli/download/fav.py b/tiddl/cli/download/fav.py index 641c70f..286180b 100644 --- a/tiddl/cli/download/fav.py +++ b/tiddl/cli/download/fav.py @@ -3,7 +3,13 @@ import click from tiddl.utils import TidalResource, ResourceTypeLiteral from tiddl.cli.ctx import Context, passContext -ResourceTypeList: list[ResourceTypeLiteral] = ["track", "video", "album", "artist", "playlist"] +ResourceTypeList: list[ResourceTypeLiteral] = [ + "track", + "video", + "album", + "artist", + "playlist", +] @click.group("fav") diff --git a/tiddl/config.py b/tiddl/config.py index 32d126a..aaf4814 100644 --- a/tiddl/config.py +++ b/tiddl/config.py @@ -15,6 +15,7 @@ makedirs(HOME_PATH, exist_ok=True) CONFIG_PATH = HOME_PATH / "tiddl.json" CONFIG_INDENT = 2 + class TemplateConfig(BaseModel): track: str = "{artist} - {title}" video: str = "{artist} - {title}" diff --git a/tiddl/download.py b/tiddl/download.py index 5909743..ed19ca6 100644 --- a/tiddl/download.py +++ b/tiddl/download.py @@ -80,9 +80,7 @@ def parseTrackStream(track_stream: TrackStream) -> tuple[list[str], str]: elif codecs.startswith("mp4"): file_extension = ".m4a" else: - raise ValueError( - f"Unknown codecs `{codecs}` (trackId {track_stream.trackId}" - ) + raise ValueError(f"Unknown codecs `{codecs}` (trackId {track_stream.trackId}") return urls, file_extension diff --git a/tiddl/metadata.py b/tiddl/metadata.py index fe0d4af..e291f63 100644 --- a/tiddl/metadata.py +++ b/tiddl/metadata.py @@ -40,8 +40,12 @@ def addMetadata( picture.mime = "image/jpeg" metadata.add_picture(picture) - metadata["TITLE"] = track.title + (" ({})".format(track.version) if track.version else "") - metadata["WORK"] = track.title + (" ({})".format(track.version) if track.version else "") + metadata["TITLE"] = track.title + ( + " ({})".format(track.version) if track.version else "" + ) + metadata["WORK"] = track.title + ( + " ({})".format(track.version) if track.version else "" + ) metadata["TRACKNUMBER"] = str(track.trackNumber) metadata["DISCNUMBER"] = str(track.volumeNumber) @@ -58,9 +62,7 @@ def addMetadata( if track.streamStartDate: metadata["DATE"] = track.streamStartDate.strftime("%Y-%m-%d") - metadata["ORIGINALDATE"] = track.streamStartDate.strftime( - "%Y-%m-%d" - ) + metadata["ORIGINALDATE"] = track.streamStartDate.strftime("%Y-%m-%d") metadata["YEAR"] = str(track.streamStartDate.strftime("%Y")) metadata["ORIGINALYEAR"] = str(track.streamStartDate.strftime("%Y")) @@ -102,13 +104,9 @@ def addMetadata( "discnumber": str(track.volumeNumber), "copyright": track.copyright if track.copyright else "", "albumartist": track.artist.name if track.artist else "", - "artist": ";".join( - [artist.name.strip() for artist in track.artists] - ), + "artist": ";".join([artist.name.strip() for artist in track.artists]), "album": track.album.title, - "date": str(track.streamStartDate) - if track.streamStartDate - else "", + "date": str(track.streamStartDate) if track.streamStartDate else "", "bpm": str(track.bpm or 0), } ) @@ -129,9 +127,7 @@ def addVideoMetadata(path: Path, video: Video): { "title": video.title, "albumartist": video.artist.name if video.artist else "", - "artist": ";".join( - [artist.name.strip() for artist in video.artists] - ), + "artist": ";".join([artist.name.strip() for artist in video.artists]), "album": video.album.title if video.album else "", "date": str(video.streamStartDate) if video.streamStartDate else "", } @@ -162,7 +158,9 @@ class Cover: self.uid = uid formatted_uid = uid.replace("-", "/") - self.url = f"https://resources.tidal.com/images/{formatted_uid}/{size}x{size}.jpg" + self.url = ( + f"https://resources.tidal.com/images/{formatted_uid}/{size}x{size}.jpg" + ) logger.debug((self.uid, self.url)) @@ -172,9 +170,7 @@ class Cover: req = requests.get(self.url) if req.status_code != 200: - logger.error( - f"could not download cover. ({req.status_code}) {self.url}" - ) + logger.error(f"could not download cover. ({req.status_code}) {self.url}") return b"" logger.debug(f"got cover: {self.uid}") @@ -185,13 +181,13 @@ class Cover: if not self.content: logger.error("cover file content is empty") return - + file = directory_path / filename if file.exists(): logger.debug(f"cover already exists ({file})") return - + makedirs(directory_path, exist_ok=True) try: diff --git a/tiddl/models/api.py b/tiddl/models/api.py index 8be8bca..68b2cfa 100644 --- a/tiddl/models/api.py +++ b/tiddl/models/api.py @@ -11,7 +11,7 @@ __all__ = [ "Favorites", "TrackStream", "Search", - "Lyrics" + "Lyrics", ] diff --git a/tiddl/utils.py b/tiddl/utils.py index 36fa55e..ad98b5d 100644 --- a/tiddl/utils.py +++ b/tiddl/utils.py @@ -80,9 +80,7 @@ def formatTrack( "disc": track.volumeNumber, "date": (track.streamStartDate if track.streamStartDate else ""), # i think we can remove year as we are able to format date - "year": track.streamStartDate.strftime("%Y") - if track.streamStartDate - else "", + "year": track.streamStartDate.strftime("%Y") if track.streamStartDate else "", "playlist": sanitizeString(playlist_title), "bpm": track.bpm or "", "quality": QUALITY_TO_ARG[track.audioQuality], @@ -130,9 +128,9 @@ def formatResource( "disc": resource.volumeNumber, "date": (resource.streamStartDate if resource.streamStartDate else ""), # i think we can remove year as we are able to format date - "year": resource.streamStartDate.strftime("%Y") - if resource.streamStartDate - else "", + "year": ( + resource.streamStartDate.strftime("%Y") if resource.streamStartDate else "" + ), "playlist": sanitizeString(playlist_title), "album_artist": sanitizeString(album_artist), "playlist_number": playlist_index or 0, @@ -223,7 +221,6 @@ async def convertFileExtension( ffmpeg.input(str(source_file)) ffmpeg.output(str(output_file), **ffmpeg_args) - @ffmpeg.on("completed") def on_completed(): logging.debug("Conversion successful for: %s", output_file) @@ -232,6 +229,7 @@ async def convertFileExtension( os.remove(source_file) except OSError as e: logging.error(f"Error removing source file {source_file}: {e}") + await ffmpeg.execute() except Exception as e: logging.error(f"FFMPEG Error during conversion of {source_file}: {e}")