mirror of
https://github.com/xpltdco/media-rip.git
synced 2026-04-03 02:53:58 -06:00
- Wire up get_cookie_path_for_session() in download opts — session
cookies.txt is now passed to yt-dlp as cookiefile when present
- Add YtdlpConfig with extractor_args field, configurable via
config.yaml or MEDIARIP__YTDLP__EXTRACTOR_ARGS env var
(e.g. {"youtube": {"player_client": ["web_safari"]}})
- Inject extractor_args into all three yt-dlp call sites:
_enqueue_single, _extract_info, _extract_url_info
- Enhance 403 error messages with actionable guidance directing
users to upload cookies.txt
161 lines
4.5 KiB
Python
161 lines
4.5 KiB
Python
"""Application configuration via pydantic-settings.
|
|
|
|
Loads settings from (highest → lowest priority):
|
|
1. Environment variables (prefix ``MEDIARIP``, nested delimiter ``__``)
|
|
2. YAML config file (optional — zero-config if missing)
|
|
3. Init kwargs
|
|
4. .env file
|
|
|
|
Zero-config mode: if no YAML file is provided or the file doesn't exist,
|
|
all settings fall back to sensible defaults.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel
|
|
from pydantic_settings import (
|
|
BaseSettings,
|
|
PydanticBaseSettingsSource,
|
|
SettingsConfigDict,
|
|
YamlConfigSettingsSource,
|
|
)
|
|
|
|
logger = logging.getLogger("mediarip.config")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Nested config sections
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class ServerConfig(BaseModel):
|
|
"""Core server settings."""
|
|
|
|
host: str = "0.0.0.0"
|
|
port: int = 8000
|
|
log_level: str = "info"
|
|
db_path: str = "mediarip.db"
|
|
data_dir: str = "/data"
|
|
|
|
|
|
class DownloadsConfig(BaseModel):
|
|
"""Download behaviour defaults."""
|
|
|
|
output_dir: str = "/downloads"
|
|
max_concurrent: int = 3
|
|
source_templates: dict[str, str] = {
|
|
"youtube.com": "%(uploader)s/%(title)s.%(ext)s",
|
|
"soundcloud.com": "%(uploader)s/%(title)s.%(ext)s",
|
|
"*": "%(title)s.%(ext)s",
|
|
}
|
|
default_template: str = "%(title)s.%(ext)s"
|
|
|
|
|
|
class YtdlpConfig(BaseModel):
|
|
"""yt-dlp tuning — operator-level knobs for YouTube and other extractors.
|
|
|
|
``extractor_args`` maps extractor names to dicts of arg lists, e.g.:
|
|
youtube:
|
|
player_client: ["web_safari", "android_vr"]
|
|
|
|
These are passed through to yt-dlp as ``extractor_args``.
|
|
"""
|
|
|
|
extractor_args: dict[str, dict[str, list[str]]] = {}
|
|
|
|
|
|
class SessionConfig(BaseModel):
|
|
"""Session management settings."""
|
|
|
|
mode: str = "isolated"
|
|
timeout_hours: int = 72
|
|
|
|
|
|
class PurgeConfig(BaseModel):
|
|
"""Automatic purge / cleanup settings."""
|
|
|
|
enabled: bool = True
|
|
max_age_minutes: int = 1440 # 24 hours
|
|
cron: str = "* * * * *" # every minute
|
|
privacy_mode: bool = False
|
|
privacy_retention_minutes: int = 1440 # default when privacy mode enabled
|
|
|
|
|
|
class UIConfig(BaseModel):
|
|
"""UI preferences."""
|
|
|
|
default_theme: str = "dark"
|
|
welcome_message: str = "Paste any video or audio URL. We rip it, you download it. No accounts, no tracking."
|
|
|
|
|
|
class AdminConfig(BaseModel):
|
|
"""Admin panel settings."""
|
|
|
|
enabled: bool = True
|
|
username: str = "admin"
|
|
password_hash: str = ""
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Safe YAML source — tolerates missing files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _SafeYamlSource(YamlConfigSettingsSource):
|
|
"""YAML source that returns an empty dict when the file is missing."""
|
|
|
|
def __call__(self) -> dict[str, Any]:
|
|
yaml_file = self.yaml_file_path
|
|
if yaml_file is None:
|
|
return {}
|
|
if not Path(yaml_file).is_file():
|
|
logger.debug("YAML config file not found at %s — using defaults", yaml_file)
|
|
return {}
|
|
return super().__call__()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Root config
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class AppConfig(BaseSettings):
|
|
"""Top-level application configuration.
|
|
|
|
Priority (highest wins): env vars → YAML file → init kwargs → .env file.
|
|
"""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_prefix="MEDIARIP__",
|
|
env_nested_delimiter="__",
|
|
yaml_file=None,
|
|
)
|
|
|
|
server: ServerConfig = ServerConfig()
|
|
downloads: DownloadsConfig = DownloadsConfig()
|
|
session: SessionConfig = SessionConfig()
|
|
purge: PurgeConfig = PurgeConfig()
|
|
ui: UIConfig = UIConfig()
|
|
admin: AdminConfig = AdminConfig()
|
|
ytdlp: YtdlpConfig = YtdlpConfig()
|
|
themes_dir: str = "./themes"
|
|
|
|
@classmethod
|
|
def settings_customise_sources(
|
|
cls,
|
|
settings_cls: type[BaseSettings],
|
|
init_settings: PydanticBaseSettingsSource,
|
|
env_settings: PydanticBaseSettingsSource,
|
|
dotenv_settings: PydanticBaseSettingsSource,
|
|
file_secret_settings: PydanticBaseSettingsSource,
|
|
) -> tuple[PydanticBaseSettingsSource, ...]:
|
|
return (
|
|
env_settings,
|
|
_SafeYamlSource(settings_cls),
|
|
init_settings,
|
|
dotenv_settings,
|
|
)
|