mirror of
https://github.com/darkzoul5/YoutubePlaylistSync.git
synced 2026-09-18 20:43:54 +03:00
refactor: add shared autosave helper
This commit is contained in:
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user