| """ |
| Custom Exception Classes |
| Provides specific exception types for better error handling and debugging |
| """ |
|
|
| from typing import Optional, Any, Dict |
|
|
| class VideoProcessingError(Exception): |
| """Base exception for video processing errors""" |
| |
| def __init__(self, message: str, error_code: Optional[str] = None, details: Optional[Dict[str, Any]] = None): |
| super().__init__(message) |
| self.message = message |
| self.error_code = error_code |
| self.details = details or {} |
| |
| def __str__(self): |
| base_msg = self.message |
| if self.error_code: |
| base_msg = f"[{self.error_code}] {base_msg}" |
| if self.details: |
| details_str = ", ".join(f"{k}: {v}" for k, v in self.details.items()) |
| base_msg = f"{base_msg} ({details_str})" |
| return base_msg |
|
|
| class ModelLoadingError(VideoProcessingError): |
| """Exception raised when model loading fails""" |
| |
| def __init__(self, model_name: str, message: str, original_error: Optional[Exception] = None): |
| super().__init__( |
| message=f"Failed to load {model_name}: {message}", |
| error_code="MODEL_LOAD_FAILED", |
| details={ |
| "model_name": model_name, |
| "original_error": str(original_error) if original_error else None |
| } |
| ) |
| self.model_name = model_name |
| self.original_error = original_error |
|
|
| class DeviceError(VideoProcessingError): |
| """Exception raised when device-related operations fail""" |
| |
| def __init__(self, device_type: str, message: str, available_devices: Optional[list] = None): |
| super().__init__( |
| message=f"Device error ({device_type}): {message}", |
| error_code="DEVICE_ERROR", |
| details={ |
| "device_type": device_type, |
| "available_devices": available_devices |
| } |
| ) |
| self.device_type = device_type |
| self.available_devices = available_devices or [] |
|
|
| class MemoryError(VideoProcessingError): |
| """Exception raised when memory operations fail""" |
| |
| def __init__(self, operation: str, message: str, memory_usage: Optional[Dict[str, Any]] = None): |
| super().__init__( |
| message=f"Memory error during {operation}: {message}", |
| error_code="MEMORY_ERROR", |
| details={ |
| "operation": operation, |
| "memory_usage": memory_usage |
| } |
| ) |
| self.operation = operation |
| self.memory_usage = memory_usage or {} |
|
|
| class VideoFileError(VideoProcessingError): |
| """Exception raised when video file operations fail""" |
| |
| def __init__(self, file_path: str, operation: str, message: str): |
| super().__init__( |
| message=f"Video file error ({operation}): {message}", |
| error_code="VIDEO_FILE_ERROR", |
| details={ |
| "file_path": file_path, |
| "operation": operation |
| } |
| ) |
| self.file_path = file_path |
| self.operation = operation |
|
|
| class BackgroundProcessingError(VideoProcessingError): |
| """Exception raised when background processing fails""" |
| |
| def __init__(self, background_type: str, message: str, background_path: Optional[str] = None): |
| super().__init__( |
| message=f"Background processing error ({background_type}): {message}", |
| error_code="BACKGROUND_ERROR", |
| details={ |
| "background_type": background_type, |
| "background_path": background_path |
| } |
| ) |
| self.background_type = background_type |
| self.background_path = background_path |
|
|
| class SegmentationError(VideoProcessingError): |
| """Exception raised when person segmentation fails""" |
| |
| def __init__(self, frame_number: int, message: str, segmentation_method: Optional[str] = None): |
| super().__init__( |
| message=f"Segmentation error at frame {frame_number}: {message}", |
| error_code="SEGMENTATION_ERROR", |
| details={ |
| "frame_number": frame_number, |
| "segmentation_method": segmentation_method |
| } |
| ) |
| self.frame_number = frame_number |
| self.segmentation_method = segmentation_method |
|
|
| class AudioProcessingError(VideoProcessingError): |
| """Exception raised when audio processing fails""" |
| |
| def __init__(self, operation: str, message: str, input_file: Optional[str] = None, output_file: Optional[str] = None): |
| super().__init__( |
| message=f"Audio processing error ({operation}): {message}", |
| error_code="AUDIO_ERROR", |
| details={ |
| "operation": operation, |
| "input_file": input_file, |
| "output_file": output_file |
| } |
| ) |
| self.operation = operation |
| self.input_file = input_file |
| self.output_file = output_file |
|
|
| class ConfigurationError(VideoProcessingError): |
| """Exception raised when configuration is invalid""" |
| |
| def __init__(self, config_key: str, value: Any, expected: str): |
| super().__init__( |
| message=f"Invalid configuration: {config_key} = {value}, expected {expected}", |
| error_code="CONFIG_ERROR", |
| details={ |
| "config_key": config_key, |
| "value": value, |
| "expected": expected |
| } |
| ) |
| self.config_key = config_key |
| self.value = value |
| self.expected = expected |
|
|
| class ProcessingCancelledError(VideoProcessingError): |
| """Exception raised when processing is cancelled by user""" |
| |
| def __init__(self, stage: str, processed_frames: int = 0, total_frames: int = 0): |
| super().__init__( |
| message=f"Processing cancelled at {stage}", |
| error_code="PROCESSING_CANCELLED", |
| details={ |
| "stage": stage, |
| "processed_frames": processed_frames, |
| "total_frames": total_frames, |
| "completion_percentage": (processed_frames / total_frames * 100) if total_frames > 0 else 0 |
| } |
| ) |
| self.stage = stage |
| self.processed_frames = processed_frames |
| self.total_frames = total_frames |
|
|
| class TwoStageProcessingError(VideoProcessingError): |
| """Exception raised during two-stage processing""" |
| |
| def __init__(self, stage: str, message: str, chroma_preset: Optional[str] = None): |
| super().__init__( |
| message=f"Two-stage processing error ({stage}): {message}", |
| error_code="TWO_STAGE_ERROR", |
| details={ |
| "stage": stage, |
| "chroma_preset": chroma_preset |
| } |
| ) |
| self.stage = stage |
| self.chroma_preset = chroma_preset |
|
|
| class ResourceExhaustionError(VideoProcessingError): |
| """Exception raised when system resources are exhausted""" |
| |
| def __init__(self, resource_type: str, current_usage: float, limit: float, unit: str = ""): |
| super().__init__( |
| message=f"Resource exhaustion: {resource_type} usage {current_usage}{unit} exceeds limit {limit}{unit}", |
| error_code="RESOURCE_EXHAUSTION", |
| details={ |
| "resource_type": resource_type, |
| "current_usage": current_usage, |
| "limit": limit, |
| "unit": unit |
| } |
| ) |
| self.resource_type = resource_type |
| self.current_usage = current_usage |
| self.limit = limit |
| self.unit = unit |
|
|
| class ValidationError(VideoProcessingError): |
| """Exception raised when input validation fails""" |
| |
| def __init__(self, validation_type: str, message: str, invalid_value: Any = None): |
| super().__init__( |
| message=f"Validation error ({validation_type}): {message}", |
| error_code="VALIDATION_ERROR", |
| details={ |
| "validation_type": validation_type, |
| "invalid_value": invalid_value |
| } |
| ) |
| self.validation_type = validation_type |
| self.invalid_value = invalid_value |
|
|
| |
|
|
| def handle_processing_error(func): |
| """Decorator to handle common processing errors""" |
| def wrapper(*args, **kwargs): |
| try: |
| return func(*args, **kwargs) |
| except VideoProcessingError: |
| |
| raise |
| except MemoryError as e: |
| raise MemoryError("memory_operation", str(e)) from e |
| except FileNotFoundError as e: |
| raise VideoFileError(str(e.filename) if e.filename else "unknown", "file_access", str(e)) from e |
| except PermissionError as e: |
| raise VideoFileError(str(e.filename) if e.filename else "unknown", "file_permission", str(e)) from e |
| except Exception as e: |
| raise VideoProcessingError(f"Unexpected error in {func.__name__}: {str(e)}") from e |
| return wrapper |
|
|
| def create_error_context(operation: str, **context) -> Dict[str, Any]: |
| """Create error context dictionary""" |
| return { |
| "operation": operation, |
| "timestamp": __import__('time').time(), |
| **context |
| } |
|
|
| def log_error_with_context(logger, error: VideoProcessingError, additional_context: Optional[Dict[str, Any]] = None): |
| """Log error with full context information""" |
| context = error.details.copy() |
| if additional_context: |
| context.update(additional_context) |
| |
| logger.error(f"{error} | Context: {context}") |
| |
| |
| if hasattr(error, 'original_error') and error.original_error: |
| logger.error(f"Original error: {error.original_error}") |
|
|
| def is_recoverable_error(error: Exception) -> bool: |
| """Determine if an error is potentially recoverable""" |
| recoverable_errors = ( |
| MemoryError, |
| ResourceExhaustionError, |
| DeviceError |
| ) |
| |
| |
| if isinstance(error, recoverable_errors): |
| return True |
| |
| |
| if isinstance(error, VideoProcessingError): |
| recoverable_codes = ["MEMORY_ERROR", "DEVICE_ERROR", "RESOURCE_EXHAUSTION"] |
| return error.error_code in recoverable_codes |
| |
| return False |
|
|
| def get_error_severity(error: Exception) -> str: |
| """Get error severity level""" |
| if isinstance(error, ProcessingCancelledError): |
| return "INFO" |
| elif isinstance(error, ValidationError): |
| return "WARNING" |
| elif isinstance(error, (ModelLoadingError, DeviceError)): |
| return "CRITICAL" |
| elif isinstance(error, VideoProcessingError): |
| return "ERROR" |
| else: |
| return "UNKNOWN" |