diff options
Diffstat (limited to 'tidal_dl_ng/config.py')
| -rw-r--r-- | tidal_dl_ng/config.py | 90 |
1 files changed, 67 insertions, 23 deletions
diff --git a/tidal_dl_ng/config.py b/tidal_dl_ng/config.py index 1ba8c8c..8be4459 100644 --- a/tidal_dl_ng/config.py +++ b/tidal_dl_ng/config.py @@ -2,10 +2,9 @@ import json import os import shutil from collections.abc import Callable -from contextlib import contextmanager from json import JSONDecodeError from pathlib import Path -from threading import Event +from threading import Event, Lock from typing import Any import tidalapi @@ -103,6 +102,19 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): self.cls_model = ModelToken tidal_config: tidalapi.Config = tidalapi.Config(item_limit=10000) self.session = tidalapi.Session(tidal_config) + 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. + # This lock protects against a race condition where one thread + # changes the session credentials while another is using them. + # It is intentionally held by Download._get_stream_info + # for the *entire* duration of the credential switch AND + # the get_stream() call. + self.stream_lock = Lock() + # State-tracking flag to prevent redundant, expensive + # 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() @@ -116,7 +128,8 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): if settings: self.settings = settings - self.session.audio_quality = tidalapi.Quality(self.settings.data.quality_audio) + if not self.is_atmos_session: + self.session.audio_quality = tidalapi.Quality(self.settings.data.quality_audio) self.session.video_quality = tidalapi.VideoQuality.high return True @@ -162,33 +175,64 @@ class Tidal(BaseConfig, metaclass=SingletonMeta): self.set_option("expiry_time", self.session.expiry_time) self.save() - @contextmanager - def atmos_session_context(self): + def switch_to_atmos_session(self) -> bool: + """ + Switches the shared session to Dolby Atmos credentials. + Only re-authenticates if not already in Atmos mode. + + Returns: + bool: True if successful or already in Atmos mode, False otherwise. + """ + # If we are already in Atmos mode, do nothing. + if self.is_atmos_session: + return True + + print("Switching session context to Dolby Atmos...") + self.session.config.client_id = ATMOS_CLIENT_ID + self.session.config.client_secret = ATMOS_CLIENT_SECRET + self.session.audio_quality = ATMOS_REQUEST_QUALITY - if not self.session.check_login(): - print("Not logged in.") + # Re-login with new credentials + if not self.login_token(do_pkce=self.is_pkce): + print("Warning: Atmos session authentication failed.") + # Try to switch back to normal to be safe + self.restore_normal_session(force=True) + return False - original_client_id = self.session.config.client_id - original_client_secret = self.session.config.client_secret - original_audio_quality = self.session.audio_quality + self.is_atmos_session = True # Set the flag + print("Session is now in Atmos mode.") + return True - try: - self.session.config.client_id = ATMOS_CLIENT_ID - self.session.config.client_secret = ATMOS_CLIENT_SECRET - self.session.audio_quality = ATMOS_REQUEST_QUALITY + def restore_normal_session(self, force: bool = False) -> bool: + """ + Restores the shared session to the original user credentials. + Only re-authenticates if not already in Normal mode. + + Args: + force: If True, forces restoration even if already in Normal mode. + + Returns: + bool: True if successful or already in Normal mode, False otherwise. + """ + # If we are already in Normal mode (and not forced), do nothing. + if not self.is_atmos_session and not force: + return True - if not self.login_token(do_pkce=self.is_pkce): - print("Warning: Session restore failed.") + print("Restoring session context to Normal...") + self.session.config.client_id = self.original_client_id + self.session.config.client_secret = self.original_client_secret - yield + # Explicitly restore audio quality to user's configured setting + self.session.audio_quality = tidalapi.Quality(self.settings.data.quality_audio) - finally: - self.session.config.client_id = original_client_id - self.session.config.client_secret = original_client_secret - self.session.audio_quality = original_audio_quality + # Re-login with original credentials + if not self.login_token(do_pkce=self.is_pkce): + print("Warning: Restoring the original session context failed. Please restart the application.") + return False - if not self.login_token(do_pkce=self.is_pkce): - print("Warning: Restoring the original session context failed. Please restart the application.") + self.is_atmos_session = False # Set the flag + print("Session is now in Normal mode.") + return True def login(self, fn_print: Callable) -> bool: is_token = self.login_token() |
