QFlow/qflow/config.py
2025-11-11 15:20:18 +08:00

138 lines
3.9 KiB
Python

from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
@dataclass(slots=True)
class QBittorrentConfig:
host: str
port: int
username: str
password: str
use_https: bool
@dataclass(slots=True)
class SchedulerConfig:
poll_seconds: int
safe_margin_gb: int
anchors_per_batch: int
filler_limit: int
cold_start_small_limit: int
stall_percent: float
stall_minutes: int
stall_resume_minutes: int
@dataclass(slots=True)
class UploaderConfig:
remote: str
root_template: str
threads: int
rclone_path: str
rclone_config: Optional[Path]
transfers: int
checkers: int
bwlimit_up: Optional[str]
drive_chunk_size: Optional[str]
retries: int
low_level_retries: int
ionice_class: Optional[str]
ionice_level: Optional[str]
nice_level: Optional[str]
@dataclass(slots=True)
class AppPaths:
download_dir: Path
database_path: Path
log_dir: Path
@dataclass(slots=True)
class AppConfig:
qbittorrent: QBittorrentConfig
scheduler: SchedulerConfig
uploader: UploaderConfig
paths: AppPaths
def _env(key: str, default: Optional[str] = None) -> Optional[str]:
return os.environ.get(key, default)
def _env_int(key: str, default: int) -> int:
try:
return int(_env(key, str(default)))
except (TypeError, ValueError):
return default
def _env_float(key: str, default: float) -> float:
try:
return float(_env(key, str(default)))
except (TypeError, ValueError):
return default
def _env_path(key: str, default: Path) -> Path:
raw = _env(key)
return Path(raw).expanduser() if raw else default
def load_config() -> AppConfig:
state_dir = _env_path("QFLOW_STATE_DIR", Path("./state"))
log_dir = _env_path("QFLOW_LOG_DIR", state_dir / "logs")
db_path = _env_path("QFLOW_DB_PATH", state_dir / "qflow.db")
download_dir = _env_path("QFLOW_DOWNLOAD_DIR", Path("/downloads"))
qb = QBittorrentConfig(
host=_env("QFLOW_QBIT_HOST", "http://qbittorrent:8080"),
port=_env_int("QFLOW_QBIT_PORT", 8080),
username=_env("QFLOW_QBIT_USER", "admin"),
password=_env("QFLOW_QBIT_PASS", "adminadmin"),
use_https=_env("QFLOW_QBIT_HTTPS", "false").lower() == "true",
)
scheduler = SchedulerConfig(
poll_seconds=_env_int("QFLOW_SCHED_POLL", 15),
safe_margin_gb=_env_int("QFLOW_SAFE_MARGIN_GB", 20),
anchors_per_batch=_env_int("QFLOW_ANCHORS", 1),
filler_limit=_env_int("QFLOW_FILLERS", 4),
cold_start_small_limit=_env_int("QFLOW_COLD_SMALL", 3),
stall_percent=_env_float("QFLOW_STALL_PCT", 85.0),
stall_minutes=_env_int("QFLOW_STALL_MIN", 5),
stall_resume_minutes=_env_int("QFLOW_STALL_RESUME_MIN", 2),
)
uploader = UploaderConfig(
remote=_env("QFLOW_REMOTE", "gcrypt:"),
root_template=_env("QFLOW_REMOTE_TEMPLATE", "{year}/{month:02d}"),
threads=_env_int("QFLOW_UPLOAD_THREADS", 2),
rclone_path=_env("QFLOW_RCLONE_BIN", "rclone"),
rclone_config=_env_path("QFLOW_RCLONE_CONFIG", Path("/config/rclone/rclone.conf")),
transfers=_env_int("QFLOW_RCLONE_TRANSFERS", 8),
checkers=_env_int("QFLOW_RCLONE_CHECKERS", 16),
bwlimit_up=_env("QFLOW_RCLONE_BWLIMIT", None),
drive_chunk_size=_env("QFLOW_DRIVE_CHUNK", "128M"),
retries=_env_int("QFLOW_RCLONE_RETRIES", 3),
low_level_retries=_env_int("QFLOW_RCLONE_LL_RETRIES", 10),
ionice_class=_env("QFLOW_IONICE_CLASS", "2"),
ionice_level=_env("QFLOW_IONICE_LEVEL", "7"),
nice_level=_env("QFLOW_NICE_LEVEL", "10"),
)
return AppConfig(
qbittorrent=qb,
scheduler=scheduler,
uploader=uploader,
paths=AppPaths(
download_dir=download_dir,
database_path=db_path,
log_dir=log_dir,
),
)