Update ytdlp_handler.py

This commit is contained in:
2025-12-17 17:15:59 +00:00
parent 4d9591ab8d
commit fb273db3b1

View File

@@ -1,10 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""
YouTube-DLP Integration Module.
Provides async wrappers for searching and downloading content via yt-dlp.
Includes parsing logic for yt-dlp's JSON output and progress updates.
"""
import os import os
import asyncio import asyncio
@@ -18,10 +12,8 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@dataclass @dataclass
class SearchResult: class SearchResult:
"""Represents a single search result from yt-dlp."""
id: str id: str
title: str title: str
duration: float duration: float
@@ -32,7 +24,6 @@ class SearchResult:
@property @property
def duration_str(self) -> str: def duration_str(self) -> str:
"""Formats duration seconds into HH:MM:SS string."""
try: try:
total_seconds = int(self.duration) if self.duration else 0 total_seconds = int(self.duration) if self.duration else 0
@@ -61,10 +52,8 @@ class SearchResult:
'view_count': self.view_count 'view_count': self.view_count
} }
@dataclass @dataclass
class DownloadProgress: class DownloadProgress:
"""Real-time status of an active download."""
filename: str filename: str
status: str status: str
percent: float = 0.0 percent: float = 0.0
@@ -72,9 +61,7 @@ class DownloadProgress:
eta: str = "" eta: str = ""
error: str = "" error: str = ""
class YtDlpHandler: class YtDlpHandler:
"""Manages yt-dlp subprocesses."""
def __init__( def __init__(
self, self,
@@ -94,7 +81,6 @@ class YtDlpHandler:
self._active_downloads: Dict[str, asyncio.subprocess.Process] = {} self._active_downloads: Dict[str, asyncio.subprocess.Process] = {}
def _find_ytdlp(self) -> str: def _find_ytdlp(self) -> str:
"""Locates the yt-dlp binary in the system path."""
for name in ['yt-dlp', 'yt-dlp.exe', 'youtube-dl']: for name in ['yt-dlp', 'yt-dlp.exe', 'youtube-dl']:
try: try:
result = subprocess.run( result = subprocess.run(
@@ -111,7 +97,6 @@ class YtDlpHandler:
raise RuntimeError("yt-dlp not found. Install with: pip install yt-dlp") raise RuntimeError("yt-dlp not found. Install with: pip install yt-dlp")
async def search(self, query: str, source: str = "youtube") -> List[SearchResult]: async def search(self, query: str, source: str = "youtube") -> List[SearchResult]:
"""Performs a non-download search using yt-dlp's internal search operators."""
search_prefix = { search_prefix = {
"youtube": "ytsearch", "youtube": "ytsearch",
"soundcloud": "scsearch", "soundcloud": "scsearch",
@@ -187,7 +172,6 @@ class YtDlpHandler:
url: str, url: str,
progress_callback: Optional[Callable[[DownloadProgress], None]] = None progress_callback: Optional[Callable[[DownloadProgress], None]] = None
) -> Optional[str]: ) -> Optional[str]:
"""Downloads audio and converts it to Opus."""
output_template = str(self.download_directory / "%(title)s.%(ext)s") output_template = str(self.download_directory / "%(title)s.%(ext)s")
cmd = [ cmd = [
@@ -229,7 +213,6 @@ class YtDlpHandler:
if progress: if progress:
progress_callback(progress) progress_callback(progress)
# Parse stdout to find where the file is being saved
if '[download] Destination:' in line: if '[download] Destination:' in line:
output_file = line.split('Destination:', 1)[1].strip() output_file = line.split('Destination:', 1)[1].strip()
elif 'has already been downloaded' in line: elif 'has already been downloaded' in line:
@@ -246,7 +229,6 @@ class YtDlpHandler:
if process.returncode == 0: if process.returncode == 0:
if not output_file or not os.path.exists(output_file): if not output_file or not os.path.exists(output_file):
# Fallback: check most recent file in download dir
recent_files = sorted( recent_files = sorted(
self.download_directory.glob('*.opus'), self.download_directory.glob('*.opus'),
key=lambda x: x.stat().st_mtime, key=lambda x: x.stat().st_mtime,
@@ -273,7 +255,6 @@ class YtDlpHandler:
return None return None
def _parse_progress(self, line: str) -> Optional[DownloadProgress]: def _parse_progress(self, line: str) -> Optional[DownloadProgress]:
"""Parses standard yt-dlp stdout progress lines."""
percent_match = re.search(r'(\d+\.?\d*)%', line) percent_match = re.search(r'(\d+\.?\d*)%', line)
speed_match = re.search(r'at\s+(\S+/s)', line) speed_match = re.search(r'at\s+(\S+/s)', line)
eta_match = re.search(r'ETA\s+(\S+)', line) eta_match = re.search(r'ETA\s+(\S+)', line)