Files

114 lines
3.8 KiB
Python

"""Configuration loading and defaults."""
from __future__ import annotations
import os
import tomllib
from pathlib import Path
from typing import Any, Optional
DEFAULT_CONFIG_NAME = "yt_downloader.toml"
QUALITY_PRESETS = ("best", "1080", "720", "480", "audio")
def default_config_path() -> Path:
xdg = os.environ.get("XDG_CONFIG_HOME")
base = Path(xdg) if xdg else Path.home() / ".config"
return base / "yt-downloader" / DEFAULT_CONFIG_NAME
def _as_bool(value: Any, default: bool) -> bool:
if value is None:
return default
if isinstance(value, bool):
return value
return str(value).strip().lower() in ("1", "true", "yes", "on")
class Config:
"""Thin wrapper around the parsed TOML config."""
def __init__(self, data: dict, path: Path):
self._data = data
self.path = path
# -- download --------------------------------------------------------
@property
def out_dir(self) -> Path:
return Path(self._data.get("download", {}).get("out", "./videos"))
@property
def quality(self) -> str:
q = str(self._data.get("download", {}).get("quality", "best")).lower()
return q if q in QUALITY_PRESETS else "best"
@property
def format_override(self) -> Optional[str]:
return self._data.get("download", {}).get("format")
@property
def cookies_file(self) -> Optional[str]:
return self._data.get("download", {}).get("cookies_file")
@property
def cookies_from_browser(self) -> Optional[str]:
return self._data.get("download", {}).get("cookies_from_browser")
# -- vpn ------------------------------------------------------------
@property
def vpn_wireguard(self) -> Optional[str]:
return self._data.get("vpn", {}).get("wireguard")
@property
def vpn_namespace(self) -> Optional[str]:
return self._data.get("vpn", {}).get("namespace")
@property
def vpn_interface(self) -> Optional[str]:
return self._data.get("vpn", {}).get("interface")
# -- misc -----------------------------------------------------------
@property
def dry_run(self) -> bool:
return _as_bool(self._data.get("global", {}).get("dry_run", False), False)
def load_config(path: Optional[Path | str] = None) -> Config:
if path is None:
path = default_config_path()
path = Path(path)
if not path.exists():
raise FileNotFoundError(
f"Config not found at {path}. Run 'yt-downloader init' first."
)
with open(path, "rb") as fh:
data = tomllib.load(fh)
return Config(data, path)
def write_default_config(path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
template = """\
# yt-downloader configuration
[global]
dry_run = false # when true: plan only, touch nothing
[download]
out = "~/Videos/yt-downloader" # where videos land
quality = "best" # best | 1080 | 720 | 480 | audio
# format = "" # raw yt-dlp -f string, overrides quality
# cookies_file = "/path/to/cookies.txt" # pass your browser cookies (best
# cookies_from_browser = "firefox" # protection against bot checks;
# # e.g. chromium, firefox, safari)
[vpn]
# wireguard = "~/.config/yt-downloader/mullvad.conf"
# # optional: path to a WireGuard (Mullvad) .conf.
# # When set, `download` runs inside a dedicated
# # network namespace so ONLY yt-dlp traffic goes
# # through the VPN. Requires sudo + wireguard-tools.
# namespace = "mullvad" # optional: network namespace name
# interface = "mv0" # optional: WireGuard interface name
"""
path.write_text(template)