Files
Pac-cogs/videoarchiver/ffmpeg/verification_manager.py
2024-11-18 01:21:40 +00:00

162 lines
5.4 KiB
Python

"""Module for verifying FFmpeg functionality"""
import logging
import os
import subprocess
from pathlib import Path
from typing import Dict, List, Optional
# try:
# Try relative imports first
from .exceptions import (
TimeoutError,
VerificationError,
EncodingError,
handle_ffmpeg_error,
)
# except ImportError:
# Fall back to absolute imports if relative imports fail
# from videoarchiver.ffmpeg.exceptions import (
# TimeoutError,
# VerificationError,
# EncodingError,
# handle_ffmpeg_error
# )
logger = logging.getLogger("FFmpegVerification")
class VerificationManager:
"""Handles verification of FFmpeg functionality"""
def __init__(self, process_manager):
self.process_manager = process_manager
def verify_ffmpeg(
self, ffmpeg_path: Path, ffprobe_path: Path, gpu_info: Dict[str, bool]
) -> None:
"""Verify FFmpeg functionality with comprehensive checks
Args:
ffmpeg_path: Path to FFmpeg binary
ffprobe_path: Path to FFprobe binary
gpu_info: Dictionary of GPU availability
Raises:
VerificationError: If verification fails
TimeoutError: If verification times out
EncodingError: If required encoders are missing
"""
try:
# Check FFmpeg version
self._verify_ffmpeg_version(ffmpeg_path)
# Check FFprobe version
self._verify_ffprobe_version(ffprobe_path)
# Check FFmpeg capabilities
self._verify_ffmpeg_capabilities(ffmpeg_path, gpu_info)
logger.info("FFmpeg verification completed successfully")
except Exception as e:
logger.error(f"FFmpeg verification failed: {e}")
if isinstance(e, (TimeoutError, EncodingError, VerificationError)):
raise
raise VerificationError(f"FFmpeg verification failed: {e}")
def _verify_ffmpeg_version(self, ffmpeg_path: Path) -> None:
"""Verify FFmpeg version"""
try:
result = self._execute_command(
[str(ffmpeg_path), "-version"], "FFmpeg version check"
)
logger.info(f"FFmpeg version: {result.stdout.split()[2]}")
except Exception as e:
raise VerificationError(f"FFmpeg version check failed: {e}")
def _verify_ffprobe_version(self, ffprobe_path: Path) -> None:
"""Verify FFprobe version"""
try:
result = self._execute_command(
[str(ffprobe_path), "-version"], "FFprobe version check"
)
logger.info(f"FFprobe version: {result.stdout.split()[2]}")
except Exception as e:
raise VerificationError(f"FFprobe version check failed: {e}")
def _verify_ffmpeg_capabilities(
self, ffmpeg_path: Path, gpu_info: Dict[str, bool]
) -> None:
"""Verify FFmpeg capabilities and encoders"""
try:
result = self._execute_command(
[str(ffmpeg_path), "-hide_banner", "-encoders"],
"FFmpeg capabilities check",
)
# Verify required encoders
required_encoders = self._get_required_encoders(gpu_info)
available_encoders = result.stdout.lower()
missing_encoders = [
encoder
for encoder in required_encoders
if encoder not in available_encoders
]
if missing_encoders:
logger.warning(f"Missing encoders: {', '.join(missing_encoders)}")
if "libx264" in missing_encoders:
raise EncodingError("Required encoder libx264 not available")
except Exception as e:
if isinstance(e, EncodingError):
raise
raise VerificationError(f"FFmpeg capabilities check failed: {e}")
def _execute_command(
self, command: List[str], operation: str, timeout: int = 10
) -> subprocess.CompletedProcess:
"""Execute a command with proper error handling"""
try:
result = self.process_manager.execute_command(
command, timeout=timeout, check=False
)
if result.returncode != 0:
error = handle_ffmpeg_error(result.stderr)
logger.error(f"{operation} failed: {result.stderr}")
raise error
return result
except subprocess.TimeoutExpired:
raise TimeoutError(f"{operation} timed out")
except Exception as e:
if isinstance(e, (TimeoutError, EncodingError)):
raise
raise VerificationError(f"{operation} failed: {e}")
def _get_required_encoders(self, gpu_info: Dict[str, bool]) -> List[str]:
"""Get list of required encoders based on GPU availability"""
required_encoders = ["libx264"]
if gpu_info["nvidia"]:
required_encoders.append("h264_nvenc")
elif gpu_info["amd"]:
required_encoders.append("h264_amf")
elif gpu_info["intel"]:
required_encoders.append("h264_qsv")
return required_encoders
def verify_binary_permissions(self, binary_path: Path) -> None:
"""Verify and set binary permissions"""
try:
if os.name != "nt": # Not Windows
os.chmod(str(binary_path), 0o755)
except Exception as e:
raise VerificationError(f"Failed to set binary permissions: {e}")