From f9801d095f4ae76e7fa653c5a7120dc6c63870ff Mon Sep 17 00:00:00 2001 From: cashmere Date: Fri, 3 Jul 2026 14:46:57 +0200 Subject: fix: use lossless-capable TIDAL client for normal streaming The default tidalapi OAuth client no longer has lossless/hi-res streaming entitlements, causing all downloads to fall back to AAC 320 kbps even when HI_RES_LOSSLESS is requested. - Replace broken Atmos/Fire TV client credentials with the working Android Atmos client (km8T1xS355y7dd3H) which supports lossless stereo as well as Dolby Atmos. - Use that client for the main session, not just Atmos tracks. - Refresh existing tokens on login so they are bound to the new client. --- tidal_dl_ng/config.py | 16 ++++++++++++++-- tidal_dl_ng/constants.py | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'tidal_dl_ng') diff --git a/tidal_dl_ng/config.py b/tidal_dl_ng/config.py index aa36cf6..ccf6766 100644 --- a/tidal_dl_ng/config.py +++ b/tidal_dl_ng/config.py @@ -101,6 +101,11 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): self.cls_model = ModelToken tidal_config: tidalapi.Config = tidalapi.Config(item_limit=10000) self.session = tidalapi.Session(tidal_config) + # The default tidalapi OAuth client does not have lossless / hi-res + # streaming entitlements. Use the Android Atmos client for the main + # session; it supports lossless stereo as well as Dolby Atmos. + self.session.config.client_id = ATMOS_CLIENT_ID + self.session.config.client_secret = ATMOS_CLIENT_SECRET self.original_client_id = self.session.config.client_id self.original_client_secret = self.session.config.client_secret # Lock to ensure session-switching is thread-safe. @@ -114,8 +119,6 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): # session re-authentication when the session is already in the # correct mode (Atmos or Normal). self.is_atmos_session = False - # self.session.config.client_id = "km8T1xS355y7dd3H" - # self.session.config.client_secret = "vcmeGW1OuZ0fWYMCSZ6vNvSLJlT3XEpW0ambgYt5ZuI=" self.file_path = path_file_token() self.token_from_storage = self.read(self.file_path) @@ -146,6 +149,15 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): self.data.expiry_time, is_pkce=do_pkce, ) + + if result and self.data.refresh_token: + # Existing tokens were issued for the previous default client + # which lacks lossless entitlements. Refresh the token so it + # is bound to the current (lossless-capable) client. + result = self.session.token_refresh(self.data.refresh_token) + + if result: + self.token_persist() except Exception: result = False # Remove token file. Probably corrupt or invalid. diff --git a/tidal_dl_ng/constants.py b/tidal_dl_ng/constants.py index 696b62a..887c35b 100644 --- a/tidal_dl_ng/constants.py +++ b/tidal_dl_ng/constants.py @@ -18,9 +18,9 @@ FILENAME_LENGTH_MAX: int = 255 FORMAT_TEMPLATE_EXPLICIT: str = " (Explicit)" METADATA_EXPLICIT: str = " 🅴" -# Dolby Atmos API credentials (obfuscated) -ATMOS_ID_B64 = "N203QX" + "AwSkM5aj" + "FjT00zbg==" -ATMOS_SECRET_B64 = "dlJBZEEx" + "MDh0bHZrSnB" + "Uc0daUzhyR1" + "o3eFRsYkow" + "cWFaMks5c2F" + "FenNnWT0=" +# Android Atmos client credentials (obfuscated). +ATMOS_ID_B64 = "a204VDF4UzM" + "1NXk3ZGQzSA==" +ATMOS_SECRET_B64 = "dmNtZUdXMU91" + "WjBmV1lNQ1NaNnZOdlNMSmxUM1hFcFcwYW1iZ1l0NVp1ST0=" ATMOS_CLIENT_ID = base64.b64decode(ATMOS_ID_B64).decode("utf-8") ATMOS_CLIENT_SECRET = base64.b64decode(ATMOS_SECRET_B64).decode("utf-8") -- cgit v1.3.1