Files
media-downloader/web/backend/core/config.py
Todd 0d7b2b1aab Initial commit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 22:42:55 -04:00

122 lines
4.4 KiB
Python

"""
Unified Configuration Manager
Provides a single source of truth for all configuration values with a clear hierarchy:
1. Environment variables (highest priority)
2. .env file
3. Database settings
4. Hardcoded defaults (lowest priority)
"""
import os
from pathlib import Path
from typing import Any, Dict, Optional
from functools import lru_cache
from dotenv import load_dotenv
# Load environment variables from .env file
PROJECT_ROOT = Path(__file__).parent.parent.parent.parent
ENV_PATH = PROJECT_ROOT / '.env'
if ENV_PATH.exists():
load_dotenv(ENV_PATH)
class Settings:
"""Centralized configuration settings"""
# Project paths
PROJECT_ROOT: Path = PROJECT_ROOT
DB_PATH: Path = PROJECT_ROOT / 'database' / 'media_downloader.db'
CONFIG_PATH: Path = PROJECT_ROOT / 'config' / 'settings.json'
LOG_PATH: Path = PROJECT_ROOT / 'logs'
TEMP_DIR: Path = PROJECT_ROOT / 'temp'
COOKIES_DIR: Path = PROJECT_ROOT / 'cookies'
DATA_DIR: Path = PROJECT_ROOT / 'data'
VENV_BIN: Path = PROJECT_ROOT / 'venv' / 'bin'
MEDIA_BASE_PATH: Path = Path(os.getenv("MEDIA_BASE_PATH", "/opt/immich/md"))
REVIEW_PATH: Path = Path(os.getenv("REVIEW_PATH", "/opt/immich/review"))
RECYCLE_PATH: Path = Path(os.getenv("RECYCLE_PATH", "/opt/immich/recycle"))
# External tool paths (use venv by default)
YT_DLP_PATH: Path = VENV_BIN / 'yt-dlp'
GALLERY_DL_PATH: Path = VENV_BIN / 'gallery-dl'
PYTHON_PATH: Path = VENV_BIN / 'python3'
# Database configuration
DATABASE_BACKEND: str = os.getenv("DATABASE_BACKEND", "sqlite")
DATABASE_URL: str = os.getenv("DATABASE_URL", "")
DB_POOL_SIZE: int = int(os.getenv("DB_POOL_SIZE", "20"))
DB_CONNECTION_TIMEOUT: float = float(os.getenv("DB_CONNECTION_TIMEOUT", "30.0"))
# Process timeouts (seconds)
PROCESS_TIMEOUT_SHORT: int = int(os.getenv("PROCESS_TIMEOUT_SHORT", "2"))
PROCESS_TIMEOUT_MEDIUM: int = int(os.getenv("PROCESS_TIMEOUT_MEDIUM", "10"))
PROCESS_TIMEOUT_LONG: int = int(os.getenv("PROCESS_TIMEOUT_LONG", "120"))
# WebSocket configuration
WEBSOCKET_TIMEOUT: float = float(os.getenv("WEBSOCKET_TIMEOUT", "30.0"))
# Background task intervals (seconds)
ACTIVITY_LOG_INTERVAL: int = int(os.getenv("ACTIVITY_LOG_INTERVAL", "300"))
EMBEDDING_QUEUE_CHECK_INTERVAL: int = int(os.getenv("EMBEDDING_QUEUE_CHECK_INTERVAL", "30"))
EMBEDDING_BATCH_SIZE: int = int(os.getenv("EMBEDDING_BATCH_SIZE", "10"))
EMBEDDING_BATCH_LIMIT: int = int(os.getenv("EMBEDDING_BATCH_LIMIT", "50000"))
# Thumbnail generation
THUMBNAIL_DB_TIMEOUT: float = float(os.getenv("THUMBNAIL_DB_TIMEOUT", "30.0"))
THUMBNAIL_SIZE: tuple = (300, 300)
# Video proxy configuration
PROXY_FILE_CACHE_DURATION: int = int(os.getenv("PROXY_FILE_CACHE_DURATION", "300"))
# Redis cache configuration
REDIS_HOST: str = os.getenv("REDIS_HOST", "127.0.0.1")
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
REDIS_TTL: int = int(os.getenv("REDIS_TTL", "300"))
# API configuration
API_VERSION: str = "13.13.1"
API_TITLE: str = "Media Downloader API"
API_DESCRIPTION: str = "Web API for managing media downloads"
# CORS configuration
ALLOWED_ORIGINS: list = os.getenv(
"ALLOWED_ORIGINS",
"http://localhost:5173,http://localhost:3000,http://127.0.0.1:5173,http://127.0.0.1:3000"
).split(",")
# Security
SECURE_COOKIES: bool = os.getenv("SECURE_COOKIES", "false").lower() == "true"
SESSION_SECRET_KEY: str = os.getenv("SESSION_SECRET_KEY", "")
CSRF_SECRET_KEY: str = os.getenv("CSRF_SECRET_KEY", "")
# Rate limiting
RATE_LIMIT_DEFAULT: str = os.getenv("RATE_LIMIT_DEFAULT", "100/minute")
RATE_LIMIT_STRICT: str = os.getenv("RATE_LIMIT_STRICT", "10/minute")
@classmethod
def get(cls, key: str, default: Any = None) -> Any:
"""Get a configuration value by key"""
return getattr(cls, key, default)
@classmethod
def get_path(cls, key: str) -> Path:
"""Get a path configuration value, ensuring it's a Path object"""
value = getattr(cls, key, None)
if value is None:
raise ValueError(f"Configuration key {key} not found")
if isinstance(value, Path):
return value
return Path(value)
@lru_cache()
def get_settings() -> Settings:
"""Get cached settings instance"""
return Settings()
# Convenience exports
settings = get_settings()