mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
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
187 lines
5.5 KiB
Python
187 lines
5.5 KiB
Python
"""FFmpeg module initialization"""
|
|
|
|
import logging
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Dict, Any, Optional
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.StreamHandler(sys.stdout)
|
|
]
|
|
)
|
|
|
|
logger = logging.getLogger("VideoArchiver")
|
|
|
|
# Import components after logging is configured
|
|
from .ffmpeg_manager import FFmpegManager
|
|
from .video_analyzer import VideoAnalyzer
|
|
from .gpu_detector import GPUDetector
|
|
from .encoder_params import EncoderParams
|
|
from .ffmpeg_downloader import FFmpegDownloader
|
|
from .exceptions import (
|
|
FFmpegError,
|
|
DownloadError,
|
|
VerificationError,
|
|
EncodingError,
|
|
AnalysisError,
|
|
GPUError,
|
|
HardwareAccelerationError,
|
|
FFmpegNotFoundError,
|
|
FFprobeError,
|
|
CompressionError,
|
|
FormatError,
|
|
PermissionError,
|
|
TimeoutError,
|
|
ResourceError,
|
|
QualityError,
|
|
AudioError,
|
|
BitrateError,
|
|
)
|
|
|
|
class FFmpeg:
|
|
"""Main FFmpeg interface"""
|
|
|
|
_instance = None
|
|
|
|
def __new__(cls, base_dir: Optional[Path] = None):
|
|
"""Singleton pattern to ensure only one FFmpeg instance"""
|
|
if cls._instance is None:
|
|
cls._instance = super(FFmpeg, cls).__new__(cls)
|
|
cls._instance._initialized = False
|
|
return cls._instance
|
|
|
|
def __init__(self, base_dir: Optional[Path] = None):
|
|
"""Initialize FFmpeg interface
|
|
|
|
Args:
|
|
base_dir: Optional base directory for FFmpeg files. If not provided,
|
|
will use the default directory in the module.
|
|
"""
|
|
# Skip initialization if already done
|
|
if self._initialized:
|
|
return
|
|
|
|
try:
|
|
self._manager = FFmpegManager()
|
|
logger.info(f"FFmpeg initialized at {self._manager.get_ffmpeg_path()}")
|
|
logger.info(f"GPU support: {self._manager.gpu_info}")
|
|
self._initialized = True
|
|
except Exception as e:
|
|
logger.error(f"Failed to initialize FFmpeg interface: {e}")
|
|
raise FFmpegError(f"FFmpeg initialization failed: {e}")
|
|
|
|
@property
|
|
def ffmpeg_path(self) -> Path:
|
|
"""Get path to FFmpeg binary"""
|
|
return Path(self._manager.get_ffmpeg_path())
|
|
|
|
@property
|
|
def gpu_info(self) -> Dict[str, bool]:
|
|
"""Get GPU information"""
|
|
return self._manager.gpu_info
|
|
|
|
def analyze_video(self, input_path: str) -> Dict[str, Any]:
|
|
"""Analyze video content
|
|
|
|
Args:
|
|
input_path: Path to input video file
|
|
|
|
Returns:
|
|
Dict containing video analysis results
|
|
"""
|
|
try:
|
|
if not os.path.exists(input_path):
|
|
raise FileNotFoundError(f"Input file not found: {input_path}")
|
|
return self._manager.analyze_video(input_path)
|
|
except Exception as e:
|
|
logger.error(f"Video analysis failed: {e}")
|
|
raise AnalysisError(f"Failed to analyze video: {e}")
|
|
|
|
def get_compression_params(self, input_path: str, target_size_mb: int) -> Dict[str, str]:
|
|
"""Get optimal compression parameters
|
|
|
|
Args:
|
|
input_path: Path to input video file
|
|
target_size_mb: Target file size in megabytes
|
|
|
|
Returns:
|
|
Dict containing FFmpeg encoding parameters
|
|
"""
|
|
try:
|
|
if not os.path.exists(input_path):
|
|
raise FileNotFoundError(f"Input file not found: {input_path}")
|
|
return self._manager.get_compression_params(input_path, target_size_mb)
|
|
except Exception as e:
|
|
logger.error(f"Failed to get compression parameters: {e}")
|
|
raise EncodingError(f"Failed to get compression parameters: {e}")
|
|
|
|
def force_download(self) -> bool:
|
|
"""Force re-download of FFmpeg binary
|
|
|
|
Returns:
|
|
bool: True if download successful, False otherwise
|
|
"""
|
|
try:
|
|
return self._manager.force_download()
|
|
except Exception as e:
|
|
logger.error(f"Force download failed: {e}")
|
|
raise DownloadError(f"Failed to force download FFmpeg: {e}")
|
|
|
|
@property
|
|
def version(self) -> str:
|
|
"""Get FFmpeg version"""
|
|
try:
|
|
import subprocess
|
|
result = subprocess.run(
|
|
[str(self.ffmpeg_path), "-version"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
if result.returncode == 0:
|
|
return result.stdout.split()[2]
|
|
raise FFmpegError(f"FFmpeg version check failed: {result.stderr}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to get FFmpeg version: {e}")
|
|
raise FFmpegError(f"Failed to get FFmpeg version: {e}")
|
|
|
|
# Initialize default instance
|
|
try:
|
|
ffmpeg = FFmpeg()
|
|
logger.info(f"Default FFmpeg instance initialized (version {ffmpeg.version})")
|
|
except Exception as e:
|
|
logger.error(f"Failed to initialize default FFmpeg instance: {e}")
|
|
ffmpeg = None
|
|
|
|
__all__ = [
|
|
'FFmpeg',
|
|
'ffmpeg',
|
|
'FFmpegManager',
|
|
'VideoAnalyzer',
|
|
'GPUDetector',
|
|
'EncoderParams',
|
|
'FFmpegDownloader',
|
|
'FFmpegError',
|
|
'DownloadError',
|
|
'VerificationError',
|
|
'EncodingError',
|
|
'AnalysisError',
|
|
'GPUError',
|
|
'HardwareAccelerationError',
|
|
'FFmpegNotFoundError',
|
|
'FFprobeError',
|
|
'CompressionError',
|
|
'FormatError',
|
|
'PermissionError',
|
|
'TimeoutError',
|
|
'ResourceError',
|
|
'QualityError',
|
|
'AudioError',
|
|
'BitrateError',
|
|
]
|