from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent VIDEOS_ROOT = BASE_DIR / "videos" OUTPUTS_ROOT = BASE_DIR / "outputs" TEMP_ROOT = BASE_DIR / "temp" @dataclass(frozen=True) class RabbitMQSettings: # host: str = os.environ.get("RABBITMQ_HOST", "154.12.229.181") # port: int = int(os.environ.get("RABBITMQ_PORT", 32790)) host: str = os.environ.get("RABBITMQ_HOST", "rabbitmq") port: int = int(os.environ.get("RABBITMQ_PORT", 5672)) user: str = os.environ.get("RABBITMQ_USER", "admin") password: str = os.environ.get("RABBITMQ_PASS") consume_queue: str = os.environ.get("RABBITMQ_QUEUE", "to-render") publish_queue: str = os.environ.get("RABBITMQ_UPLOAD_QUEUE", "to-upload") prefetch_count: int = int(os.environ.get("RABBITMQ_PREFETCH", 1)) heartbeat: int = int(os.environ.get("RABBITMQ_HEARTBEAT", 600)) blocked_timeout: int = int(os.environ.get("RABBITMQ_BLOCKED_TIMEOUT", 7200)) @dataclass(frozen=True) class OpenRouterSettings: api_key: str = os.environ.get("OPENROUTER_API_KEY", "https://openrouter.ai/api/v1/chat/completions") model: str = os.environ.get( "OPENROUTER_MODEL", "openai/gpt-oss-20b:free" ) temperature: float = float(os.environ.get("OPENROUTER_TEMPERATURE", 0.6)) prompt_path: str = os.environ.get("OPENROUTER_PROMPT_PATH", "prompts/generate.txt") @dataclass(frozen=True) class WhisperSettings: model_size: str = os.environ.get("FASTER_WHISPER_MODEL_SIZE", "medium") device: str | None = os.environ.get("FASTER_WHISPER_DEVICE") compute_type: str | None = os.environ.get("FASTER_WHISPER_COMPUTE_TYPE") download_root: Path = Path( os.environ.get("FASTER_WHISPER_DOWNLOAD_ROOT", str(BASE_DIR / ".whisper")) ) @dataclass(frozen=True) class RenderingSettings: frame_width: int = int(os.environ.get("RENDER_WIDTH", 1080)) frame_height: int = int(os.environ.get("RENDER_HEIGHT", 1920)) fps: int = int(os.environ.get("RENDER_FPS", 30)) video_codec: str = os.environ.get("RENDER_CODEC", "libx264") audio_codec: str = os.environ.get("RENDER_AUDIO_CODEC", "aac") bitrate: str = os.environ.get("RENDER_BITRATE", "5000k") preset: str = os.environ.get("RENDER_PRESET", "faster") highlight_color: str = os.environ.get("SUBTITLE_HIGHLIGHT_COLOR", "#00FF00") base_color: str = os.environ.get("SUBTITLE_BASE_COLOR", "#FFFFFF") font_path: Path = Path(os.environ.get("RENDER_FONT_PATH", "./Montserrat.ttf")) title_font_size: int = int(os.environ.get("RENDER_TITLE_FONT_SIZE", 110)) subtitle_font_size: int = int(os.environ.get("RENDER_SUBTITLE_FONT_SIZE", 64)) caption_min_words: int = int(os.environ.get("CAPTION_MIN_WORDS", 2)) caption_max_words: int = int(os.environ.get("CAPTION_MAX_WORDS", 2)) enable_smart_framing: bool = os.environ.get("ENABLE_SMART_FRAMING", "true").lower() in ("true", "1", "yes") smart_framing_min_confidence: float = float(os.environ.get("SMART_FRAMING_MIN_CONFIDENCE", 0.3)) smart_framing_smoothing_window: int = int(os.environ.get("SMART_FRAMING_SMOOTHING_WINDOW", 30)) smart_framing_frame_skip: int = int(os.environ.get("SMART_FRAMING_FRAME_SKIP", 1)) smart_framing_max_velocity: int = int(os.environ.get("SMART_FRAMING_MAX_VELOCITY", 25)) smart_framing_person_switch_cooldown: int = int(os.environ.get("SMART_FRAMING_PERSON_SWITCH_COOLDOWN", 30)) smart_framing_response_time: float = float(os.environ.get("SMART_FRAMING_RESPONSE_TIME", 0.6)) smart_framing_group_padding: float = float(os.environ.get("SMART_FRAMING_GROUP_PADDING", 0.15)) smart_framing_max_zoom_out: float = float(os.environ.get("SMART_FRAMING_MAX_ZOOM_OUT", 2.0)) smart_framing_dead_zone: int = int(os.environ.get("SMART_FRAMING_DEAD_ZONE", 60)) @dataclass(frozen=True) class Settings: rabbitmq: RabbitMQSettings = RabbitMQSettings() openrouter: OpenRouterSettings = OpenRouterSettings() whisper: WhisperSettings = WhisperSettings() rendering: RenderingSettings = RenderingSettings() videos_dir: Path = VIDEOS_ROOT outputs_dir: Path = OUTPUTS_ROOT temp_dir: Path = TEMP_ROOT def load_settings() -> Settings: settings = Settings() if not settings.rabbitmq.password: raise RuntimeError("RABBITMQ_PASS must be provided") settings.videos_dir.mkdir(parents=True, exist_ok=True) settings.outputs_dir.mkdir(parents=True, exist_ok=True) settings.temp_dir.mkdir(parents=True, exist_ok=True) return settings