From 83200c493265619e3079b7cac7f02cd75943ae3e Mon Sep 17 00:00:00 2001 From: Xarkam Date: Fri, 13 Mar 2026 10:59:05 +0100 Subject: [PATCH] =?UTF-8?q?Intr=C3=A9gration=20du=20module=20de=20configur?= =?UTF-8?q?ation=20pour=20ici=20sauvergarder=20le=20volume=20audio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/configure.py | 126 ++++++++++++++++++++++++++++++++++++++++ src/mainwindow.py | 14 ++++- 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/config/configure.py diff --git a/src/config/configure.py b/src/config/configure.py new file mode 100644 index 0000000..89c6a8f --- /dev/null +++ b/src/config/configure.py @@ -0,0 +1,126 @@ +import json +import os +from pathlib import Path +from sys import argv, executable +from typing import Any, Callable, NotRequired, TypedDict, cast + +# Configuration du chemin du fichier de configuration avec son nom. +def _get_config_path() -> Path: + base_path = Path(executable if frozenset else os.path.realpath(argv[0])).parent.absolute() + #return base_path / "config.json" + return Path(os.path.realpath(argv[0])).parent.absolute() / "config.json" + +class ConfigData(TypedDict): + discord_user_id: NotRequired[str] + volume: NotRequired[int] + +Validator = Callable[[Any], bool] +Normalizer = Callable[[Any], Any] + +class ConfigField(TypedDict): + default: Any + validator: Validator + normalizer: Normalizer + +CONFIG_PATH = _get_config_path() + +DISCORD_USER_KEY = "discord_user_id" +VOLUME_KEY = "volume" + +CONFIG_SCHEMA: dict[str, ConfigField] = { + DISCORD_USER_KEY: { + "default": "", + "validator": lambda value: isinstance(value, str), + "normalizer": lambda value: str(value).strip(), + }, + VOLUME_KEY: { + "default": 25, + "validator": lambda value: isinstance(value, int) and 0 <= value <= 100, + "normalizer": lambda value: max(0, min(int(value), 100)), + }, +} + +class ConfigManager: + def __init__(self, path: Path = CONFIG_PATH) -> None: + self.path = path + + # Lecture du fichier de configuration + def load(self) -> ConfigData: + if not self.path.exists(): + return {} + + try: + with self.path.open("r", encoding="utf-8") as file: + data = json.load(file) + except (json.JSONDecodeError, OSError): + return {} + + if not isinstance(data, dict): + return {} + + return cast(ConfigData, data) + + # Sauvegarde du fichier de configuration + def save(self, data: ConfigData) -> None: + self.path.parent.mkdir(parents=True, exist_ok=True) + with self.path.open("w", encoding="utf-8") as file: + json.dump(data, file, indent=4, ensure_ascii=False) + + def _get_field(self, key: str) -> ConfigField: + if key not in CONFIG_SCHEMA: + raise KeyError(f"Unknown config key: {key}") + return CONFIG_SCHEMA[key] + + def get(self, key: str) -> Any: + field = self._get_field(key) + data = self.load() + value = data.get(key, field["default"]) + + if not field["validator"](value): + return field["default"] + + return value + + def set(self, key: str, value: Any) -> None: + field = self._get_field(key) + normalized_value = field["normalizer"](value) + + if not field["validator"](normalized_value): + raise ValueError(f"Invalid value for config key: {key}") + + data = self.load() + data[key] = normalized_value + self.save(data) + + def reset_key(self, key: str) -> None: + field = self._get_field(key) + data = self.load() + data[key] = field["default"] + self.save(data) + + def reset_all(self) -> None: + defaults: ConfigData = cast( + ConfigData, + {key: field["default"] for key, field in CONFIG_SCHEMA.items()}, + ) + self.save(defaults) + + def get_all(self) -> ConfigData: + return cast( + ConfigData, + {key: self.get(key) for key in CONFIG_SCHEMA}, + ) + + # --------------- SETTERS MÉTIER ------------------------------- + def set_discord_user(self, user_id: str) -> None: + self.set(DISCORD_USER_KEY, user_id) + + def set_volume(self, volume: int) -> None: + self.set(VOLUME_KEY, volume) + + # --------------- GETTERS MÉTIER ------------------------------- + def get_discord_user(self) -> str: + return cast(str, self.get(DISCORD_USER_KEY)) + + def get_volume(self) -> int: + return cast(int, self.get(VOLUME_KEY)) diff --git a/src/mainwindow.py b/src/mainwindow.py index 64e24c6..7cc538d 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -9,9 +9,12 @@ from PySide6.QtUiTools import QUiLoader from PySide6.QtWidgets import QMainWindow, QApplication, QGraphicsDropShadowEffect from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput +from config.configure import ConfigManager + # Compile resources.qrc into resources_rc.py # rcc -g python .\resources.qrc -o .\src\resources_rc.py +# import utilisé pour la font custom import resources as resources # This is generated from the .qrc file # noqa: F401 # Remove this into final release @@ -73,8 +76,10 @@ class MainWindow(QMainWindow): self.media_player.setSourceDevice(self.mp3buffer) # Définir la valeur initiale du slider (ici: 10%) - self.ui.audio_volume_adjust.setValue(10) - self.audio_output.setVolume(0.1) + #self.ui.audio_volume_adjust.setValue(10) + self.ui.audio_volume_adjust.setValue(config.get_volume()) + #self.audio_output.setVolume(0.1) + self.audio_output.setVolume(config.get_volume() / 100) # Initialisation de la mémoire du volume (par défaut 10%) self.previous_volume = 10 @@ -181,6 +186,7 @@ class MainWindow(QMainWindow): # On convertit en float pour QAudioOutput (0.0 à 1.0) volume = value / 100.0 self.audio_output.setVolume(volume) + config.set_volume(value) def mute_btn_link(self) -> None: if not self.is_muted: @@ -196,6 +202,7 @@ class MainWindow(QMainWindow): self.ui.mute_btn.setStyleSheet("background-color: red;") self.is_muted = True + config.set_volume(0) else: # --- RETOUR DU SON --- # On restaure le volume précédent @@ -207,8 +214,11 @@ class MainWindow(QMainWindow): self.ui.mute_btn.setStyleSheet("") self.is_muted = False + config.set_volume(self.previous_volume) if __name__ == "__main__": + # Initialisation de la configuration + config = ConfigManager() app = QApplication(sys.argv) with open(f"{bundle_dir}/styles/styles.qss", 'r') as f: