diff --git a/SpotiFLAC.py b/SpotiFLAC.py index 488a99a..5c69016 100644 --- a/SpotiFLAC.py +++ b/SpotiFLAC.py @@ -213,13 +213,16 @@ class DownloadWorker(QThread): is_paused_callback = lambda: self.is_paused is_stopped_callback = lambda: self.is_stopped + auto_fallback = (self.tidal_api_url == "auto") + download_result_details = downloader.download( query=f"{track.title} {track.artists}", isrc=track.isrc, output_dir=track_outpath, quality="LOSSLESS", is_paused_callback=is_paused_callback, - is_stopped_callback=is_stopped_callback + is_stopped_callback=is_stopped_callback, + auto_fallback=auto_fallback ) if isinstance(download_result_details, str) and os.path.exists(download_result_details): @@ -391,7 +394,7 @@ class ServiceStatusDelegate(QStyledItemDelegate): class TidalAPIDelegate(QStyledItemDelegate): def paint(self, painter, option, index): - item_data = index.data(Qt.ItemDataRole.UserRole) + item_data = index.data(Qt.ItemDataRole.UserRole + 1) super().paint(painter, option, index) @@ -532,7 +535,7 @@ class ServiceComboBox(QComboBox): class SpotiFLACGUI(QWidget): def __init__(self): super().__init__() - self.current_version = "5.1" + self.current_version = "5.2" self.tracks = [] self.all_tracks = [] self.successful_downloads = [] @@ -1153,8 +1156,8 @@ class SpotiFLACGUI(QWidget): self.tidal_api_dropdown = QComboBox() self.tidal_api_dropdown.setItemDelegate(TidalAPIDelegate()) - self.tidal_api_dropdown.addItem("Default (401658)", "https://hifi.401658.xyz") - self.tidal_api_dropdown.addItem("Auto-select fastest", "auto") + self.tidal_api_dropdown.addItem("Default", "https://hifi.401658.xyz") + self.tidal_api_dropdown.addItem("Auto Fallback", "auto") self.tidal_api_dropdown.currentIndexChanged.connect(self.on_tidal_api_changed) self.refresh_api_btn = QPushButton('Refresh') @@ -1458,7 +1461,7 @@ class SpotiFLACGUI(QWidget): self.tidal_api_dropdown.addItem(label, url) item_index = self.tidal_api_dropdown.count() - 1 - self.tidal_api_dropdown.setItemData(item_index, status_data, Qt.ItemDataRole.UserRole) + self.tidal_api_dropdown.setItemData(item_index, status_data, Qt.ItemDataRole.UserRole + 1) self.log_output.append(f"Found {len(apis)} available Tidal APIs") else: @@ -1926,15 +1929,11 @@ class SpotiFLACGUI(QWidget): if service == "tidal": selected_api = self.tidal_api_dropdown.currentData() if selected_api == "auto": - apis = TidalDownloader.get_available_apis() - if apis: - tidal_api_url = apis[0]['url'] - self.log_output.append(f"Auto-selected fastest API: {tidal_api_url}") - else: - tidal_api_url = "https://hifi.401658.xyz" - self.log_output.append("Using default API: https://hifi.401658.xyz") + tidal_api_url = "auto" + self.log_output.append("Using auto fallback mode (will try multiple APIs)") else: tidal_api_url = selected_api + self.log_output.append(f"Using API: {selected_api}") self.worker = DownloadWorker( tracks_to_download, diff --git a/tidalDL.py b/tidalDL.py index 8d7423d..1d719fc 100644 --- a/tidalDL.py +++ b/tidalDL.py @@ -385,13 +385,46 @@ class TidalDownloader: print(f"Error embedding metadata: {str(e)}") return False - def download(self, query, isrc=None, output_dir=".", quality="LOSSLESS", is_paused_callback=None, is_stopped_callback=None): + def download(self, query, isrc=None, output_dir=".", quality="LOSSLESS", is_paused_callback=None, is_stopped_callback=None, auto_fallback=False): if output_dir != ".": try: os.makedirs(output_dir, exist_ok=True) except OSError as e: raise Exception(f"Directory error: {e}") - + + if auto_fallback: + apis = self.get_available_apis() + if not apis: + print("No APIs available for fallback, using current API") + return self._download_single(query, isrc, output_dir, quality, is_paused_callback, is_stopped_callback) + + last_error = None + for i, api in enumerate(apis, 1): + api_url = api.get('url') + try: + print(f"[Auto Fallback {i}/{len(apis)}] Trying: {api_url}") + + fallback_downloader = TidalDownloader(api_url=api_url) + fallback_downloader.set_progress_callback(self.progress_callback) + + result = fallback_downloader._download_single( + query, isrc, output_dir, quality, + is_paused_callback, is_stopped_callback + ) + + print(f"✓ Success with: {api_url}") + return result + + except Exception as e: + last_error = str(e) + print(f"✗ Failed with {api_url}: {last_error[:80]}") + continue + + raise Exception(f"All {len(apis)} APIs failed. Last error: {last_error}") + + return self._download_single(query, isrc, output_dir, quality, is_paused_callback, is_stopped_callback) + + def _download_single(self, query, isrc, output_dir, quality, is_paused_callback, is_stopped_callback): track_info = self.get_track_info(query, isrc) track_id = track_info.get("id")