Added audio mode filter (Dolby Atmos or Stereo) (#363)

* prepare dolby atmos config

* add audio mode filter logic
This commit is contained in:
Oskar Dudziński
2026-05-06 00:57:46 +02:00
committed by GitHub
parent ee160fc5bc
commit 459d5a50b9
4 changed files with 38 additions and 2 deletions
+7
View File
@@ -91,6 +91,13 @@ write_lrc_file = false
# downloads will continue under "FooBar". # downloads will continue under "FooBar".
match_existing_path_case = false 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] [metadata]
# embed metadata in files # embed metadata in files
+10
View File
@@ -20,6 +20,7 @@ from tiddl.cli.config import (
ARTIST_SINGLES_FILTER_LITERAL, ARTIST_SINGLES_FILTER_LITERAL,
VALID_M3U_RESOURCE_LITERAL, VALID_M3U_RESOURCE_LITERAL,
VIDEOS_FILTER_LITERAL, VIDEOS_FILTER_LITERAL,
ATMOS_FILTER_LITERAL,
) )
from tiddl.cli.utils.resource import TidalResource from tiddl.cli.utils.resource import TidalResource
from tiddl.cli.ctx import Context from tiddl.cli.ctx import Context
@@ -126,6 +127,14 @@ def download_callback(
help="Raise an error on resource download failure. Use for debugging", help="Raise an error on resource download failure. Use for debugging",
), ),
] = False, ] = 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. Download Tidal resources.
@@ -208,6 +217,7 @@ def download_callback(
download_path=DOWNLOAD_PATH, download_path=DOWNLOAD_PATH,
scan_path=SCAN_PATH, scan_path=SCAN_PATH,
match_existing_path_case=CONFIG.download.match_existing_path_case, match_existing_path_case=CONFIG.download.match_existing_path_case,
dolby_atmos_filter=DOLBY_ATMOS_FILTER,
) )
class Metadata: class Metadata:
+19 -2
View File
@@ -7,7 +7,7 @@ from tempfile import NamedTemporaryFile
import aiofiles import aiofiles
import aiohttp 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.download import get_existing_track_filename
from tiddl.cli.utils.path import resolve_existing_path_case from tiddl.cli.utils.path import resolve_existing_path_case
from tiddl.core.api import ApiError, TidalAPI from tiddl.core.api import ApiError, TidalAPI
@@ -52,6 +52,7 @@ class Downloader:
download_path: Path download_path: Path
scan_path: Path scan_path: Path
match_existing_path_case: bool match_existing_path_case: bool
dolby_atmos_filter: ATMOS_FILTER_LITERAL
def __init__( def __init__(
self, self,
@@ -65,6 +66,7 @@ class Downloader:
download_path: Path, download_path: Path,
scan_path: Path, scan_path: Path,
match_existing_path_case: bool = False, match_existing_path_case: bool = False,
dolby_atmos_filter: ATMOS_FILTER_LITERAL = "none",
) -> None: ) -> None:
self.api = tidal_api self.api = tidal_api
self.rich_output = rich_output self.rich_output = rich_output
@@ -76,6 +78,7 @@ class Downloader:
self.download_path = download_path self.download_path = download_path
self.scan_path = scan_path self.scan_path = scan_path
self.match_existing_path_case = match_existing_path_case 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: def get_path(self, base_path: Path, relative_path: Path) -> Path:
if self.match_existing_path_case: if self.match_existing_path_case:
@@ -144,9 +147,23 @@ class Downloader:
stream = self.api.get_track_stream( stream = self.api.get_track_stream(
track_id=item.id, quality=self.track_quality track_id=item.id, quality=self.track_quality
) )
log.debug( 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: except ApiError as e:
log.error(f"{item.id=} {e=}") log.error(f"{item.id=} {e=}")
self.rich_output.console.print( self.rich_output.console.print(
+2
View File
@@ -14,6 +14,7 @@ ARTIST_SINGLES_FILTER_LITERAL = Literal["none", "only", "include"]
VALID_M3U_RESOURCE_LITERAL = Literal["album", "playlist", "mix"] VALID_M3U_RESOURCE_LITERAL = Literal["album", "playlist", "mix"]
VALID_RESOURCE_COVER_SAVE_LITERAL = Literal["track", "album", "playlist"] VALID_RESOURCE_COVER_SAVE_LITERAL = Literal["track", "album", "playlist"]
VIDEOS_FILTER_LITERAL = Literal["none", "only", "allow"] VIDEOS_FILTER_LITERAL = Literal["none", "only", "allow"]
ATMOS_FILTER_LITERAL = Literal["none", "only", "allow"]
log = getLogger(__name__) log = getLogger(__name__)
@@ -57,6 +58,7 @@ class Config(BaseModel):
rewrite_metadata: bool = False rewrite_metadata: bool = False
write_lrc_file: bool = False write_lrc_file: bool = False
match_existing_path_case: bool = False match_existing_path_case: bool = False
atmos_filter: ATMOS_FILTER_LITERAL = "none"
def model_post_init(self, __context): def model_post_init(self, __context):
# set scan path to download path when download path is non default # set scan path to download path when download path is non default