better
This commit is contained in:
parent
c70f87a9e5
commit
9ee81165c2
1 changed files with 177 additions and 29 deletions
206
ui/pages/sync.py
206
ui/pages/sync.py
|
|
@ -158,7 +158,7 @@ class PlaylistTrackAnalysisWorker(QRunnable):
|
||||||
|
|
||||||
class TrackDownloadWorkerSignals(QObject):
|
class TrackDownloadWorkerSignals(QObject):
|
||||||
"""Signals for track download worker"""
|
"""Signals for track download worker"""
|
||||||
download_completed = pyqtSignal(int, int, str) # download_index, track_index, download_id
|
download_started = pyqtSignal(int, int, str) # download_index, track_index, download_id
|
||||||
download_failed = pyqtSignal(int, int, str) # download_index, track_index, error_message
|
download_failed = pyqtSignal(int, int, str) # download_index, track_index, error_message
|
||||||
|
|
||||||
class TrackDownloadWorker(QRunnable):
|
class TrackDownloadWorker(QRunnable):
|
||||||
|
|
@ -216,7 +216,7 @@ class TrackDownloadWorker(QRunnable):
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
if download_id:
|
if download_id:
|
||||||
self.signals.download_completed.emit(self.download_index, self.track_index, download_id)
|
self.signals.download_started.emit(self.download_index, self.track_index, download_id)
|
||||||
else:
|
else:
|
||||||
self.signals.download_failed.emit(self.download_index, self.track_index, "No search results found")
|
self.signals.download_failed.emit(self.download_index, self.track_index, "No search results found")
|
||||||
|
|
||||||
|
|
@ -4267,7 +4267,7 @@ class DownloadMissingTracksModal(QDialog):
|
||||||
|
|
||||||
# Create download worker
|
# Create download worker
|
||||||
worker = TrackDownloadWorker(track, self.parent_page.soulseek_client, self.current_download, track_index)
|
worker = TrackDownloadWorker(track, self.parent_page.soulseek_client, self.current_download, track_index)
|
||||||
worker.signals.download_completed.connect(self.on_track_download_complete)
|
worker.signals.download_started.connect(self.on_track_download_started)
|
||||||
worker.signals.download_failed.connect(self.on_track_download_failed)
|
worker.signals.download_failed.connect(self.on_track_download_failed)
|
||||||
|
|
||||||
# Track worker for cleanup
|
# Track worker for cleanup
|
||||||
|
|
@ -4289,37 +4289,35 @@ class DownloadMissingTracksModal(QDialog):
|
||||||
return i
|
return i
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def on_track_download_complete(self, download_index, track_index, download_id):
|
def on_track_download_started(self, download_index, track_index, download_id):
|
||||||
"""Handle successful track download"""
|
"""Handle download start - set up monitoring for completion"""
|
||||||
print(f"✅ Download {download_index + 1} completed: {download_id}")
|
print(f"⏬ Download {download_index + 1} started: {download_id}")
|
||||||
|
|
||||||
|
# Store download info for monitoring
|
||||||
|
if not hasattr(self, 'active_downloads'):
|
||||||
|
self.active_downloads = {}
|
||||||
|
self.active_downloads[download_id] = {
|
||||||
|
'download_index': download_index,
|
||||||
|
'track_index': track_index,
|
||||||
|
'status': 'downloading'
|
||||||
|
}
|
||||||
|
|
||||||
# Update main console log
|
# Update main console log
|
||||||
if hasattr(self.parent_page, 'log_area') and download_index < len(self.missing_tracks):
|
if hasattr(self.parent_page, 'log_area') and download_index < len(self.missing_tracks):
|
||||||
track = self.missing_tracks[download_index].spotify_track
|
track = self.missing_tracks[download_index].spotify_track
|
||||||
track_name = track.name
|
track_name = track.name
|
||||||
artist_name = track.artists[0] if track.artists else "Unknown Artist"
|
artist_name = track.artists[0] if track.artists else "Unknown Artist"
|
||||||
remaining = len(self.missing_tracks) - (download_index + 1)
|
self.parent_page.log_area.append(f"⏬ Started download: {track_name} by {artist_name}")
|
||||||
self.parent_page.log_area.append(f"✅ Downloaded: {track_name} by {artist_name} ({remaining} remaining)")
|
|
||||||
|
|
||||||
# Update table row
|
# Table already shows "⏬ Downloading" from download_next_track()
|
||||||
if track_index is not None:
|
|
||||||
downloaded_item = QTableWidgetItem("✅ Downloaded")
|
|
||||||
downloaded_item.setFlags(downloaded_item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
|
||||||
downloaded_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
||||||
self.track_table.setItem(track_index, 4, downloaded_item)
|
|
||||||
|
|
||||||
# Update progress
|
# Start monitoring this download for completion
|
||||||
|
self.start_download_monitoring(download_id, download_index, track_index)
|
||||||
|
|
||||||
|
# Increment download counter (tracking started downloads)
|
||||||
self.current_download += 1
|
self.current_download += 1
|
||||||
self.download_progress.setValue(self.current_download)
|
|
||||||
|
|
||||||
# Update playlist status indicator
|
# Continue with next download (don't wait for completion)
|
||||||
playlist_item = self.find_playlist_item()
|
|
||||||
if playlist_item:
|
|
||||||
total = len(self.missing_tracks) if hasattr(self, 'missing_tracks') else 0
|
|
||||||
status_text = f"⏬ Downloading {self.current_download}/{total}"
|
|
||||||
playlist_item.update_operation_status(status_text)
|
|
||||||
|
|
||||||
# Continue with next download
|
|
||||||
self.download_next_track()
|
self.download_next_track()
|
||||||
|
|
||||||
def on_track_download_failed(self, download_index, track_index, error_message):
|
def on_track_download_failed(self, download_index, track_index, error_message):
|
||||||
|
|
@ -4340,12 +4338,16 @@ class DownloadMissingTracksModal(QDialog):
|
||||||
failed_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
failed_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.track_table.setItem(track_index, 4, failed_item)
|
self.track_table.setItem(track_index, 4, failed_item)
|
||||||
|
|
||||||
# Update progress and continue
|
# Update failure tracking
|
||||||
self.current_download += 1
|
self.failed_downloads += 1
|
||||||
self.download_progress.setValue(self.current_download)
|
self.completed_downloads += 1 # Count as completed (failed = finished)
|
||||||
|
|
||||||
# Continue with next download
|
# Update progress based on completed downloads (success + failures)
|
||||||
self.download_next_track()
|
self.download_progress.setValue(self.completed_downloads)
|
||||||
|
|
||||||
|
# Check if all downloads are complete
|
||||||
|
if self.completed_downloads >= len(self.missing_tracks):
|
||||||
|
self.on_all_downloads_complete()
|
||||||
|
|
||||||
def on_all_downloads_complete(self):
|
def on_all_downloads_complete(self):
|
||||||
"""Handle completion of all downloads"""
|
"""Handle completion of all downloads"""
|
||||||
|
|
@ -4382,6 +4384,152 @@ class DownloadMissingTracksModal(QDialog):
|
||||||
QMessageBox.information(self, "Downloads Complete",
|
QMessageBox.information(self, "Downloads Complete",
|
||||||
f"Completed downloading {completed_count}/{len(self.missing_tracks)} missing tracks!")
|
f"Completed downloading {completed_count}/{len(self.missing_tracks)} missing tracks!")
|
||||||
|
|
||||||
|
def start_download_monitoring(self, download_id, download_index, track_index):
|
||||||
|
"""Start monitoring a download for completion using downloads.py approach"""
|
||||||
|
if not hasattr(self, 'download_timers'):
|
||||||
|
self.download_timers = []
|
||||||
|
|
||||||
|
# Create a timer to check this specific download
|
||||||
|
timer = QTimer()
|
||||||
|
timer.timeout.connect(lambda: self.check_download_status(download_id, download_index, track_index, timer))
|
||||||
|
timer.start(2000) # Check every 2 seconds
|
||||||
|
self.download_timers.append(timer)
|
||||||
|
|
||||||
|
print(f"🕐 Started monitoring download: {download_id}")
|
||||||
|
|
||||||
|
def check_download_status(self, download_id, download_index, track_index, timer):
|
||||||
|
"""Check if a specific download has completed"""
|
||||||
|
# Check for cancellation
|
||||||
|
if hasattr(self, 'cancel_requested') and self.cancel_requested:
|
||||||
|
timer.stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create worker to check download status (like downloads.py does)
|
||||||
|
from PyQt6.QtCore import QRunnable, QObject, pyqtSignal
|
||||||
|
|
||||||
|
class DownloadStatusWorkerSignals(QObject):
|
||||||
|
status_checked = pyqtSignal(str, int, int, QTimer) # status, download_index, track_index, timer
|
||||||
|
check_failed = pyqtSignal(int, int, str, QTimer) # download_index, track_index, error, timer
|
||||||
|
|
||||||
|
class DownloadStatusWorker(QRunnable):
|
||||||
|
def __init__(self, soulseek_client, download_id):
|
||||||
|
super().__init__()
|
||||||
|
self.soulseek_client = soulseek_client
|
||||||
|
self.download_id = download_id
|
||||||
|
self.signals = DownloadStatusWorkerSignals()
|
||||||
|
self._stop_requested = False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self._stop_requested:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
import asyncio
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
# Get all downloads and find our specific one
|
||||||
|
downloads = loop.run_until_complete(self.soulseek_client.get_all_downloads())
|
||||||
|
|
||||||
|
if downloads and 'transfers' in downloads:
|
||||||
|
for user_data in downloads['transfers']:
|
||||||
|
if 'directories' in user_data:
|
||||||
|
for directory in user_data['directories']:
|
||||||
|
if 'files' in directory:
|
||||||
|
for file_transfer in directory['files']:
|
||||||
|
if file_transfer.get('id') == self.download_id:
|
||||||
|
state = file_transfer.get('state', 'Unknown')
|
||||||
|
self.signals.status_checked.emit(state, download_index, track_index, timer)
|
||||||
|
loop.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we get here, download wasn't found - might be completed and cleaned up
|
||||||
|
self.signals.check_failed.emit(download_index, track_index, "Download not found in active transfers", timer)
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.signals.check_failed.emit(download_index, track_index, str(e), timer)
|
||||||
|
if 'loop' in locals():
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
# Create and start worker
|
||||||
|
worker = DownloadStatusWorker(self.parent_page.soulseek_client, download_id)
|
||||||
|
worker.signals.status_checked.connect(self.on_download_status_checked)
|
||||||
|
worker.signals.check_failed.connect(self.on_download_status_check_failed)
|
||||||
|
|
||||||
|
# CRITICAL: Track worker for cancellation
|
||||||
|
if not hasattr(self, 'active_workers'):
|
||||||
|
self.active_workers = []
|
||||||
|
self.active_workers.append(worker)
|
||||||
|
|
||||||
|
# Submit to thread pool
|
||||||
|
if hasattr(self.parent_page, 'thread_pool'):
|
||||||
|
self.parent_page.thread_pool.start(worker)
|
||||||
|
else:
|
||||||
|
thread_pool = QThreadPool()
|
||||||
|
self.fallback_pools.append(thread_pool)
|
||||||
|
thread_pool.start(worker)
|
||||||
|
|
||||||
|
def on_download_status_checked(self, state, download_index, track_index, timer):
|
||||||
|
"""Handle download status check result"""
|
||||||
|
if state == "Completed":
|
||||||
|
timer.stop()
|
||||||
|
self.on_actual_track_download_complete(download_index, track_index)
|
||||||
|
elif state in ["Cancelled", "Failed"]:
|
||||||
|
timer.stop()
|
||||||
|
self.on_track_download_failed(download_index, track_index, f"Download {state.lower()}")
|
||||||
|
# For "InProgress", "Queued", etc. - keep monitoring
|
||||||
|
|
||||||
|
def on_download_status_check_failed(self, download_index, track_index, error, timer):
|
||||||
|
"""Handle download status check failure"""
|
||||||
|
# If download not found, it might have completed and been cleaned up
|
||||||
|
# Try one more check in 5 seconds, then assume completed
|
||||||
|
print(f"⚠️ Status check failed for download {download_index + 1}: {error}")
|
||||||
|
|
||||||
|
# For now, assume it completed (downloads.py removes completed downloads)
|
||||||
|
timer.stop()
|
||||||
|
self.on_actual_track_download_complete(download_index, track_index)
|
||||||
|
|
||||||
|
def on_actual_track_download_complete(self, download_index, track_index):
|
||||||
|
"""Handle when a download is actually completed (not just started)"""
|
||||||
|
print(f"✅ Download {download_index + 1} actually completed!")
|
||||||
|
|
||||||
|
# Update table to show Downloaded
|
||||||
|
if track_index is not None:
|
||||||
|
downloaded_item = QTableWidgetItem("✅ Downloaded")
|
||||||
|
downloaded_item.setFlags(downloaded_item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
||||||
|
downloaded_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.track_table.setItem(track_index, 4, downloaded_item)
|
||||||
|
|
||||||
|
# Update completion tracking
|
||||||
|
self.successful_downloads += 1
|
||||||
|
self.completed_downloads += 1
|
||||||
|
|
||||||
|
# Update main console log
|
||||||
|
if hasattr(self.parent_page, 'log_area') and download_index < len(self.missing_tracks):
|
||||||
|
track = self.missing_tracks[download_index].spotify_track
|
||||||
|
track_name = track.name
|
||||||
|
artist_name = track.artists[0] if track.artists else "Unknown Artist"
|
||||||
|
remaining = len(self.missing_tracks) - self.successful_downloads
|
||||||
|
self.parent_page.log_area.append(f"✅ Downloaded: {track_name} by {artist_name} ({remaining} remaining)")
|
||||||
|
|
||||||
|
# Update progress bar to reflect actual completions
|
||||||
|
self.download_progress.setValue(self.completed_downloads)
|
||||||
|
|
||||||
|
# Update playlist status indicator
|
||||||
|
playlist_item = self.find_playlist_item()
|
||||||
|
if playlist_item:
|
||||||
|
total = len(self.missing_tracks) if hasattr(self, 'missing_tracks') else 0
|
||||||
|
if self.completed_downloads >= total:
|
||||||
|
self.on_all_downloads_complete()
|
||||||
|
else:
|
||||||
|
status_text = f"⏬ Downloading {self.completed_downloads}/{total}"
|
||||||
|
playlist_item.update_operation_status(status_text)
|
||||||
|
|
||||||
|
# Check if all downloads are complete
|
||||||
|
if self.completed_downloads >= len(self.missing_tracks):
|
||||||
|
self.on_all_downloads_complete()
|
||||||
|
|
||||||
def setup_background_status_updates(self):
|
def setup_background_status_updates(self):
|
||||||
"""Set up timer-based background status updates for playlist indicator"""
|
"""Set up timer-based background status updates for playlist indicator"""
|
||||||
from PyQt6.QtCore import QTimer
|
from PyQt6.QtCore import QTimer
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue