1
0
mirror of https://github.com/darkzoul5/YoutubePlaylistSync.git synced 2026-07-03 04:23:59 +03:00
Files
YoutubePlaylistSync/src/app/gui/pages/settings.py
T

166 lines
7.0 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Any
from PySide6 import QtCore, QtWidgets
from ..config_store import load_config, save_config
class SettingsPage(QtWidgets.QWidget):
def __init__(self, parent: QtWidgets.QWidget | None = None) -> None:
super().__init__(parent)
self.setObjectName("settingsPage")
self._config_path: Path | None = None
self._config: dict[str, Any] = {}
layout = QtWidgets.QVBoxLayout(self)
title = QtWidgets.QLabel("Settings")
title.setObjectName("pageTitle")
layout.addWidget(title)
form = QtWidgets.QFormLayout()
self._ffmpeg_path = QtWidgets.QLineEdit()
self._ffmpeg_path.setPlaceholderText("./bin/ffmpeg.exe (Windows) or ./bin/ffmpeg (Linux)")
form.addRow("ffmpeg_path", self._ffmpeg_path)
self._max_parallel = QtWidgets.QSpinBox()
self._max_parallel.setRange(1, 64)
form.addRow("max_parallel_downloads", self._max_parallel)
self._retry_max = QtWidgets.QSpinBox()
self._retry_max.setRange(0, 20)
form.addRow("retry_max_retries", self._retry_max)
self._retry_delay = QtWidgets.QDoubleSpinBox()
self._retry_delay.setRange(0.0, 60.0)
self._retry_delay.setDecimals(2)
self._retry_delay.setSingleStep(0.25)
form.addRow("retry_delay_seconds", self._retry_delay)
self._download_delay = QtWidgets.QDoubleSpinBox()
self._download_delay.setRange(0.0, 600.0)
self._download_delay.setDecimals(2)
self._download_delay.setSingleStep(0.25)
form.addRow("delay_between_downloads_seconds", self._download_delay)
form_box = QtWidgets.QGroupBox("Global defaults")
form_box.setLayout(form)
layout.addWidget(form_box)
tray_form = QtWidgets.QFormLayout()
self._close_to_tray = QtWidgets.QCheckBox()
self._close_to_tray.setChecked(False)
tray_form.addRow("close_to_tray", self._close_to_tray)
self._minimize_to_tray = QtWidgets.QCheckBox()
self._minimize_to_tray.setChecked(False)
tray_form.addRow("minimize_to_tray", self._minimize_to_tray)
self._start_minimized_to_tray = QtWidgets.QCheckBox()
self._start_minimized_to_tray.setChecked(False)
tray_form.addRow("start_minimized_to_tray", self._start_minimized_to_tray)
tray_box = QtWidgets.QGroupBox("Tray behavior")
tray_box.setLayout(tray_form)
layout.addWidget(tray_box)
btns = QtWidgets.QHBoxLayout()
self._reload_btn = QtWidgets.QPushButton("Reload")
self._reload_btn.clicked.connect(self.reload_from_config)
self._save_btn = QtWidgets.QPushButton("Save")
self._save_btn.clicked.connect(self.save_to_config)
btns.addStretch(1)
btns.addWidget(self._reload_btn)
btns.addWidget(self._save_btn)
layout.addLayout(btns)
self._status = QtWidgets.QLabel("")
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)
# Autosave on focus-out / change.
self._ffmpeg_path.editingFinished.connect(self._schedule_autosave)
self._max_parallel.valueChanged.connect(lambda _v: self._schedule_autosave())
self._retry_max.valueChanged.connect(lambda _v: self._schedule_autosave())
self._retry_delay.valueChanged.connect(lambda _v: self._schedule_autosave())
self._download_delay.valueChanged.connect(lambda _v: self._schedule_autosave())
self._close_to_tray.stateChanged.connect(lambda _v: self._schedule_autosave())
self._minimize_to_tray.stateChanged.connect(lambda _v: self._schedule_autosave())
self._start_minimized_to_tray.stateChanged.connect(lambda _v: self._schedule_autosave())
def set_config_path(self, path: Path) -> None:
self._config_path = path
self.reload_from_config()
@QtCore.Slot()
def reload_from_config(self) -> None:
if self._config_path is None:
self._status.setText("No config loaded yet.")
return
try:
self._suppress_autosave = True
cfg = load_config(self._config_path)
self._config = dict(cfg.data)
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)))
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()
@QtCore.Slot()
def save_to_config(self) -> None:
if self._config_path is None:
self._status.setText("No config loaded yet.")
return
try:
data = dict(self._config or {})
data["ffmpeg_path"] = self._ffmpeg_path.text().strip() or data.get("ffmpeg_path")
data["max_parallel_downloads"] = int(self._max_parallel.value())
data["retry_max_retries"] = int(self._retry_max.value())
data["retry_delay_seconds"] = float(self._retry_delay.value())
data["delay_between_downloads_seconds"] = float(self._download_delay.value())
ui = data.get("ui")
ui = ui if isinstance(ui, dict) else {}
tray = ui.get("tray")
tray = tray if isinstance(tray, dict) else {}
tray["close_to_tray"] = bool(self._close_to_tray.isChecked())
tray["minimize_to_tray"] = bool(self._minimize_to_tray.isChecked())
tray["start_minimized_to_tray"] = bool(self._start_minimized_to_tray.isChecked())
ui["tray"] = tray
data["ui"] = ui
save_config(self._config_path, data)
self._status.setText(f"Saved settings to {self._config_path}.")
except Exception as exc:
self._status.setText(f"Failed to save settings: {exc}")