Fix: Use yt_dlp.utils.DownloadCancelled for proper download cancellation
- Import and use DownloadCancelled exception which YT-DLP properly handles - Add InterruptedError handling throughout the call chain - Fire 'cancelled' status event when download is cancelled - Handle InterruptedError in DownloadService to set CANCELLED status
This commit is contained in:
@@ -4,6 +4,8 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote
|
||||
@@ -14,6 +16,7 @@ from fake_useragent import UserAgent
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
from yt_dlp import YoutubeDL
|
||||
from yt_dlp.utils import DownloadCancelled
|
||||
|
||||
from ..interfaces.providers import Providers
|
||||
from .base_provider import Loader
|
||||
@@ -308,14 +311,19 @@ class AniworldLoader(Loader):
|
||||
)
|
||||
logging.debug("Direct link obtained from provider")
|
||||
|
||||
# Create a cancellation-aware progress hook
|
||||
# Create a cancellation-aware progress hook using DownloadCancelled
|
||||
# which YT-DLP properly handles
|
||||
cancel_flag = self._cancel_flag
|
||||
|
||||
def cancellation_check_hook(d):
|
||||
"""Progress hook that checks for cancellation."""
|
||||
"""Progress hook that checks for cancellation.
|
||||
|
||||
Uses yt_dlp.utils.DownloadCancelled which is properly
|
||||
handled by YT-DLP to abort downloads immediately.
|
||||
"""
|
||||
if cancel_flag.is_set():
|
||||
logging.info("Cancellation detected in progress hook")
|
||||
raise InterruptedError("Download cancelled")
|
||||
raise DownloadCancelled("Download cancelled by user")
|
||||
|
||||
ydl_opts = {
|
||||
'fragment_retries': float('inf'),
|
||||
@@ -334,10 +342,10 @@ class AniworldLoader(Loader):
|
||||
if progress_callback:
|
||||
# Wrap the callback to add logging and keep cancellation check
|
||||
def logged_progress_callback(d):
|
||||
# Check cancellation first
|
||||
# Check cancellation first - use DownloadCancelled
|
||||
if cancel_flag.is_set():
|
||||
logging.info("Cancellation detected in progress callback")
|
||||
raise InterruptedError("Download cancelled")
|
||||
raise DownloadCancelled("Download cancelled by user")
|
||||
logging.debug(
|
||||
f"YT-DLP progress: status={d.get('status')}, "
|
||||
f"downloaded={d.get('downloaded_bytes')}, "
|
||||
@@ -385,16 +393,19 @@ class AniworldLoader(Loader):
|
||||
)
|
||||
self.clear_cache()
|
||||
return False
|
||||
except InterruptedError:
|
||||
except (InterruptedError, DownloadCancelled) as e:
|
||||
# Re-raise cancellation errors
|
||||
logging.info("Download interrupted, propagating cancellation")
|
||||
logging.info(
|
||||
"Download cancelled: %s, propagating cancellation",
|
||||
type(e).__name__
|
||||
)
|
||||
# Clean up temp file if exists
|
||||
if os.path.exists(temp_path):
|
||||
try:
|
||||
os.remove(temp_path)
|
||||
except OSError:
|
||||
pass
|
||||
raise
|
||||
raise InterruptedError("Download cancelled") from e
|
||||
except BrokenPipeError as e:
|
||||
logging.error(
|
||||
f"Broken pipe error with provider {provider}: {e}. "
|
||||
@@ -403,6 +414,15 @@ class AniworldLoader(Loader):
|
||||
# Try next provider if available
|
||||
continue
|
||||
except Exception as e:
|
||||
# Check if this is a cancellation wrapped in another exception
|
||||
if self.is_cancelled():
|
||||
logging.info("Download cancelled (detected in exception handler)")
|
||||
if os.path.exists(temp_path):
|
||||
try:
|
||||
os.remove(temp_path)
|
||||
except OSError:
|
||||
pass
|
||||
raise InterruptedError("Download cancelled") from e
|
||||
logging.error(
|
||||
f"YoutubeDL download failed with provider {provider}: "
|
||||
f"{type(e).__name__}: {e}"
|
||||
|
||||
Reference in New Issue
Block a user