AI-Talent-Force/dev_caio
0
1"""2ShortSmith v2 - Configuration Module3 4Centralized configuration for all components including model paths,5thresholds, domain presets, and runtime settings.6"""7 8import os9from dataclasses import dataclass, field10from typing import Dict, Optional11from enum import Enum12 13 14class ContentDomain(Enum):15 """Supported content domains with different hype characteristics."""16 SPORTS = "sports"17 VLOGS = "vlogs"18 MUSIC = "music"19 PODCASTS = "podcasts"20 GAMING = "gaming"21 GENERAL = "general"22 23 24@dataclass25class DomainWeights:26 """Weight configuration for visual vs audio scoring per domain."""27 visual_weight: float28 audio_weight: float29 motion_weight: float = 0.030 31 def __post_init__(self):32 """Normalize weights to sum to 1.0."""33 total = self.visual_weight + self.audio_weight + self.motion_weight34 if total > 0:35 self.visual_weight /= total36 self.audio_weight /= total37 self.motion_weight /= total38 39 40# Domain-specific weight presets41DOMAIN_PRESETS: Dict[ContentDomain, DomainWeights] = {42 ContentDomain.SPORTS: DomainWeights(visual_weight=0.35, audio_weight=0.50, motion_weight=0.15),43 ContentDomain.VLOGS: DomainWeights(visual_weight=0.70, audio_weight=0.20, motion_weight=0.10),44 ContentDomain.MUSIC: DomainWeights(visual_weight=0.40, audio_weight=0.50, motion_weight=0.10),45 ContentDomain.PODCASTS: DomainWeights(visual_weight=0.10, audio_weight=0.85, motion_weight=0.05),46 ContentDomain.GAMING: DomainWeights(visual_weight=0.50, audio_weight=0.35, motion_weight=0.15),47 ContentDomain.GENERAL: DomainWeights(visual_weight=0.50, audio_weight=0.40, motion_weight=0.10),48}49 50 51@dataclass52class ModelConfig:53 """Configuration for AI models."""54 # Visual model (Qwen2-VL)55 visual_model_id: str = "Qwen/Qwen2-VL-2B-Instruct"56 visual_model_quantization: str = "int4" # Options: "int4", "int8", "none"57 visual_max_frames: int = 3258 59 # Audio model60 audio_model_id: str = "facebook/wav2vec2-base-960h"61 use_advanced_audio: bool = False # Use Wav2Vec2 instead of just Librosa62 63 # Face recognition (InsightFace)64 face_detection_model: str = "buffalo_l" # SCRFD model65 face_similarity_threshold: float = 0.466 67 # Body recognition (OSNet)68 body_model_name: str = "osnet_x1_0"69 body_similarity_threshold: float = 0.570 71 # Motion detection (RAFT)72 motion_model: str = "raft-things"73 motion_threshold: float = 5.074 75 # Device settings76 device: str = "cuda" # Options: "cuda", "cpu", "mps"77 78 def __post_init__(self):79 """Validate and adjust device based on availability."""80 import torch81 if self.device == "cuda" and not torch.cuda.is_available():82 self.device = "cpu"83 elif self.device == "mps" and not (hasattr(torch.backends, "mps") and torch.backends.mps.is_available()):84 self.device = "cpu"85 86 87@dataclass88class ProcessingConfig:89 """Configuration for video processing pipeline."""90 # Sampling settings91 coarse_sample_interval: float = 5.0 # Seconds between frames in first pass92 dense_sample_fps: float = 3.0 # FPS for dense sampling on candidates93 min_motion_for_dense: float = 2.0 # Threshold to trigger dense sampling94 95 # Clip settings96 min_clip_duration: float = 10.0 # Minimum clip length in seconds97 max_clip_duration: float = 20.0 # Maximum clip length in seconds98 default_clip_duration: float = 15.0 # Default clip length99 min_gap_between_clips: float = 30.0 # Minimum gap between clip starts100 101 # Output settings102 default_num_clips: int = 3103 max_num_clips: int = 10104 output_format: str = "mp4"105 output_codec: str = "libx264"106 output_audio_codec: str = "aac"107 108 # Scene detection109 scene_threshold: float = 27.0 # PySceneDetect threshold110 111 # Hype scoring112 hype_threshold: float = 0.3 # Minimum normalized score to consider113 diversity_weight: float = 0.2 # Weight for temporal diversity in ranking114 115 # Performance116 batch_size: int = 8 # Frames per batch for model inference117 max_video_duration: float = 12000.0 118 119 # Temporary files120 temp_dir: Optional[str] = None121 cleanup_temp: bool = True122 123 124@dataclass125class AppConfig:126 """Main application configuration."""127 model: ModelConfig = field(default_factory=ModelConfig)128 processing: ProcessingConfig = field(default_factory=ProcessingConfig)129 130 # Logging131 log_level: str = "INFO"132 log_file: Optional[str] = "shortsmith.log"133 log_to_console: bool = True134 135 # API settings (for future extensibility)136 api_key: Optional[str] = None137 138 # UI settings139 share_gradio: bool = False140 server_port: int = 7860141 142 @classmethod143 def from_env(cls) -> "AppConfig":144 """Create configuration from environment variables."""145 config = cls()146 147 # Override from environment148 if os.environ.get("SHORTSMITH_LOG_LEVEL"):149 config.log_level = os.environ["SHORTSMITH_LOG_LEVEL"]150 151 if os.environ.get("SHORTSMITH_DEVICE"):152 config.model.device = os.environ["SHORTSMITH_DEVICE"]153 154 if os.environ.get("SHORTSMITH_API_KEY"):155 config.api_key = os.environ["SHORTSMITH_API_KEY"]156 157 if os.environ.get("HF_TOKEN"):158 # HuggingFace token for accessing gated models159 pass160 161 return config162 163 164# Global configuration instance165_config: Optional[AppConfig] = None166 167 168def get_config() -> AppConfig:169 """Get the global configuration instance."""170 global _config171 if _config is None:172 _config = AppConfig.from_env()173 return _config174 175 176def set_config(config: AppConfig) -> None:177 """Set the global configuration instance."""178 global _config179 _config = config180 181 182# Export commonly used items183__all__ = [184 "ContentDomain",185 "DomainWeights",186 "DOMAIN_PRESETS",187 "ModelConfig",188 "ProcessingConfig",189 "AppConfig",190 "get_config",191 "set_config",192]193 