122 lines
4.4 KiB
Python
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()
|