KaraokeVideoDownloader/karaoke_downloader/youtube_utils.py

116 lines
3.7 KiB
Python

"""
YouTube utilities for channel info, playlist info, and yt-dlp command generation.
"""
import subprocess
import json
from pathlib import Path
from typing import List, Dict, Any, Optional
def get_channel_info(channel_url: str, yt_dlp_path: str = "downloader/yt-dlp.exe") -> Dict[str, Any]:
"""Get channel information using yt-dlp."""
try:
cmd = [
yt_dlp_path,
"--dump-json",
"--no-playlist",
channel_url
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
print(f"❌ Failed to get channel info: {e}")
return {}
def get_playlist_info(playlist_url: str, yt_dlp_path: str = "downloader/yt-dlp.exe") -> List[Dict[str, Any]]:
"""Get playlist information using yt-dlp."""
try:
cmd = [
yt_dlp_path,
"--dump-json",
"--flat-playlist",
playlist_url
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
videos = []
for line in result.stdout.strip().split('\n'):
if line.strip():
videos.append(json.loads(line))
return videos
except subprocess.CalledProcessError as e:
print(f"❌ Failed to get playlist info: {e}")
return []
def build_yt_dlp_command(
yt_dlp_path: str,
video_url: str,
output_path: Path,
config: Dict[str, Any],
additional_args: Optional[List[str]] = None
) -> List[str]:
"""
Build a standardized yt-dlp command with consistent arguments.
Args:
yt_dlp_path: Path to yt-dlp executable
video_url: YouTube video URL
output_path: Output file path
config: Configuration dictionary with download settings
additional_args: Optional additional arguments to append
Returns:
List of command arguments for subprocess.run
"""
cmd = [
str(yt_dlp_path),
"--no-check-certificates",
"--ignore-errors",
"--no-warnings",
"-o", str(output_path),
"-f", config.get("download_settings", {}).get("format", "best[height<=720][ext=mp4]/best[height<=720]/best[ext=mp4]/best"),
video_url
]
# Add any additional arguments
if additional_args:
cmd.extend(additional_args)
return cmd
def execute_yt_dlp_command(cmd: List[str], timeout: Optional[int] = None) -> subprocess.CompletedProcess:
"""
Execute a yt-dlp command with standardized error handling.
Args:
cmd: Command list to execute
timeout: Optional timeout in seconds
Returns:
CompletedProcess object
Raises:
subprocess.CalledProcessError: If the command fails
subprocess.TimeoutExpired: If the command times out
"""
return subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=timeout)
def show_available_formats(video_url: str, yt_dlp_path: str = "downloader/yt-dlp.exe", timeout: int = 30) -> None:
"""
Show available formats for a video (debugging utility).
Args:
video_url: YouTube video URL
yt_dlp_path: Path to yt-dlp executable
timeout: Timeout in seconds
"""
print(f"🔍 Checking available formats for: {video_url}")
format_cmd = [
str(yt_dlp_path),
"--list-formats",
video_url
]
try:
format_result = subprocess.run(format_cmd, capture_output=True, text=True, timeout=timeout)
print(f"📋 Available formats:\n{format_result.stdout}")
except Exception as e:
print(f"⚠️ Could not check formats: {e}")