diff --git a/src/app/config/settings.py b/src/app/config/settings.py index 9911829..f9030e4 100644 --- a/src/app/config/settings.py +++ b/src/app/config/settings.py @@ -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"]), diff --git a/src/app/gui/main.py b/src/app/gui/main.py index 7b193f8..8d5a40e 100644 --- a/src/app/gui/main.py +++ b/src/app/gui/main.py @@ -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 diff --git a/src/app/gui/pages/playlists.py b/src/app/gui/pages/playlists.py index 7b83414..1987634 100644 --- a/src/app/gui/pages/playlists.py +++ b/src/app/gui/pages/playlists.py @@ -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) diff --git a/src/app/gui/pages/settings.py b/src/app/gui/pages/settings.py index a6e9027..753136b 100644 --- a/src/app/gui/pages/settings.py +++ b/src/app/gui/pages/settings.py @@ -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):