From 054665e49431b4071b010152aa2e5df08dc8ca74 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Sun, 27 Jul 2025 15:55:24 -0700 Subject: [PATCH] good --- .spotify_cache | 2 +- ui/pages/__pycache__/artists.cpython-312.pyc | Bin 194348 -> 196168 bytes ui/pages/__pycache__/sync.cpython-312.pyc | Bin 227001 -> 228783 bytes ui/pages/artists.py | 43 ++++++++++++++++++- ui/pages/sync.py | 42 +++++++++++++++++- 5 files changed, 82 insertions(+), 5 deletions(-) diff --git a/.spotify_cache b/.spotify_cache index 0ca3eb59..a04fbc00 100644 --- a/.spotify_cache +++ b/.spotify_cache @@ -1 +1 @@ -{"access_token": "BQAx5uqD8QJ7K2LzND9FXXb07OJ9t85iKLs3hzKOzJ0dP4L6xWrX-qdiF5Lh7pAW1GAsYLFxAgzad7Vi0_Db7RQNQOvKydHbFaxQkudzPIsQlDN3tqW9N6NK_5Y0NkkT4uB_LnVgkqxFif0dE6Khw9a0yJBpOwU8caZxf5-gWqXXmc2q4edbAo_IyxgoQ2Qvw5X7ywgQZhxeD3Z-QfbojQEfpEjUrXk71haRs0oLmcxsNi4wMB77iORpAO38zsEg", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753656578, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file +{"access_token": "BQCi9oKx-56eoC1PB6pL-sW7IS-3RPGj-3ZvpsHmT5ZJXW4Qj16sldlovl05GbrmwSXwr9xnO_T1UxIu-E3mrl9HvtBVCJjZ3Oj7iIZX5mfwKc6Vhx18a0yimYN2mxm1YChHO5Pe8mNyI_ilSYZI1fqg6sNxz_ZvJERSwFGDVIdOxcQDYpTIMQnSDPdK7xs4WpWvbwpWLq9PmhkePeVQtW-UXP_jrjzK5kRWyu-AdnsjtMGFuJaQqv9F4hSJEUK8", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753660130, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file diff --git a/ui/pages/__pycache__/artists.cpython-312.pyc b/ui/pages/__pycache__/artists.cpython-312.pyc index 8444fa48dab0c41b54ac6728e52c69f5e73f92f3..c2f2d4bb1679c43e9842f7b75ae299a381a22c1d 100644 GIT binary patch delta 4761 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 delta 3184 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 Ww`=&@J|?K3H}oy<|3JfE)$acwevJG8 diff --git a/ui/pages/__pycache__/sync.cpython-312.pyc b/ui/pages/__pycache__/sync.cpython-312.pyc index f5b9fac581f6fa2f9229689ce881624b4edc87b8..8c37f49ba54463a2482ab84fc17f4d60311ad9aa 100644 GIT binary patch delta 2391 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 l5xk>;$LD=|Q-F4GujncrX2*h<{hh{4ZD8y1W1Y delta 869 zcmYk4TWpL`5XaA)`L^9!U$v4_+VHuwx|i;D-AU*Qs#qj0t*o?K{T3xezwK>nBWWyY zr0$6!qIf}w2#K6DNSb&kaa+*_4^pj=C=!>rmT(R)&cpAVOlJP`pUJs2ptrx%Q}5{d zASRDni*JlWbLy^CxSGn(6HKRe;)fugf=Brd4`&OQ>5Mbe70$nas(`VO*L25O$Y**a z3tCtsg#0U*(<^=+<&}Hk6qV=VQaCOy=HdWA-;q)nP91r;16rxjiKmIXus-LpnHjU! zvj{%`tWC4kt*hE>RMr{=IbLIVxYmkRZ{6UveoOb(b&av6QU9e? zZ#8P_!gb;QGK-vpX^R&v`sKw-FqAI(a2f=}OCNSZN}@cP7~Rx0VmsaQV|z*>)R@SM zc4ait*@@^MKZge(Wf{Dm$C=C5qIKJIzkgiFnNbQ6n{0nrU+mC?e+JIhAS$-a!Vfk$ zCf3cxogA)+SMxE6!&9nBM}zcY%myJ`#i()U5WYp2%i%Va1hGPYDu-~Q%sOz7A&BdVRX6% zlqIkXY01g|Ia8qsmjT1DrYNglmHuq1Y2#UR!^Ck=B<_aMix3n88*wtgS1QO2LXW};g_8=q6m~20)0;*N0FWn&=aPe9 f^L;GmqBn|e4nu_=!-J4sI|N*(hJR-2{_Ein`{?}n diff --git a/ui/pages/artists.py b/ui/pages/artists.py index bc8b58ee..fad746e0 100644 --- a/ui/pages/artists.py +++ b/ui/pages/artists.py @@ -1953,7 +1953,9 @@ class DownloadMissingAlbumTracksModal(QDialog): download_info['downloading_start_time'] = time.time() # 90-second timeout for being stuck at 0% elif time.time() - download_info['downloading_start_time'] > 90: - print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck at 0%. Retrying.") + print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck at 0%. Cancelling and retrying.") + # Cancel the old download before retry + self.cancel_download_before_retry(download_info) if download_info in self.active_downloads: self.active_downloads.remove(download_info) self.retry_parallel_download_with_fallback(download_info) @@ -1968,13 +1970,50 @@ class DownloadMissingAlbumTracksModal(QDialog): if 'queued_start_time' not in download_info: download_info['queued_start_time'] = time.time() elif time.time() - download_info['queued_start_time'] > 90: # 90-second timeout - print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck in queue. Retrying.") + print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck in queue. Cancelling and retrying.") + # Cancel the old download before retry + self.cancel_download_before_retry(download_info) if download_info in self.active_downloads: self.active_downloads.remove(download_info) self.retry_parallel_download_with_fallback(download_info) self._is_status_update_running = False + def cancel_download_before_retry(self, download_info): + """Cancel the current download before retrying with alternative source""" + try: + slskd_result = download_info.get('slskd_result') + if not slskd_result: + print("⚠️ No slskd_result found in download_info for cancellation") + return + + # Extract download details for cancellation + download_id = download_info.get('download_id') + username = getattr(slskd_result, 'username', None) + + if download_id and username: + print(f"🚫 Cancelling timed-out album download: {download_id} from {username}") + + # Use asyncio to call the async cancel method + import asyncio + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + success = loop.run_until_complete( + self.soulseek_client.cancel_download(download_id, username, remove=False) + ) + if success: + print(f"✅ Successfully cancelled album download {download_id}") + else: + print(f"⚠️ Failed to cancel album download {download_id}") + finally: + loop.close() + else: + print(f"⚠️ Missing download_id ({download_id}) or username ({username}) for album cancellation") + + except Exception as e: + print(f"❌ Error cancelling album download: {e}") + def retry_parallel_download_with_fallback(self, failed_download_info): """Retries a failed download by selecting the next-best cached candidate""" download_index = failed_download_info['download_index'] diff --git a/ui/pages/sync.py b/ui/pages/sync.py index 1c970d9c..c8fdb646 100644 --- a/ui/pages/sync.py +++ b/ui/pages/sync.py @@ -4190,7 +4190,9 @@ class DownloadMissingTracksModal(QDialog): download_info['downloading_start_time'] = time.time() # 90-second timeout for being stuck at 0% elif time.time() - download_info['downloading_start_time'] > 90: - print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck at 0%. Retrying.") + print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck at 0%. Cancelling and retrying.") + # Cancel the old download before retry + self.cancel_download_before_retry(download_info) if download_info in self.active_downloads: self.active_downloads.remove(download_info) self.retry_parallel_download_with_fallback(download_info) @@ -4206,13 +4208,49 @@ class DownloadMissingTracksModal(QDialog): if 'queued_start_time' not in download_info: download_info['queued_start_time'] = time.time() elif time.time() - download_info['queued_start_time'] > 90: # 90-second timeout - print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck in queue. Retrying.") + print(f"⚠️ Download for '{download_info['slskd_result'].filename}' is stuck in queue. Cancelling and retrying.") + # Cancel the old download before retry + self.cancel_download_before_retry(download_info) if download_info in self.active_downloads: self.active_downloads.remove(download_info) self.retry_parallel_download_with_fallback(download_info) self._is_status_update_running = False + def cancel_download_before_retry(self, download_info): + """Cancel the current download before retrying with alternative source""" + try: + slskd_result = download_info.get('slskd_result') + if not slskd_result: + print("⚠️ No slskd_result found in download_info for cancellation") + return + + # Extract download details for cancellation + download_id = download_info.get('download_id') + username = getattr(slskd_result, 'username', None) + + if download_id and username: + print(f"🚫 Cancelling timed-out download: {download_id} from {username}") + + # Use asyncio to call the async cancel method + import asyncio + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + success = loop.run_until_complete( + self.soulseek_client.cancel_download(download_id, username, remove=False) + ) + if success: + print(f"✅ Successfully cancelled download {download_id}") + else: + print(f"⚠️ Failed to cancel download {download_id}") + finally: + loop.close() + else: + print(f"⚠️ Missing download_id ({download_id}) or username ({username}) for cancellation") + + except Exception as e: + print(f"❌ Error cancelling download: {e}") def retry_parallel_download_with_fallback(self, failed_download_info): """Retries a failed download by selecting the next-best cached candidate."""