mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
Enhanced FFmpeg Integration:
Added robust error handling and logging Improved binary verification and initialization Added proper GPU detection and hardware acceleration Optimized encoding parameters for different content types Improved File Operations: Added retry mechanisms for file operations Enhanced temporary directory management Improved cleanup of failed downloads Added proper permission handling Enhanced Queue Management: Fixed queue manager initialization Added better error recovery Improved status tracking and logging Enhanced cleanup of failed items Better Error Handling: Added comprehensive exception hierarchy Improved error logging and reporting Added fallback mechanisms for failures Enhanced error recovery strategies
This commit is contained in:
@@ -31,9 +31,12 @@ class FFmpegManager:
|
||||
base_dir=self.base_dir
|
||||
)
|
||||
|
||||
# Get or download FFmpeg
|
||||
self.ffmpeg_path = self._initialize_ffmpeg()
|
||||
# Get or download FFmpeg and FFprobe
|
||||
binaries = self._initialize_binaries()
|
||||
self.ffmpeg_path = binaries["ffmpeg"]
|
||||
self.ffprobe_path = binaries["ffprobe"]
|
||||
logger.info(f"Using FFmpeg from: {self.ffmpeg_path}")
|
||||
logger.info(f"Using FFprobe from: {self.ffprobe_path}")
|
||||
|
||||
# Initialize components
|
||||
self.gpu_detector = GPUDetector(self.ffmpeg_path)
|
||||
@@ -48,35 +51,40 @@ class FFmpegManager:
|
||||
self._verify_ffmpeg()
|
||||
logger.info("FFmpeg manager initialized successfully")
|
||||
|
||||
def _initialize_ffmpeg(self) -> Path:
|
||||
"""Initialize FFmpeg binary with proper error handling"""
|
||||
def _initialize_binaries(self) -> Dict[str, Path]:
|
||||
"""Initialize FFmpeg and FFprobe binaries with proper error handling"""
|
||||
try:
|
||||
# Verify existing FFmpeg if it exists
|
||||
if self.downloader.ffmpeg_path.exists():
|
||||
# Verify existing binaries if they exist
|
||||
if self.downloader.ffmpeg_path.exists() and self.downloader.ffprobe_path.exists():
|
||||
logger.info(f"Found existing FFmpeg: {self.downloader.ffmpeg_path}")
|
||||
logger.info(f"Found existing FFprobe: {self.downloader.ffprobe_path}")
|
||||
if self.downloader.verify():
|
||||
return self.downloader.ffmpeg_path
|
||||
return {
|
||||
"ffmpeg": self.downloader.ffmpeg_path,
|
||||
"ffprobe": self.downloader.ffprobe_path
|
||||
}
|
||||
else:
|
||||
logger.warning("Existing FFmpeg is not functional, downloading new copy")
|
||||
logger.warning("Existing binaries are not functional, downloading new copies")
|
||||
|
||||
# Download and verify FFmpeg
|
||||
logger.info("Downloading FFmpeg...")
|
||||
ffmpeg_path = self.downloader.download()
|
||||
# Download and verify binaries
|
||||
logger.info("Downloading FFmpeg and FFprobe...")
|
||||
binaries = self.downloader.download()
|
||||
if not self.downloader.verify():
|
||||
raise FFmpegError("Downloaded FFmpeg binary is not functional")
|
||||
raise FFmpegError("Downloaded binaries are not functional")
|
||||
|
||||
# Set executable permissions
|
||||
try:
|
||||
if platform.system() != "Windows":
|
||||
os.chmod(str(ffmpeg_path), 0o755)
|
||||
os.chmod(str(binaries["ffmpeg"]), 0o755)
|
||||
os.chmod(str(binaries["ffprobe"]), 0o755)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set FFmpeg permissions: {e}")
|
||||
logger.error(f"Failed to set binary permissions: {e}")
|
||||
|
||||
return ffmpeg_path
|
||||
return binaries
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize FFmpeg: {e}")
|
||||
raise FFmpegError(f"Failed to initialize FFmpeg: {e}")
|
||||
logger.error(f"Failed to initialize binaries: {e}")
|
||||
raise FFmpegError(f"Failed to initialize binaries: {e}")
|
||||
|
||||
def _verify_ffmpeg(self) -> None:
|
||||
"""Verify FFmpeg functionality with comprehensive checks"""
|
||||
@@ -94,6 +102,19 @@ class FFmpegManager:
|
||||
raise FFmpegError("FFmpeg version check failed")
|
||||
logger.info(f"FFmpeg version: {result.stdout.split()[2]}")
|
||||
|
||||
# Check FFprobe version
|
||||
probe_cmd = [str(self.ffprobe_path), "-version"]
|
||||
result = subprocess.run(
|
||||
probe_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise FFmpegError("FFprobe version check failed")
|
||||
logger.info(f"FFprobe version: {result.stdout.split()[2]}")
|
||||
|
||||
# Check FFmpeg capabilities
|
||||
caps_cmd = [str(self.ffmpeg_path), "-hide_banner", "-encoders"]
|
||||
result = subprocess.run(
|
||||
@@ -174,11 +195,19 @@ class FFmpegManager:
|
||||
raise FFmpegError("FFmpeg is not available")
|
||||
return str(self.ffmpeg_path)
|
||||
|
||||
def get_ffprobe_path(self) -> str:
|
||||
"""Get path to FFprobe binary"""
|
||||
if not self.ffprobe_path.exists():
|
||||
raise FFmpegError("FFprobe is not available")
|
||||
return str(self.ffprobe_path)
|
||||
|
||||
def force_download(self) -> bool:
|
||||
"""Force re-download of FFmpeg binary"""
|
||||
try:
|
||||
logger.info("Force downloading FFmpeg...")
|
||||
self.ffmpeg_path = self.downloader.download()
|
||||
binaries = self.downloader.download()
|
||||
self.ffmpeg_path = binaries["ffmpeg"]
|
||||
self.ffprobe_path = binaries["ffprobe"]
|
||||
return self.downloader.verify()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to force download FFmpeg: {e}")
|
||||
|
||||
@@ -9,7 +9,6 @@ from contextlib import contextmanager
|
||||
import tempfile
|
||||
import shutil
|
||||
import json
|
||||
import re
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
@@ -47,14 +46,7 @@ class VideoAnalyzer:
|
||||
logger.info(f"Initialized VideoAnalyzer with FFmpeg: {self.ffmpeg_path}, FFprobe: {self.ffprobe_path}")
|
||||
|
||||
def analyze_video(self, input_path: str) -> Dict[str, Any]:
|
||||
"""Analyze video content for optimal encoding settings
|
||||
|
||||
Args:
|
||||
input_path: Path to input video file
|
||||
|
||||
Returns:
|
||||
Dict containing video analysis results
|
||||
"""
|
||||
"""Analyze video content for optimal encoding settings"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
logger.error(f"Input file not found: {input_path}")
|
||||
|
||||
@@ -7,6 +7,8 @@ import asyncio
|
||||
import ffmpeg
|
||||
import yt_dlp
|
||||
import shutil
|
||||
import subprocess
|
||||
import json
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from pathlib import Path
|
||||
@@ -152,21 +154,50 @@ class VideoDownloader:
|
||||
def _verify_video_file(self, file_path: str) -> bool:
|
||||
"""Verify video file integrity"""
|
||||
try:
|
||||
probe = ffmpeg.probe(file_path)
|
||||
# Use ffprobe from FFmpegManager
|
||||
ffprobe_path = str(self.ffmpeg_mgr.get_ffprobe_path())
|
||||
logger.debug(f"Using ffprobe from: {ffprobe_path}")
|
||||
|
||||
cmd = [
|
||||
ffprobe_path,
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_format",
|
||||
"-show_streams",
|
||||
file_path
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise VideoVerificationError(f"FFprobe failed: {result.stderr}")
|
||||
|
||||
probe = json.loads(result.stdout)
|
||||
|
||||
# Check if file has video stream
|
||||
video_streams = [s for s in probe["streams"] if s["codec_type"] == "video"]
|
||||
if not video_streams:
|
||||
raise VideoVerificationError("No video streams found")
|
||||
|
||||
# Check if duration is valid
|
||||
duration = float(probe["format"].get("duration", 0))
|
||||
if duration <= 0:
|
||||
raise VideoVerificationError("Invalid video duration")
|
||||
|
||||
# Check if file is readable
|
||||
with open(file_path, "rb") as f:
|
||||
f.seek(0, 2) # Seek to end
|
||||
if f.tell() == 0:
|
||||
raise VideoVerificationError("Empty file")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error verifying video file {file_path}: {e}")
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user