refactor: unified settings for cli and gui

This commit is contained in:
2026-06-19 13:45:45 +03:00
parent a7f1564581
commit b6aba1e67e
4 changed files with 48 additions and 15 deletions
+45 -10
View File
@@ -24,11 +24,45 @@ DEFAULT_CONFIG: Dict[str, Any] = {
}
def load_config(path: Path) -> Dict[str, Any]:
"""Load configuration from a JSON file."""
try:
raw = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
raise ValueError("config root must be a JSON object")
return raw
except Exception:
# Return empty dict if file doesn't exist or is invalid
return {}
def save_config(path: Path, data: Dict[str, Any]) -> None:
"""Save configuration to a JSON file."""
path.parent.mkdir(parents=True, exist_ok=True)
payload = json.dumps(data, indent=2, ensure_ascii=False) + "\n"
path.write_text(payload, encoding="utf-8")
def normalize_config(data: Dict[str, Any]) -> Dict[str, Any]:
"""Ensure basic expected shape for config dict. Keeps unknown keys intact."""
out = dict(data)
pls = out.get("playlists")
if not isinstance(pls, list):
out["playlists"] = []
return out
class Settings:
def __init__(self) -> None:
base_dir = Path("config")
base_dir.mkdir(parents=True, exist_ok=True)
self.path = (base_dir / "yt-playlist-config.json").resolve()
"""Unified configuration loader that combines file I/O and playlist merging."""
def __init__(self, config_path: Path | None = None) -> None:
if config_path is None:
base_dir = Path("config")
base_dir.mkdir(parents=True, exist_ok=True)
self.path = (base_dir / "yt-playlist-config.json").resolve()
else:
self.path = config_path.resolve()
self.data: Dict[str, Any] = dict(DEFAULT_CONFIG)
# Ensure there is always a config file at the default path.
@@ -38,13 +72,13 @@ class Settings:
self._load_from_path(self.path)
def _load_from_path(self, path: Path) -> None:
try:
self.data.update(json.loads(path.read_text(encoding="utf-8")))
except Exception:
# Leave defaults if invalid JSON; validation can be added later.
pass
"""Load and merge config from file."""
loaded = load_config(path)
if loaded:
self.data.update(normalize_config(loaded))
def _write_default_config(self, path: Path) -> None:
"""Write a default config file."""
path.parent.mkdir(parents=True, exist_ok=True)
default_payload: Dict[str, Any] = {
"playlists": [
@@ -57,10 +91,11 @@ class Settings:
],
"ffmpeg_path": _default_ffmpeg_path(),
}
path.write_text(json.dumps(default_payload, indent=2) + "\n", encoding="utf-8")
save_config(path, default_payload)
@property
def playlists(self) -> List[Dict[str, Any]]:
"""Get playlists with global defaults merged in."""
global_defaults = {
"download_mode": self.data.get("download_mode", DEFAULT_CONFIG["download_mode"]),
"max_download_quality": self.data.get("max_download_quality", DEFAULT_CONFIG["max_download_quality"]),
+1 -2
View File
@@ -5,11 +5,10 @@ import threading
from PySide6 import QtCore, QtGui, QtWidgets
from ..config.settings import Settings
from ..config.settings import Settings, load_config
from ..core.events.event_bus import EventBus
from .bus_bridge import BusBridge
from .app_icon import load_app_icon
from .config_store import load_config
from .runner import SyncRequest, SyncRunner
from .pages.playlists import PlaylistManagerPage
from .pages.queue import QueuePage
+1 -2
View File
@@ -6,11 +6,10 @@ from pathlib import Path
from PySide6 import QtCore, QtGui, QtWidgets
from ...config.settings import Settings
from ...config.settings import Settings, load_config, normalize_config, save_config
from ...core.database.db import Database
from ...core.utils.yt import extract_playlist_id
from ..smooth_scroll import enable_smooth_scrolling
from ..config_store import load_config, normalize_config, save_config
@dataclass(frozen=True)
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Any
from PySide6 import QtCore, QtWidgets
from ..config_store import load_config, save_config
from ..config.settings import load_config, save_config
class SettingsPage(QtWidgets.QWidget):