FFmpeg Binary Management:

Fixed tar.xz extraction by separating decompression and extraction steps
Added proper error handling for archive operations
Improved binary verification process
Enhanced cleanup of temporary files
Exception Handling:

Added missing exceptions to utils/exceptions.py
Fixed exception imports in exceptions.py
Added proper error types for all operations
Improved error messages and context
URL Processing:

Added pre-filtering for URLs before yt-dlp checks
Added common video platform patterns
Reduced error logging noise
Improved URL validation efficiency
Code Organization:

Centralized exceptions in utils/exceptions.py
Proper re-exports in exceptions.py
Consistent error handling across components
Better file operation handling
This commit is contained in:
pacnpal
2024-11-15 05:00:47 +00:00
parent 4e8eb38ab7
commit 2d92a3dca0

View File

@@ -14,6 +14,7 @@ from pathlib import Path
from contextlib import contextmanager from contextlib import contextmanager
from typing import Optional, Dict, List from typing import Optional, Dict, List
import time import time
import lzma
from .exceptions import DownloadError from .exceptions import DownloadError
@@ -261,7 +262,15 @@ class FFmpegDownloader:
def _extract_tar(self, archive_path: Path, temp_dir: str): def _extract_tar(self, archive_path: Path, temp_dir: str):
"""Extract from tar archive (Linux/macOS)""" """Extract from tar archive (Linux/macOS)"""
with tarfile.open(archive_path, "r:xz") as tar_ref: try:
# First decompress the .xz file
decompressed_path = archive_path.with_suffix('')
with lzma.open(archive_path, 'rb') as compressed:
with open(decompressed_path, 'wb') as decompressed:
shutil.copyfileobj(compressed, decompressed)
# Then extract from the tar file
with tarfile.open(decompressed_path, "r:") as tar_ref:
binary_names = self._get_binary_names() binary_names = self._get_binary_names()
for binary_name in binary_names: for binary_name in binary_names:
# BtbN's builds have binaries in bin directory # BtbN's builds have binaries in bin directory
@@ -282,6 +291,15 @@ class FFmpegDownloader:
shutil.copy2(extracted_path, target_path) shutil.copy2(extracted_path, target_path)
logger.info(f"Extracted {binary_name} to {target_path}") logger.info(f"Extracted {binary_name} to {target_path}")
# Clean up decompressed file
try:
os.unlink(decompressed_path)
except Exception as e:
logger.warning(f"Failed to clean up decompressed file: {e}")
except Exception as e:
raise DownloadError(f"Failed to extract tar.xz archive: {e}")
def verify(self) -> bool: def verify(self) -> bool:
"""Verify FFmpeg and FFprobe binaries work""" """Verify FFmpeg and FFprobe binaries work"""
try: try:
@@ -299,7 +317,7 @@ class FFmpegDownloader:
# Test FFmpeg functionality with enhanced error handling # Test FFmpeg functionality with enhanced error handling
try: try:
ffmpeg_result = subprocess.run( result = subprocess.run(
[str(self.ffmpeg_path), "-version"], [str(self.ffmpeg_path), "-version"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@@ -317,7 +335,7 @@ class FFmpegDownloader:
# Test FFprobe functionality with enhanced error handling # Test FFprobe functionality with enhanced error handling
try: try:
ffprobe_result = subprocess.run( result = subprocess.run(
[str(self.ffprobe_path), "-version"], [str(self.ffprobe_path), "-version"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@@ -334,24 +352,17 @@ class FFmpegDownloader:
return False return False
# Check results # Check results
if ffmpeg_result.returncode == 0 and ffprobe_result.returncode == 0: if result.returncode == 0:
try: try:
ffmpeg_version = ffmpeg_result.stdout.split("\n")[0] ffmpeg_version = result.stdout.split("\n")[0]
ffprobe_version = ffprobe_result.stdout.split("\n")[0]
logger.info(f"FFmpeg verification successful: {ffmpeg_version}") logger.info(f"FFmpeg verification successful: {ffmpeg_version}")
logger.info(f"FFprobe verification successful: {ffprobe_version}")
return True return True
except Exception as e: except Exception as e:
logger.error(f"Failed to parse version output: {e}") logger.error(f"Failed to parse version output: {e}")
return False return False
else: else:
if ffmpeg_result.returncode != 0:
logger.error( logger.error(
f"FFmpeg verification failed with code {ffmpeg_result.returncode}: {ffmpeg_result.stderr}" f"FFmpeg verification failed with code {result.returncode}: {result.stderr}"
)
if ffprobe_result.returncode != 0:
logger.error(
f"FFprobe verification failed with code {ffprobe_result.returncode}: {ffprobe_result.stderr}"
) )
return False return False