refactor: add shared autosave helper

This commit is contained in:
2026-06-19 14:42:27 +03:00
parent 373a19510e
commit afebc35166
3 changed files with 66 additions and 46 deletions
+38
View File
@@ -0,0 +1,38 @@
from __future__ import annotations
from contextlib import contextmanager
from typing import Callable, Iterator
from PySide6 import QtCore
class DebouncedAutosave(QtCore.QObject):
"""Small helper for debounced autosave flows in Qt widgets."""
def __init__(self, parent: QtCore.QObject, callback: Callable[[], None], interval_ms: int = 600) -> None:
super().__init__(parent)
self._suppressed = False
self._timer = QtCore.QTimer(self)
self._timer.setSingleShot(True)
self._timer.setInterval(interval_ms)
self._timer.timeout.connect(callback)
@contextmanager
def suppressed(self) -> Iterator[None]:
previous = self._suppressed
self._suppressed = True
try:
yield
finally:
self._suppressed = previous
def set_suppressed(self, suppressed: bool) -> None:
self._suppressed = bool(suppressed)
def schedule(self, *, enabled: bool = True) -> None:
if self._suppressed or not enabled:
return
self._timer.start()
def stop(self) -> None:
self._timer.stop()
+10 -21
View File
@@ -9,6 +9,7 @@ from PySide6 import QtCore, QtGui, QtWidgets
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 ..autosave import DebouncedAutosave
from ..smooth_scroll import enable_smooth_scrolling
@@ -40,11 +41,7 @@ class PlaylistManagerPage(QtWidgets.QWidget):
self._config_path = getattr(settings, "path", None)
self._config: dict[str, Any] = {}
self._download_state_by_pid: dict[str, dict[str, Any]] = {}
self._suppress_autosave = False
self._autosave_timer = QtCore.QTimer(self)
self._autosave_timer.setSingleShot(True)
self._autosave_timer.setInterval(600)
self._autosave_timer.timeout.connect(self._autosave_now)
self._autosave = DebouncedAutosave(self, self._autosave_now)
header = QtWidgets.QLabel("Playlists")
header.setObjectName("pageTitle")
@@ -129,18 +126,16 @@ class PlaylistManagerPage(QtWidgets.QWidget):
@QtCore.Slot()
def reload_from_config(self) -> None:
try:
self._suppress_autosave = True
self._settings = Settings()
self._config_path = getattr(self._settings, "path", None)
if self._config_path is None:
raise RuntimeError("Config path not available")
self._config = normalize_config(load_config(self._config_path))
rows = self._rows_from_settings()
with self._autosave.suppressed():
self._settings = Settings()
self._config_path = getattr(self._settings, "path", None)
if self._config_path is None:
raise RuntimeError("Config path not available")
self._config = normalize_config(load_config(self._config_path))
rows = self._rows_from_settings()
except Exception as exc:
self._status.setText(f"Failed to load config: {exc}")
return
finally:
self._suppress_autosave = False
# Optional DB metadata (last_sync). If DB is missing/corrupt, keep UI usable.
last_sync_by_id: dict[str, str] = {}
@@ -281,18 +276,12 @@ class PlaylistManagerPage(QtWidgets.QWidget):
@QtCore.Slot()
def _schedule_autosave(self) -> None:
if self._suppress_autosave:
return
if not self.isEnabled():
return
self._autosave_timer.start()
self._autosave.schedule(enabled=self.isEnabled())
@QtCore.Slot()
def _autosave_now(self) -> None:
if self._config_path is None:
return
if self._suppress_autosave:
return
if not self._validate_all(show_status=False):
# Don't autosave invalid configs; user sees inline errors.
return
+18 -25
View File
@@ -6,6 +6,7 @@ from typing import Any
from PySide6 import QtCore, QtWidgets
from ...config.settings import load_config, save_config
from ..autosave import DebouncedAutosave
class SettingsPage(QtWidgets.QWidget):
@@ -81,11 +82,7 @@ class SettingsPage(QtWidgets.QWidget):
self._status.setWordWrap(True)
layout.addWidget(self._status)
self._suppress_autosave = False
self._autosave_timer = QtCore.QTimer(self)
self._autosave_timer.setSingleShot(True)
self._autosave_timer.setInterval(600)
self._autosave_timer.timeout.connect(self.save_to_config)
self._autosave = DebouncedAutosave(self, self.save_to_config)
# Autosave on focus-out / change.
self._ffmpeg_path.editingFinished.connect(self._schedule_autosave)
@@ -107,34 +104,30 @@ class SettingsPage(QtWidgets.QWidget):
self._status.setText("No config loaded yet.")
return
try:
self._suppress_autosave = True
cfg = load_config(self._config_path)
self._config = dict(cfg)
with self._autosave.suppressed():
cfg = load_config(self._config_path)
self._config = dict(cfg)
self._ffmpeg_path.setText(str(self._config.get("ffmpeg_path") or ""))
self._max_parallel.setValue(int(self._config.get("max_parallel_downloads") or 2))
self._retry_max.setValue(int(self._config.get("retry_max_retries") or 2))
self._retry_delay.setValue(float(self._config.get("retry_delay_seconds") or 1.5))
self._download_delay.setValue(float(self._config.get("delay_between_downloads_seconds") or 0.0))
self._ffmpeg_path.setText(str(self._config.get("ffmpeg_path") or ""))
self._max_parallel.setValue(int(self._config.get("max_parallel_downloads") or 2))
self._retry_max.setValue(int(self._config.get("retry_max_retries") or 2))
self._retry_delay.setValue(float(self._config.get("retry_delay_seconds") or 1.5))
self._download_delay.setValue(float(self._config.get("delay_between_downloads_seconds") or 0.0))
ui = self._config.get("ui")
ui = ui if isinstance(ui, dict) else {}
tray = ui.get("tray")
tray = tray if isinstance(tray, dict) else {}
self._close_to_tray.setChecked(bool(tray.get("close_to_tray", False)))
self._minimize_to_tray.setChecked(bool(tray.get("minimize_to_tray", False)))
self._start_minimized_to_tray.setChecked(bool(tray.get("start_minimized_to_tray", False)))
ui = self._config.get("ui")
ui = ui if isinstance(ui, dict) else {}
tray = ui.get("tray")
tray = tray if isinstance(tray, dict) else {}
self._close_to_tray.setChecked(bool(tray.get("close_to_tray", False)))
self._minimize_to_tray.setChecked(bool(tray.get("minimize_to_tray", False)))
self._start_minimized_to_tray.setChecked(bool(tray.get("start_minimized_to_tray", False)))
self._status.setText(f"Loaded settings from {self._config_path}.")
except Exception as exc:
self._status.setText(f"Failed to load settings: {exc}")
finally:
self._suppress_autosave = False
def _schedule_autosave(self) -> None:
if self._suppress_autosave:
return
self._autosave_timer.start()
self._autosave.schedule()
@QtCore.Slot()
def save_to_config(self) -> None: