| """ |
| Application Configuration Module |
| Centralizes all application configuration and environment variable handling |
| |
| Note: Named 'app_config.py' to avoid conflicts with existing 'Configs/' folder |
| """ |
|
|
| import os |
| from dataclasses import dataclass, asdict |
| from typing import Dict, Any, Optional |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
| @dataclass |
| class ProcessingConfig: |
| """ |
| Main processing configuration with environment variable defaults |
| """ |
| |
| keyframe_interval: int = int(os.getenv('KEYFRAME_INTERVAL', '5')) |
| frame_skip: int = int(os.getenv('FRAME_SKIP', '1')) |
| |
| |
| memory_cleanup_interval: int = int(os.getenv('MEMORY_CLEANUP_INTERVAL', '30')) |
| |
| |
| max_video_length: int = int(os.getenv('MAX_VIDEO_LENGTH', '300')) |
| max_video_resolution: str = os.getenv('MAX_VIDEO_RESOLUTION', '1920x1080') |
| |
| |
| quality_preset: str = os.getenv('QUALITY_PRESET', 'balanced') |
| |
| |
| sam2_model_size: str = os.getenv('SAM2_MODEL_SIZE', 'large') |
| matanyone_precision: str = os.getenv('MATANYONE_PRECISION', 'fp32') |
| |
| |
| temporal_consistency: bool = os.getenv('TEMPORAL_CONSISTENCY', 'true').lower() == 'true' |
| edge_refinement: bool = os.getenv('EDGE_REFINEMENT', 'true').lower() == 'true' |
| |
| |
| output_format: str = os.getenv('OUTPUT_FORMAT', 'mp4') |
| output_quality: str = os.getenv('OUTPUT_QUALITY', 'high') |
| preserve_audio: bool = os.getenv('PRESERVE_AUDIO', 'true').lower() == 'true' |
| |
| |
| model_cache_dir: str = os.getenv('MODEL_CACHE_DIR', '/tmp/model_cache') |
| temp_dir: str = os.getenv('TEMP_DIR', '/tmp') |
| cleanup_temp_files: bool = os.getenv('CLEANUP_TEMP_FILES', 'true').lower() == 'true' |
| |
| |
| max_concurrent_processes: int = int(os.getenv('MAX_CONCURRENT_PROCESSES', '1')) |
| gpu_memory_fraction: float = float(os.getenv('GPU_MEMORY_FRACTION', '0.8')) |
| |
| |
| debug_mode: bool = os.getenv('DEBUG_MODE', 'false').lower() == 'true' |
| save_intermediate_results: bool = os.getenv('SAVE_INTERMEDIATE_RESULTS', 'false').lower() == 'true' |
| |
| def __post_init__(self): |
| """Validate configuration after initialization""" |
| self._validate_config() |
| self._create_directories() |
| if self.debug_mode: |
| self._log_config() |
| |
| def _validate_config(self): |
| """Validate configuration values""" |
| |
| if self.keyframe_interval < 1: |
| logger.warning(f"keyframe_interval must be >= 1, got {self.keyframe_interval}. Setting to 1.") |
| self.keyframe_interval = 1 |
| |
| if self.frame_skip < 1: |
| logger.warning(f"frame_skip must be >= 1, got {self.frame_skip}. Setting to 1.") |
| self.frame_skip = 1 |
| |
| |
| if self.memory_cleanup_interval < 1: |
| logger.warning(f"memory_cleanup_interval must be >= 1, got {self.memory_cleanup_interval}. Setting to 10.") |
| self.memory_cleanup_interval = 10 |
| |
| |
| if self.max_video_length < 1: |
| logger.warning(f"max_video_length must be >= 1, got {self.max_video_length}. Setting to 60.") |
| self.max_video_length = 60 |
| |
| |
| if 'x' not in self.max_video_resolution: |
| logger.warning(f"Invalid resolution format: {self.max_video_resolution}. Setting to 1920x1080.") |
| self.max_video_resolution = '1920x1080' |
| |
| |
| valid_presets = ['fast', 'balanced', 'high', 'ultra'] |
| if self.quality_preset not in valid_presets: |
| logger.warning(f"Invalid quality preset: {self.quality_preset}. Setting to 'balanced'.") |
| self.quality_preset = 'balanced' |
| |
| |
| valid_sam2_sizes = ['tiny', 'small', 'base', 'large'] |
| if self.sam2_model_size not in valid_sam2_sizes: |
| logger.warning(f"Invalid SAM2 model size: {self.sam2_model_size}. Setting to 'large'.") |
| self.sam2_model_size = 'large' |
| |
| valid_precisions = ['fp16', 'fp32'] |
| if self.matanyone_precision not in valid_precisions: |
| logger.warning(f"Invalid precision: {self.matanyone_precision}. Setting to 'fp32'.") |
| self.matanyone_precision = 'fp32' |
| |
| |
| valid_formats = ['mp4', 'avi', 'mov', 'webm'] |
| if self.output_format not in valid_formats: |
| logger.warning(f"Invalid output format: {self.output_format}. Setting to 'mp4'.") |
| self.output_format = 'mp4' |
| |
| valid_qualities = ['low', 'medium', 'high'] |
| if self.output_quality not in valid_qualities: |
| logger.warning(f"Invalid output quality: {self.output_quality}. Setting to 'high'.") |
| self.output_quality = 'high' |
| |
| |
| if self.max_concurrent_processes < 1: |
| logger.warning(f"max_concurrent_processes must be >= 1, got {self.max_concurrent_processes}. Setting to 1.") |
| self.max_concurrent_processes = 1 |
| |
| if not (0.1 <= self.gpu_memory_fraction <= 1.0): |
| logger.warning(f"gpu_memory_fraction must be between 0.1 and 1.0, got {self.gpu_memory_fraction}. Setting to 0.8.") |
| self.gpu_memory_fraction = 0.8 |
| |
| def _create_directories(self): |
| """Create necessary directories if they don't exist""" |
| import os |
| directories = [self.model_cache_dir, self.temp_dir] |
| |
| for directory in directories: |
| try: |
| os.makedirs(directory, exist_ok=True) |
| logger.debug(f"Ensured directory exists: {directory}") |
| except Exception as e: |
| logger.error(f"Failed to create directory {directory}: {e}") |
| |
| def _log_config(self): |
| """Log current configuration in debug mode""" |
| logger.info("=== Processing Configuration ===") |
| for key, value in asdict(self).items(): |
| logger.info(f"{key}: {value}") |
| logger.info("===============================") |
| |
| def to_dict(self) -> Dict[str, Any]: |
| """Convert configuration to dictionary""" |
| return asdict(self) |
| |
| def get_quality_settings(self) -> Dict[str, Any]: |
| """Get quality-specific settings based on preset""" |
| quality_maps = { |
| 'fast': { |
| 'keyframe_interval': max(self.keyframe_interval, 10), |
| 'frame_skip': max(self.frame_skip, 2), |
| 'edge_refinement': False, |
| 'temporal_consistency': False, |
| 'model_precision': 'fp16' |
| }, |
| 'balanced': { |
| 'keyframe_interval': self.keyframe_interval, |
| 'frame_skip': self.frame_skip, |
| 'edge_refinement': True, |
| 'temporal_consistency': True, |
| 'model_precision': 'fp32' |
| }, |
| 'high': { |
| 'keyframe_interval': max(self.keyframe_interval // 2, 1), |
| 'frame_skip': 1, |
| 'edge_refinement': True, |
| 'temporal_consistency': True, |
| 'model_precision': 'fp32' |
| }, |
| 'ultra': { |
| 'keyframe_interval': 1, |
| 'frame_skip': 1, |
| 'edge_refinement': True, |
| 'temporal_consistency': True, |
| 'model_precision': 'fp32' |
| } |
| } |
| |
| return quality_maps.get(self.quality_preset, quality_maps['balanced']) |
| |
| def get_resolution_limits(self) -> tuple[int, int]: |
| """Get max width and height from resolution setting""" |
| try: |
| width, height = map(int, self.max_video_resolution.split('x')) |
| return width, height |
| except ValueError: |
| logger.error(f"Invalid resolution format: {self.max_video_resolution}") |
| return 1920, 1080 |
| |
| def is_high_performance_mode(self) -> bool: |
| """Check if configuration is set for high performance""" |
| return ( |
| self.quality_preset in ['high', 'ultra'] and |
| self.edge_refinement and |
| self.temporal_consistency and |
| self.keyframe_interval <= 3 |
| ) |
| |
| def get_memory_limits(self) -> Dict[str, float]: |
| """Get memory-related limits""" |
| return { |
| 'gpu_memory_fraction': self.gpu_memory_fraction, |
| 'cleanup_interval': self.memory_cleanup_interval, |
| 'max_concurrent': self.max_concurrent_processes |
| } |
|
|
| |
| _config_instance: Optional[ProcessingConfig] = None |
|
|
| def get_config() -> ProcessingConfig: |
| """Get global configuration instance""" |
| global _config_instance |
| if _config_instance is None: |
| _config_instance = ProcessingConfig() |
| return _config_instance |
|
|
| def reload_config() -> ProcessingConfig: |
| """Reload configuration from environment variables""" |
| global _config_instance |
| _config_instance = ProcessingConfig() |
| logger.info("Configuration reloaded from environment variables") |
| return _config_instance |
|
|
| def update_config(**kwargs) -> ProcessingConfig: |
| """Update configuration with new values""" |
| global _config_instance |
| if _config_instance is None: |
| _config_instance = ProcessingConfig() |
| |
| for key, value in kwargs.items(): |
| if hasattr(_config_instance, key): |
| setattr(_config_instance, key, value) |
| logger.debug(f"Updated config: {key} = {value}") |
| else: |
| logger.warning(f"Unknown configuration key: {key}") |
| |
| |
| _config_instance._validate_config() |
| return _config_instance |