G: Wire YouTube progress hook to engine + drop dead threading imports
YouTube's _progress_hook still wrote to the per-client active_downloads dict + _download_lock that Phase C2 deleted — runtime crash waiting to happen. Rewritten to use engine.update_record. Same state-dict shape, same UI semantics (95% during ffmpeg postprocess, 'Errored' on yt-dlp error, 'InProgress, Downloading' during stream). Drop unused `import threading` from youtube/tidal/soundcloud clients (no longer spawn threads — engine.worker owns that). Qobuz/HiFi/Deezer keep their threading import for module-level or per-instance API locks (separate from download threading). Suite still green (2050 passed).
This commit is contained in:
parent
4b4076b57f
commit
3a70f0453c
3 changed files with 26 additions and 39 deletions
|
|
@ -27,7 +27,6 @@ import re
|
|||
import asyncio
|
||||
import uuid
|
||||
import time
|
||||
import threading
|
||||
from typing import List, Optional, Dict, Any, Tuple, Callable
|
||||
from pathlib import Path
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import re
|
|||
import asyncio
|
||||
import uuid
|
||||
import time
|
||||
import threading
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import List, Optional, Dict, Any, Tuple
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import time
|
|||
import platform
|
||||
import asyncio
|
||||
import uuid
|
||||
import threading
|
||||
from typing import List, Optional, Dict, Any, Tuple
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
|
@ -296,15 +295,14 @@ class YouTubeClient:
|
|||
self.progress_callback = callback
|
||||
|
||||
def _progress_hook(self, d):
|
||||
"""
|
||||
yt-dlp progress hook - called during download to report progress.
|
||||
Updates the active_downloads dictionary for the current download.
|
||||
Mirrors Soulseek's transfer status updates.
|
||||
"""
|
||||
"""yt-dlp progress hook — called during download to report
|
||||
progress. Writes to the engine record (Phase C2 lifted state
|
||||
out of the per-client dict; this hook follows suit)."""
|
||||
try:
|
||||
# Only update if we have a current download ID
|
||||
if not self.current_download_id:
|
||||
return
|
||||
if self._engine is None:
|
||||
return
|
||||
|
||||
status = d.get('status', 'unknown')
|
||||
|
||||
|
|
@ -313,24 +311,18 @@ class YouTubeClient:
|
|||
total = d.get('total_bytes') or d.get('total_bytes_estimate', 0)
|
||||
speed = d.get('speed', 0) or 0
|
||||
eta = d.get('eta', 0) or 0
|
||||
percent = (downloaded / total) * 100 if total > 0 else 0
|
||||
|
||||
if total > 0:
|
||||
percent = (downloaded / total) * 100
|
||||
else:
|
||||
percent = 0
|
||||
self._engine.update_record('youtube', self.current_download_id, {
|
||||
'state': 'InProgress, Downloading',
|
||||
'progress': round(percent, 1),
|
||||
'transferred': downloaded,
|
||||
'size': total,
|
||||
'speed': int(speed),
|
||||
'time_remaining': int(eta) if eta > 0 else None,
|
||||
})
|
||||
|
||||
# Update active downloads dictionary (thread-safe update with lock)
|
||||
with self._download_lock:
|
||||
if self.current_download_id in self.active_downloads:
|
||||
download_info = self.active_downloads[self.current_download_id]
|
||||
download_info['state'] = 'InProgress, Downloading' # Match Soulseek state format
|
||||
download_info['progress'] = round(percent, 1)
|
||||
download_info['transferred'] = downloaded
|
||||
download_info['size'] = total
|
||||
download_info['speed'] = int(speed)
|
||||
download_info['time_remaining'] = int(eta) if eta > 0 else None
|
||||
|
||||
# Also update current_download_progress for legacy compatibility
|
||||
# Legacy progress dict for any external listeners.
|
||||
self.current_download_progress = {
|
||||
'status': 'downloading',
|
||||
'percent': round(percent, 1),
|
||||
|
|
@ -340,30 +332,27 @@ class YouTubeClient:
|
|||
'eta': int(eta),
|
||||
'filename': d.get('filename', '')
|
||||
}
|
||||
|
||||
# Call progress callback if set (for UI updates)
|
||||
if self.progress_callback:
|
||||
self.progress_callback(self.current_download_progress)
|
||||
|
||||
elif status == 'finished':
|
||||
# Download finished, ffmpeg is converting to MP3
|
||||
# Keep state as 'InProgress, Downloading' - the download thread will set final state
|
||||
with self._download_lock:
|
||||
if self.current_download_id in self.active_downloads:
|
||||
self.active_downloads[self.current_download_id]['progress'] = 95.0 # Almost done (converting)
|
||||
|
||||
# Download finished — ffmpeg now converts to MP3. The
|
||||
# engine.worker thread flips to 'Completed, Succeeded'
|
||||
# once _download_sync returns; this just bumps progress
|
||||
# to 95% so the UI doesn't sit at 99.9% during the
|
||||
# ffmpeg post-process.
|
||||
self._engine.update_record('youtube', self.current_download_id, {
|
||||
'progress': 95.0,
|
||||
})
|
||||
self.current_download_progress['status'] = 'postprocessing'
|
||||
self.current_download_progress['percent'] = 95.0
|
||||
|
||||
if self.progress_callback:
|
||||
self.progress_callback(self.current_download_progress)
|
||||
|
||||
elif status == 'error':
|
||||
# Mark as error (thread-safe)
|
||||
with self._download_lock:
|
||||
if self.current_download_id in self.active_downloads:
|
||||
self.active_downloads[self.current_download_id]['state'] = 'Errored'
|
||||
|
||||
self._engine.update_record('youtube', self.current_download_id, {
|
||||
'state': 'Errored',
|
||||
})
|
||||
self.current_download_progress['status'] = 'error'
|
||||
if self.progress_callback:
|
||||
self.progress_callback(self.current_download_progress)
|
||||
|
|
|
|||
Loading…
Reference in a new issue