Files
AudiOhm/backend/app/core/config.py
T
root a89c7894cf Initial commit: AudiOhm - Alternative Spotify avec streaming YouTube
Backend:
- FastAPI avec PostgreSQL et Redis
- Authentification JWT complète
- API REST pour musique, playlists, recherche
- Streaming audio via yt-dlp
- SQLAlchemy 2.0 async

Frontend:
- Flutter avec thème néon cyberpunk
- State management Riverpod
- Layout adaptatif desktop/mobile
- Lecteur audio avec mini-player

Infrastructure:
- Docker Compose (PostgreSQL + Redis)
- Scripts d'installation automatisés
- Scripts de build pour exécutables

Fichiers ajoutés:
- BUILD_CLIENT_*.bat/sh: Scripts de compilation
- BUILD_CLIENT_README.md: Documentation compilation
- CHECK_FLUTTER.sh: Vérificateur d'environnement
- requirements.txt mis à jour pour Python 3.13
- Modèles SQLAlchemy corrigés (metadata -> extra_metadata)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 20:08:36 +00:00

159 lines
4.4 KiB
Python

"""Application configuration using Pydantic Settings."""
from functools import lru_cache
from typing import Literal
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings loaded from environment variables."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)
# Application
APP_NAME: str = "AudiOhm"
APP_VERSION: str = "1.0.0"
DEBUG: bool = False
API_V1_PREFIX: str = "/api/v1"
# Server
HOST: str = "0.0.0.0"
PORT: int = 8000
# CORS
BACKEND_CORS_ORIGINS: list[str] = Field(
default=["http://localhost:3000", "http://localhost:8000"],
description="List of allowed CORS origins",
)
@field_validator("BACKEND_CORS_ORIGINS", mode="before")
@classmethod
def parse_cors_origins(cls, v: str | list[str]) -> list[str]:
"""Parse CORS origins from string or list."""
if isinstance(v, str):
return [origin.strip() for origin in v.split(",")]
return v
# Database
POSTGRES_HOST: str = "localhost"
POSTGRES_PORT: int = 5432
POSTGRES_USER: str = "spotify"
POSTGRES_PASSWORD: str = "spotify_password"
POSTGRES_DB: str = "spotify_le_2"
@property
def DATABASE_URL(self) -> str:
"""Build PostgreSQL async connection URL."""
return (
f"postgresql+asyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
f"@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
)
# Redis
REDIS_HOST: str = "localhost"
REDIS_PORT: int = 6379
REDIS_PASSWORD: str | None = None
REDIS_DB: int = 0
REDIS_URL: str | None = None
@property
def FULL_REDIS_URL(self) -> str:
"""Build Redis connection URL."""
if self.REDIS_URL:
return self.REDIS_URL
auth = f":{self.REDIS_PASSWORD}@" if self.REDIS_PASSWORD else ""
return f"redis://{auth}{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
# JWT
SECRET_KEY: str = Field(
default="change-this-secret-key-in-production",
description="Secret key for JWT token signing",
)
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
# Password hashing
PASSWORD_HASH_ALGORITHM: Literal["bcrypt"] = "bcrypt"
PASSWORD_HASH_ROUNDS: int = 12
# Storage
STORAGE_PATH: str = "./storage"
AUDIO_CACHE_PATH: str = "./storage/audio/cache"
AUDIO_PERMANENT_PATH: str = "./storage/audio/permanent"
THUMBNAILS_PATH: str = "./storage/thumbnails"
MAX_CACHE_SIZE_GB: int = 50
# YouTube
YOUTUBE_API_KEY: str | None = None
YTDLP_PATH: str = "yt-dlp"
# Spotify (for import)
SPOTIFY_CLIENT_ID: str | None = None
SPOTIFY_CLIENT_SECRET: str | None = None
SPOTIFY_REDIRECT_URI: str = "http://localhost:8000/api/v1/import/spotify/callback"
# Last.fm (for metadata)
LASTFM_API_KEY: str | None = None
LASTFM_SECRET: str | None = None
# Rate limiting
RATE_LIMIT_PER_MINUTE: int = 100
RATE_LIMIT_BURST: int = 200
# Upload
MAX_FILE_SIZE_MB: int = 100
ALLOWED_AUDIO_TYPES: list[str] = [
"audio/mpeg",
"audio/ogg",
"audio/wav",
"audio/flac",
]
# Audio processing
FFMPEG_PATH: str = "ffmpeg"
AUDIO_QUALITY: Literal["low", "medium", "high"] = "high"
BITRATE: int = 320
# Logging
LOG_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
LOG_FILE_PATH: str = "./logs"
# Pagination
DEFAULT_PAGE_SIZE: int = 20
MAX_PAGE_SIZE: int = 100
def __init__(self, **kwargs):
"""Initialize settings and create storage directories."""
super().__init__(**kwargs)
self._ensure_storage_directories()
def _ensure_storage_directories(self) -> None:
"""Create storage directories if they don't exist."""
from pathlib import Path
for path in [
self.STORAGE_PATH,
self.AUDIO_CACHE_PATH,
self.AUDIO_PERMANENT_PATH,
self.THUMBNAILS_PATH,
self.LOG_FILE_PATH,
]:
Path(path).mkdir(parents=True, exist_ok=True)
@lru_cache()
def get_settings() -> Settings:
"""Get cached settings instance."""
return Settings()
# Global settings instance
settings = get_settings()