Refactor yt-dlp Integration and Error Handling
This commit is contained in:
+53
-64
@@ -1,5 +1,10 @@
|
|||||||
from PySide6.QtCore import QThread, Signal, QObject, QProcess, QTimer
|
from PySide6.QtCore import QThread, Signal, QObject, QProcess, QTimer
|
||||||
import yt_dlp # Keep yt_dlp import here - only downloader uses it.
|
try:
|
||||||
|
import yt_dlp # Keep yt_dlp import here - only downloader uses it.
|
||||||
|
YT_DLP_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
YT_DLP_AVAILABLE = False
|
||||||
|
print("Warning: yt-dlp not available at startup, will be downloaded at runtime")
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -7,6 +12,7 @@ import subprocess # For direct CLI command execution
|
|||||||
import shlex # For safely parsing command arguments
|
import shlex # For safely parsing command arguments
|
||||||
import sys # Added to get executable path information
|
import sys # Added to get executable path information
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from ytsage_yt_dlp import get_yt_dlp_path # Import the new yt-dlp path function
|
||||||
|
|
||||||
class SignalManager(QObject):
|
class SignalManager(QObject):
|
||||||
update_formats = Signal(list)
|
update_formats = Signal(list)
|
||||||
@@ -148,13 +154,14 @@ class DownloadThread(QThread):
|
|||||||
if self.cookie_file:
|
if self.cookie_file:
|
||||||
ydl_opts_check['cookiefile'] = self.cookie_file
|
ydl_opts_check['cookiefile'] = self.cookie_file
|
||||||
|
|
||||||
with yt_dlp.YoutubeDL(ydl_opts_check) as ydl:
|
if YT_DLP_AVAILABLE:
|
||||||
info = ydl.extract_info(self.url, download=False)
|
with yt_dlp.YoutubeDL(ydl_opts_check) as ydl:
|
||||||
|
info = ydl.extract_info(self.url, download=False)
|
||||||
|
|
||||||
# Handle cases where info extraction fails silently
|
# Handle cases where info extraction fails silently
|
||||||
if not info:
|
if not info:
|
||||||
print("DEBUG: Failed to extract info during file existence check. Skipping check.")
|
print("DEBUG: Failed to extract info during file existence check. Skipping check.")
|
||||||
return False # Proceed with download attempt
|
return False # Proceed with download attempt
|
||||||
|
|
||||||
# Get the title and sanitize it for filename
|
# Get the title and sanitize it for filename
|
||||||
title = info.get('title', 'video')
|
title = info.get('title', 'video')
|
||||||
@@ -169,6 +176,9 @@ class DownloadThread(QThread):
|
|||||||
break
|
break
|
||||||
|
|
||||||
print(f"DEBUG: Resolution: {resolution}")
|
print(f"DEBUG: Resolution: {resolution}")
|
||||||
|
else:
|
||||||
|
print("DEBUG: yt-dlp not available, skipping file existence check")
|
||||||
|
return False # Proceed with download attempt
|
||||||
|
|
||||||
# Create the expected filename (more specific)
|
# Create the expected filename (more specific)
|
||||||
if self.is_playlist and info.get('playlist_title'):
|
if self.is_playlist and info.get('playlist_title'):
|
||||||
@@ -210,33 +220,10 @@ class DownloadThread(QThread):
|
|||||||
|
|
||||||
def _build_yt_dlp_command(self):
|
def _build_yt_dlp_command(self):
|
||||||
"""Build the yt-dlp command line with all options for direct execution."""
|
"""Build the yt-dlp command line with all options for direct execution."""
|
||||||
# Use bundled yt-dlp executable instead of system PATH
|
# Use the new yt-dlp path function from ytsage_yt_dlp module
|
||||||
# Determine if we're running from a PyInstaller bundle
|
yt_dlp_path = get_yt_dlp_path()
|
||||||
if getattr(sys, 'frozen', False):
|
cmd = [yt_dlp_path]
|
||||||
# We're running from a PyInstaller bundle
|
print(f"DEBUG: Using yt-dlp from: {yt_dlp_path}")
|
||||||
base_path = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
|
|
||||||
yt_dlp_path = os.path.join(base_path, "yt-dlp.exe" if os.name == 'nt' else "yt-dlp")
|
|
||||||
if os.path.exists(yt_dlp_path):
|
|
||||||
cmd = [yt_dlp_path]
|
|
||||||
print(f"DEBUG: Using bundled yt-dlp from: {yt_dlp_path}")
|
|
||||||
|
|
||||||
# Check if the bundled executable is actually executable
|
|
||||||
if not os.access(yt_dlp_path, os.X_OK) and os.name != 'nt': # Skip check on Windows
|
|
||||||
print(f"WARNING: Bundled yt-dlp exists but is not executable: {yt_dlp_path}")
|
|
||||||
try:
|
|
||||||
# Try to make it executable
|
|
||||||
os.chmod(yt_dlp_path, 0o755)
|
|
||||||
print(f"Fixed permissions on bundled yt-dlp")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Failed to fix permissions: {e}")
|
|
||||||
else:
|
|
||||||
# Fall back to regular command if bundled version not found
|
|
||||||
cmd = ["yt-dlp"]
|
|
||||||
print(f"DEBUG: Bundled yt-dlp not found at {yt_dlp_path}, using system PATH")
|
|
||||||
else:
|
|
||||||
# We're running from source, use system PATH
|
|
||||||
cmd = ["yt-dlp"]
|
|
||||||
print("DEBUG: Using system PATH for yt-dlp")
|
|
||||||
|
|
||||||
# Format selection strategy - use format ID if provided or fallback to resolution
|
# Format selection strategy - use format ID if provided or fallback to resolution
|
||||||
if self.format_id:
|
if self.format_id:
|
||||||
@@ -246,19 +233,20 @@ class DownloadThread(QThread):
|
|||||||
# Check if this is an audio-only format
|
# Check if this is an audio-only format
|
||||||
is_audio_format = False
|
is_audio_format = False
|
||||||
try:
|
try:
|
||||||
ydl_opts = {
|
if YT_DLP_AVAILABLE:
|
||||||
'quiet': True,
|
ydl_opts = {
|
||||||
'no_warnings': True,
|
'quiet': True,
|
||||||
'skip_download': True,
|
'no_warnings': True,
|
||||||
}
|
'skip_download': True,
|
||||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
}
|
||||||
info = ydl.extract_info(self.url, download=False)
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||||
for fmt in info.get('formats', []):
|
info = ydl.extract_info(self.url, download=False)
|
||||||
if fmt.get('format_id') == clean_format_id:
|
for fmt in info.get('formats', []):
|
||||||
if fmt.get('vcodec') == 'none' or 'audio only' in fmt.get('format_note', '').lower():
|
if fmt.get('format_id') == clean_format_id:
|
||||||
is_audio_format = True
|
if fmt.get('vcodec') == 'none' or 'audio only' in fmt.get('format_note', '').lower():
|
||||||
print(f"DEBUG: Detected audio-only format for ID: {clean_format_id}")
|
is_audio_format = True
|
||||||
break
|
print(f"DEBUG: Detected audio-only format for ID: {clean_format_id}")
|
||||||
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"DEBUG: Error checking if format is audio-only: {e}")
|
print(f"DEBUG: Error checking if format is audio-only: {e}")
|
||||||
|
|
||||||
@@ -275,24 +263,25 @@ class DownloadThread(QThread):
|
|||||||
try:
|
try:
|
||||||
format_ext = None
|
format_ext = None
|
||||||
print(f"DEBUG: Getting format information for format ID: {self.format_id} (using: {clean_format_id})")
|
print(f"DEBUG: Getting format information for format ID: {self.format_id} (using: {clean_format_id})")
|
||||||
ydl_opts = {
|
if YT_DLP_AVAILABLE:
|
||||||
'quiet': True,
|
ydl_opts = {
|
||||||
'no_warnings': True,
|
'quiet': True,
|
||||||
'skip_download': True,
|
'no_warnings': True,
|
||||||
}
|
'skip_download': True,
|
||||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
}
|
||||||
info = ydl.extract_info(self.url, download=False)
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||||
# Look for the clean format ID first
|
info = ydl.extract_info(self.url, download=False)
|
||||||
for fmt in info.get('formats', []):
|
# Look for the clean format ID first
|
||||||
if fmt.get('format_id') == clean_format_id:
|
|
||||||
format_ext = fmt.get('ext')
|
|
||||||
break
|
|
||||||
# If not found, try the original ID as fallback
|
|
||||||
if not format_ext:
|
|
||||||
for fmt in info.get('formats', []):
|
for fmt in info.get('formats', []):
|
||||||
if fmt.get('format_id') == self.format_id:
|
if fmt.get('format_id') == clean_format_id:
|
||||||
format_ext = fmt.get('ext')
|
format_ext = fmt.get('ext')
|
||||||
break
|
break
|
||||||
|
# If not found, try the original ID as fallback
|
||||||
|
if not format_ext:
|
||||||
|
for fmt in info.get('formats', []):
|
||||||
|
if fmt.get('format_id') == self.format_id:
|
||||||
|
format_ext = fmt.get('ext')
|
||||||
|
break
|
||||||
|
|
||||||
if format_ext:
|
if format_ext:
|
||||||
print(f"DEBUG: Detected format extension: {format_ext}")
|
print(f"DEBUG: Detected format extension: {format_ext}")
|
||||||
@@ -324,7 +313,7 @@ class DownloadThread(QThread):
|
|||||||
if self.is_playlist and self.playlist_items:
|
if self.is_playlist and self.playlist_items:
|
||||||
cmd.extend(["--playlist-items", self.playlist_items])
|
cmd.extend(["--playlist-items", self.playlist_items])
|
||||||
|
|
||||||
# Add subtitle options if selected
|
# Add subtitle options if subtitles are selected
|
||||||
if self.subtitle_langs:
|
if self.subtitle_langs:
|
||||||
# Subtitles work with both audio-only and video formats
|
# Subtitles work with both audio-only and video formats
|
||||||
# For audio-only formats, subtitles will be downloaded as separate files
|
# For audio-only formats, subtitles will be downloaded as separate files
|
||||||
@@ -344,7 +333,7 @@ class DownloadThread(QThread):
|
|||||||
cmd.extend(["--sub-langs", ",".join(lang_codes)])
|
cmd.extend(["--sub-langs", ",".join(lang_codes)])
|
||||||
cmd.append("--write-auto-subs") # Include auto-generated subtitles
|
cmd.append("--write-auto-subs") # Include auto-generated subtitles
|
||||||
|
|
||||||
# Add embedding if requested - only applies to video formats
|
# Only embed subtitles if merge is enabled
|
||||||
if self.merge_subs:
|
if self.merge_subs:
|
||||||
cmd.append("--embed-subs")
|
cmd.append("--embed-subs")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user