diff --git a/docs/config.example.toml b/docs/config.example.toml index 219c6f9..cca8306 100644 --- a/docs/config.example.toml +++ b/docs/config.example.toml @@ -91,6 +91,13 @@ write_lrc_file = false # downloads will continue under "FooBar". match_existing_path_case = false +# Dolby Atmos filter +# none - download only STEREO tracks +# only - download only DOLBY_ATMOS tracks +# allow - download both +# (both versions won't be downloaded at a time, it depends on what Tidal returns) +atmos_filter = "none" + [metadata] # embed metadata in files diff --git a/tiddl/cli/commands/download/__init__.py b/tiddl/cli/commands/download/__init__.py index 03bf778..c530f61 100644 --- a/tiddl/cli/commands/download/__init__.py +++ b/tiddl/cli/commands/download/__init__.py @@ -20,6 +20,7 @@ from tiddl.cli.config import ( ARTIST_SINGLES_FILTER_LITERAL, VALID_M3U_RESOURCE_LITERAL, VIDEOS_FILTER_LITERAL, + ATMOS_FILTER_LITERAL, ) from tiddl.cli.utils.resource import TidalResource from tiddl.cli.ctx import Context @@ -126,6 +127,14 @@ def download_callback( help="Raise an error on resource download failure. Use for debugging", ), ] = False, + DOLBY_ATMOS_FILTER: Annotated[ + ATMOS_FILTER_LITERAL, + typer.Option( + "--dolby-atmos", + "-da", + help="Dolby Atmos filter, 'none' to exclude, 'allow' to include, 'only' to download only Dolby Atmos, if available.", + ), + ] = CONFIG.download.atmos_filter, ): """ Download Tidal resources. @@ -208,6 +217,7 @@ def download_callback( download_path=DOWNLOAD_PATH, scan_path=SCAN_PATH, match_existing_path_case=CONFIG.download.match_existing_path_case, + dolby_atmos_filter=DOLBY_ATMOS_FILTER, ) class Metadata: diff --git a/tiddl/cli/commands/download/downloader.py b/tiddl/cli/commands/download/downloader.py index 3528671..80eb7a8 100644 --- a/tiddl/cli/commands/download/downloader.py +++ b/tiddl/cli/commands/download/downloader.py @@ -7,7 +7,7 @@ from tempfile import NamedTemporaryFile import aiofiles import aiohttp -from tiddl.cli.config import VIDEOS_FILTER_LITERAL +from tiddl.cli.config import VIDEOS_FILTER_LITERAL, ATMOS_FILTER_LITERAL from tiddl.cli.utils.download import get_existing_track_filename from tiddl.cli.utils.path import resolve_existing_path_case from tiddl.core.api import ApiError, TidalAPI @@ -52,6 +52,7 @@ class Downloader: download_path: Path scan_path: Path match_existing_path_case: bool + dolby_atmos_filter: ATMOS_FILTER_LITERAL def __init__( self, @@ -65,6 +66,7 @@ class Downloader: download_path: Path, scan_path: Path, match_existing_path_case: bool = False, + dolby_atmos_filter: ATMOS_FILTER_LITERAL = "none", ) -> None: self.api = tidal_api self.rich_output = rich_output @@ -76,6 +78,7 @@ class Downloader: self.download_path = download_path self.scan_path = scan_path self.match_existing_path_case = match_existing_path_case + self.dolby_atmos_filter = dolby_atmos_filter def get_path(self, base_path: Path, relative_path: Path) -> Path: if self.match_existing_path_case: @@ -144,9 +147,23 @@ class Downloader: stream = self.api.get_track_stream( track_id=item.id, quality=self.track_quality ) + log.debug( - f"{stream.trackId=}, {stream.audioQuality}, {stream.audioMode}" + f"{stream.trackId=}, {stream.audioQuality=}, {stream.audioMode=}" ) + + if ( + self.dolby_atmos_filter == "none" + and stream.audioMode == "DOLBY_ATMOS" + ) or ( + self.dolby_atmos_filter == "only" + and stream.audioMode == "STEREO" + ): + self.rich_output.console.print( + f"[blue]Skipping[/] [gray]{item.title}[/] [blue]due to Dolby Atmos filter[/] {self.dolby_atmos_filter}" + ) + return None, False + except ApiError as e: log.error(f"{item.id=} {e=}") self.rich_output.console.print( diff --git a/tiddl/cli/config.py b/tiddl/cli/config.py index 0535e24..aa69335 100644 --- a/tiddl/cli/config.py +++ b/tiddl/cli/config.py @@ -14,6 +14,7 @@ ARTIST_SINGLES_FILTER_LITERAL = Literal["none", "only", "include"] VALID_M3U_RESOURCE_LITERAL = Literal["album", "playlist", "mix"] VALID_RESOURCE_COVER_SAVE_LITERAL = Literal["track", "album", "playlist"] VIDEOS_FILTER_LITERAL = Literal["none", "only", "allow"] +ATMOS_FILTER_LITERAL = Literal["none", "only", "allow"] log = getLogger(__name__) @@ -57,6 +58,7 @@ class Config(BaseModel): rewrite_metadata: bool = False write_lrc_file: bool = False match_existing_path_case: bool = False + atmos_filter: ATMOS_FILTER_LITERAL = "none" def model_post_init(self, __context): # set scan path to download path when download path is non default