pull/2/head
Broque Thomas 10 months ago
parent 21967113c6
commit ffde9d07d8

@ -1 +1 @@
{"access_token": "BQB0uHOE6mtOpZRHpDmzgRLknfFzTQgH6w2HgrvJjmXydJBCG6UuiLswSY0PH_MkLm_2AlQIk-5YmvnOUZbuuHjRH-kzyJ2Rbh5MkTs4B0WXXg_Chvx3ijW-LTopxle6Vik4fnf6BHGfK71aXx68Gyq2vUo3KaE1-HMtfdiJPGebEDXnMUh-Jbpx2_1yuGQNB5qQLFVnp1q2m6jEtNWC1ZP-VJeDT4Fwt2-N13EFvQc9cm38SzeJSgqyfTOWGuFT", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752706612, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
{"access_token": "BQDMFbkA-dtq-QJ-vhjYwLpCBIZLIVCLyfkQCuRe2p3O4NVfD_nnJs9zkeCIRz91Dxi8InIERq-ZYki_aOAsbkBSF4_yUjtgjL4ygY14UbvHicIhtMwd33zP8Zj0ekdyK60mOIoOrAR4DHUZJW8B25GD4B6SgLTipTId_HzhwJATffuuQwCkz_8q8NN40CL3qXHuM9wKAtluf75GjovVTeSg_hHVWrP2mWQZPCsrpE5baIjK2Fkes3k1gPi0hd_d", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752710282, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}

File diff suppressed because it is too large Load Diff

@ -4762,7 +4762,6 @@ class TabbedDownloadManager(QTabWidget):
if parent_widget and hasattr(parent_widget, 'update_download_manager_stats'):
parent_widget.update_download_manager_stats(active_count, finished_count)
print(f"[DEBUG] Batched update: download manager stats: Active={active_count}, Finished={finished_count}")
else:
print(f"[DEBUG] Could not find parent with update_download_manager_stats method")
@ -8311,7 +8310,6 @@ class DownloadsPage(QWidget):
# Match by track title and artist
if (track_title == download_item.title and track_artist == download_item.artist):
print(f"[DEBUG] ✅ MATCH FOUND! Updating button state for '{track_title}' to '{status}'")
# Update button state based on download status
if status == 'downloading':
@ -8329,7 +8327,6 @@ class DownloadsPage(QWidget):
else:
print(f"[DEBUG] ⚠️ Unknown status '{status}' - no button update performed")
print(f"[DEBUG] ✅ Successfully updated track button state for '{download_item.title}': {status}")
return
@ -9393,6 +9390,10 @@ class DownloadsPage(QWidget):
# Cleanup API if needed
if new_status in ['cancelled', 'failed'] and hasattr(download_item, 'download_id'):
self._schedule_api_cleanup_v2(download_item)
# Schedule fallback cleanup check for errored downloads
if new_status == 'failed':
self._schedule_fallback_cleanup_check()
def _handle_missing_transfer_v2(self, download_item):
"""Handle downloads missing from API with improved grace period"""
@ -9407,26 +9408,110 @@ class DownloadsPage(QWidget):
self.download_queue.move_to_finished(download_item)
def _schedule_api_cleanup_v2(self, download_item):
"""Schedule API cleanup in background"""
def cleanup_task():
try:
if hasattr(download_item, 'download_id') and download_item.soulseek_client:
import asyncio
"""Schedule API cleanup with robust retry mechanism"""
def cleanup_task_with_retry():
import time
import asyncio
max_retries = 3
retry_delays = [2, 5, 10] # Progressive delays in seconds
for attempt in range(max_retries):
try:
if not (hasattr(download_item, 'download_id') and download_item.soulseek_client):
return
# Wait before cleanup to let API stabilize
if attempt == 0:
time.sleep(1) # Initial delay for API stabilization
else:
time.sleep(retry_delays[attempt - 1])
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
success = loop.run_until_complete(
download_item.soulseek_client.cancel_download(
download_item.download_id,
getattr(download_item, 'username', ''),
remove=True
try:
success = loop.run_until_complete(
download_item.soulseek_client.cancel_download(
download_item.download_id,
getattr(download_item, 'username', ''),
remove=True
)
)
if success:
return # Success, exit retry loop
else:
if attempt == max_retries - 1:
print(f"❌ API cleanup failed after {max_retries} attempts: {download_item.title}")
finally:
loop.close()
except Exception as e:
if attempt == max_retries - 1:
print(f"❌ API cleanup failed after {max_retries} attempts: {e}")
# Continue to next retry
self._optimized_api_pool.submit(cleanup_task_with_retry)
def _schedule_fallback_cleanup_check(self):
"""Schedule a fallback cleanup check for persistent errored entries"""
def fallback_cleanup_task():
import time
import asyncio
# Wait longer before fallback cleanup
time.sleep(30) # 30 second delay
try:
if not self.soulseek_client:
return
# Get current transfers to find persistent errored entries
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
transfers_data = loop.run_until_complete(
self.soulseek_client.get_all_downloads()
)
if transfers_data:
# Find errored entries that should have been cleaned up
persistent_errors = []
for user_data in transfers_data:
if 'directories' in user_data:
for directory in user_data['directories']:
if 'files' in directory:
for transfer in directory['files']:
state = transfer.get('state', '')
if any(error_state in state for error_state in ['Failed', 'Errored', 'Cancelled']):
persistent_errors.append(transfer)
# Clean up persistent errors (max 5 at a time to be conservative)
for transfer in persistent_errors[:5]:
try:
success = loop.run_until_complete(
self.soulseek_client.cancel_download(
transfer.get('id'),
transfer.get('username', ''),
remove=True
)
)
if success:
print(f"✅ Fallback cleanup successful for persistent error")
except Exception as e:
pass # Silent fallback failures
finally:
loop.close()
pass # Silent cleanup success
except Exception as e:
print(f"❌ API cleanup failed: {e}")
pass # Silent fallback failures
self._optimized_api_pool.submit(cleanup_task)
# Schedule fallback cleanup
self._optimized_api_pool.submit(fallback_cleanup_task)
def _cleanup_status_thread_v2(self, thread):
"""Clean up status threads"""
@ -9486,7 +9571,6 @@ class DownloadsPage(QWidget):
if 'files' in directory:
all_transfers.extend(directory['files'])
print(f"[DEBUG] Processing {len(all_transfers)} active transfers from API")
# Track which transfers have been matched to prevent duplicate assignments
matched_transfer_ids = set()
@ -9496,7 +9580,6 @@ class DownloadsPage(QWidget):
if download_item.status.lower() in ['completed', 'finished', 'cancelled', 'failed']:
continue # Skip completed items
print(f"[DEBUG] Looking for matches for download: '{download_item.title}' by '{download_item.artist}' (download_id: {getattr(download_item, 'download_id', 'None')})")
# Try to match by download_id first (most reliable)
matching_transfer = None
@ -9749,33 +9832,44 @@ class DownloadsPage(QWidget):
file_path=download_item.file_path
)
# IMMEDIATE CLEANUP: Remove errored download from API immediately
# IMPROVED CLEANUP: Remove errored download with retry mechanism
if download_item.download_id and download_item.username and download_item.soulseek_client:
def cleanup_errored_download():
try:
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
success = loop.run_until_complete(
download_item.soulseek_client.cancel_download(
download_item.download_id,
download_item.username,
remove=True
def cleanup_errored_download_with_retry():
import time
import asyncio
max_retries = 3
for attempt in range(max_retries):
try:
# Add delay for API stabilization
if attempt == 0:
time.sleep(2) # Initial delay for errored downloads
else:
time.sleep(5 * attempt) # Progressive delay
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
success = loop.run_until_complete(
download_item.soulseek_client.cancel_download(
download_item.download_id,
download_item.username,
remove=True
)
)
)
loop.close()
if success:
print(f"✓ Immediately cleaned up errored download {download_item.download_id}")
else:
print(f"⚠️ Failed to clean up errored download {download_item.download_id}")
except Exception as e:
print(f"⚠️ Error cleaning up errored download: {e}")
loop.close()
if success:
return # Success, exit retry loop
elif attempt == max_retries - 1:
print(f"❌ Failed to clean up errored download after {max_retries} attempts: {download_item.title}")
except Exception as e:
if attempt == max_retries - 1:
print(f"❌ Error cleaning up errored download after {max_retries} attempts: {e}")
# Run cleanup in background thread
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=1) as executor:
executor.submit(cleanup_errored_download)
executor.submit(cleanup_errored_download_with_retry)
print(f"[DEBUG] Moving failed download '{download_item.title}' to finished queue")
self.download_queue.move_to_finished(download_item)

Loading…
Cancel
Save