mirror of
https://github.com/oskvr37/tiddl.git
synced 2026-06-13 04:05:08 +03:00
✨ Added audio mode filter (Dolby Atmos or Stereo) (#363)
* prepare dolby atmos config * add audio mode filter logic
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user