From 6a70895243f104303b8ad198c300a884a0a6ca80 Mon Sep 17 00:00:00 2001 From: Nell Date: Sat, 19 Apr 2025 14:51:11 +0200 Subject: [PATCH] Init --- main.py | 171 +++++++++++++++++++++++++++++++++++++---- src/handler.py | 55 ++++++++++++- windows/site_window.py | 71 +++++++++++++++-- 3 files changed, 276 insertions(+), 21 deletions(-) diff --git a/main.py b/main.py index b7bec4b..afdbed6 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ from PySide6.QtCore import QStandardPaths, QDataStream, QByteArray, QIODevice, Signal, Qt, QTimer, QCryptographicHash +from PySide6.QtGui import QPalette, QColor from PySide6.QtNetwork import QLocalServer, QLocalSocket from PySide6.QtWidgets import QApplication @@ -12,117 +13,230 @@ import hashlib import random import string import base64 +import logging from pathlib import Path from src.logs import configure_logging from windows.main_window import MainWindow +def apply_dark_theme(app): + app.setStyle("Fusion") + + dark_palette = QPalette() + + # Utilisation de gris plus clairs pour les principaux éléments + dark_palette.setColor(QPalette.ColorRole.Window, QColor(90, 90, 95)) # Barre de titre plus claire + dark_palette.setColor(QPalette.ColorRole.WindowText, QColor(255, 255, 255)) + dark_palette.setColor(QPalette.ColorRole.Base, QColor(60, 60, 65)) # Zone de texte plus claire + dark_palette.setColor(QPalette.ColorRole.AlternateBase, QColor(80, 80, 85)) + dark_palette.setColor(QPalette.ColorRole.ToolTipBase, QColor(70, 70, 75)) + dark_palette.setColor(QPalette.ColorRole.ToolTipText, QColor(255, 255, 255)) + dark_palette.setColor(QPalette.ColorRole.Text, QColor(255, 255, 255)) + dark_palette.setColor(QPalette.ColorRole.Button, QColor(75, 75, 80)) # Boutons plus clairs + dark_palette.setColor(QPalette.ColorRole.ButtonText, QColor(255, 255, 255)) + dark_palette.setColor(QPalette.ColorRole.BrightText, QColor(255, 0, 0)) + + # Les autres paramètres restent inchangés + dark_palette.setColor(QPalette.ColorRole.Highlight, QColor(61, 142, 201)) + dark_palette.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255)) + dark_palette.setColor(QPalette.ColorRole.Link, QColor(77, 166, 230)) + dark_palette.setColor(QPalette.ColorRole.LinkVisited, QColor(120, 120, 250)) + dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, QColor(150, 150, 150)) + dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(150, 150, 150)) + dark_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(150, 150, 150)) + + app.setPalette(dark_palette) + + class SingleApplication(QApplication): # Signal émis lorsque des fichiers sont reçus d'une instance secondaire files_received = Signal(list) def __init__(self, app_id, args): super().__init__(args) + + # Configuration du logger + self.logger = logging.getLogger(__name__) + self.logger.debug("Initialisation de SingleApplication") + self.app_id = app_id + self.logger.debug(f"ID de l'application: {app_id}") + self.shared_key = hashlib.sha256(app_id.encode()).hexdigest()[:16] + self.logger.debug(f"Clé partagée générée: {self.shared_key}") + self.server = None self.is_primary_instance = self.try_connect_to_primary() if self.is_primary_instance: # C'est la première instance, on crée un serveur local + self.logger.info("Instance primaire détectée, création du serveur local") self.server = QLocalServer() self.server.newConnection.connect(self.handle_new_connection) + self.logger.debug("Signal newConnection connecté") + if not self.server.listen(self.app_id): + self.logger.warning(f"Échec de l'écoute sur {self.app_id}, tentative de suppression du serveur existant") # En cas d'erreur (serveur déjà existant mais zombie), on le supprime et on réessaie QLocalServer.removeServer(self.app_id) - self.server.listen(self.app_id) + if self.server.listen(self.app_id): + self.logger.info(f"Serveur local créé avec succès après suppression de l'ancien") + else: + self.logger.error(f"Impossible de créer le serveur local même après suppression") + else: + self.logger.info(f"Serveur local créé avec succès") else: + self.logger.info("Instance secondaire détectée, fermeture de l'application") QTimer.singleShot(0, self.quit) def encrypt_data(self, data_str): - """Méthode simple pour brouiller les données""" + """ + Méthode simple pour brouiller les données + + Args: + data_str (str): Données à chiffrer + + Returns: + str: Données chiffrées en base64 + """ + self.logger.debug(f"Chiffrement des données (longueur: {len(data_str)})") + # Générer une "nonce" aléatoire pour éviter que les mêmes données produisent le même résultat nonce = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8)) + self.logger.debug(f"Nonce générée: {nonce}") # Combiner la nonce, la clé et les données combined = nonce + self.shared_key + data_str + self.logger.debug("Données combinées avec nonce et clé partagée") # Utiliser SHA-256 pour obtenir un hash hash_obj = QCryptographicHash(QCryptographicHash.Algorithm.Sha256) hash_obj.addData(combined.encode()) signature = hash_obj.result().toHex().data().decode()[:16] + self.logger.debug(f"Signature générée: {signature}") # Encoder le tout en base64 encoded = base64.b64encode((nonce + signature + data_str).encode()).decode() + self.logger.debug(f"Données encodées en base64 (longueur: {len(encoded)})") return encoded def decrypt_data(self, encoded_str): - """Déchiffre les données et vérifie leur intégrité""" + """ + Déchiffre les données et vérifie leur intégrité + + Args: + encoded_str (str): Données chiffrées en base64 + + Returns: + str ou None: Données déchiffrées ou None en cas d'erreur + """ + self.logger.debug(f"Déchiffrement des données (longueur: {len(encoded_str)})") + try: # Décoder de base64 decoded = base64.b64decode(encoded_str.encode()).decode() + self.logger.debug("Données décodées de base64") # Extraire nonce, signature et données nonce = decoded[:8] signature = decoded[8:24] data_str = decoded[24:] + self.logger.debug(f"Nonce extraite: {nonce}, signature: {signature}") # Vérifier la signature combined = nonce + self.shared_key + data_str hash_obj = QCryptographicHash(QCryptographicHash.Algorithm.Sha256) hash_obj.addData(combined.encode()) expected_signature = hash_obj.result().toHex().data().decode()[:16] + self.logger.debug(f"Signature attendue: {expected_signature}") if signature != expected_signature: - print("Signature invalide, données potentiellement corrompues ou falsifiées") + self.logger.warning("Signature invalide, données potentiellement corrompues ou falsifiées") return None + self.logger.debug(f"Données déchiffrées avec succès (longueur: {len(data_str)})") return data_str except Exception as e: - print(f"Erreur lors du déchiffrement: {e}") + self.logger.error(f"Erreur lors du déchiffrement: {e}", exc_info=True) return None def try_connect_to_primary(self): - """Essaie de se connecter à l'instance primaire de l'application""" + """ + Essaie de se connecter à l'instance primaire de l'application + + Returns: + bool: True si c'est l'instance primaire, False sinon + """ + self.logger.debug(f"Tentative de connexion à l'instance primaire: {self.app_id}") socket = QLocalSocket() socket.connectToServer(self.app_id, QIODevice.OpenModeFlag.WriteOnly) if socket.waitForConnected(500): + self.logger.info("Connexion établie avec l'instance primaire") + # Récupérer les arguments pour les envoyer à l'instance primaire args = sys.argv[1:] if len(sys.argv) > 1 else [] + self.logger.debug(f"Arguments à transmettre: {args}") + encrypt_args = self.encrypt_data(";".join(args)) + self.logger.debug("Arguments chiffrés pour transmission") # Envoyer les arguments à l'instance primaire stream = QDataStream(socket) stream.writeQString(encrypt_args) socket.flush() - socket.waitForBytesWritten(1000) + self.logger.debug("Données envoyées à l'instance primaire") + + if socket.waitForBytesWritten(1000): + self.logger.debug("Données écrites avec succès") + else: + self.logger.warning("Délai d'attente dépassé pour l'écriture des données") + socket.disconnectFromServer() + self.logger.debug("Déconnexion du serveur") QTimer.singleShot(0, self.quit) + self.logger.info("Instance secondaire, programmation de la fermeture") return False # Ce n'est pas l'instance primaire + + self.logger.info("Aucune instance primaire trouvée, devenant l'instance primaire") return True # C'est l'instance primaire def handle_new_connection(self): - """Gère une nouvelle connexion d'une instance secondaire""" + """ + Gère une nouvelle connexion d'une instance secondaire + """ + self.logger.info("Nouvelle connexion d'une instance secondaire reçue") socket = self.server.nextPendingConnection() if socket.waitForReadyRead(2000): + self.logger.debug("Données disponibles pour lecture") stream = QDataStream(socket) encrypted_args = stream.readQString() - args_str = self.decrypt_data(encrypted_args) + self.logger.debug(f"Arguments chiffrés reçus (longueur: {len(encrypted_args)})") - # Émettre un signal pour informer l'application des fichiers à ouvrir + args_str = self.decrypt_data(encrypted_args) if args_str: + self.logger.debug(f"Arguments déchiffrés: {args_str}") + + # Émettre un signal pour informer l'application des fichiers à ouvrir args = args_str.split(";") if args_str else [] if args: + self.logger.info(f"Émission du signal files_received avec {len(args)} arguments") self.files_received.emit(args) + else: + self.logger.debug("Aucun argument à traiter") + else: + self.logger.warning("Échec du déchiffrement des arguments") + else: + self.logger.warning("Délai d'attente dépassé pour la lecture des données") socket.disconnectFromServer() + self.logger.debug("Déconnexion du client") if __name__ == "__main__": @@ -135,28 +249,59 @@ if __name__ == "__main__": # Configurer le logging en fonction du mode configure_logging(args.dev) + # Créer un logger pour le module principal + logger = logging.getLogger(__name__) + logger.info("Démarrage de l'application") + + # Configuration des variables d'environnement pour QtWebEngine os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-gpu-rasterization --ignore-gpu-blocklist" + logger.debug("Flags Chromium configurés pour l'accélération GPU") + if args.dev: os.environ["QTWEBENGINE_REMOTE_DEBUGGING"] = "4000" + logger.info("Mode développement activé avec débogage distant sur le port 4000") + # Création de l'application app_id = "OxAPP25" + logger.debug(f"ID de l'application: {app_id}") + app = SingleApplication(app_id, sys.argv) + apply_dark_theme(app) + logger.debug("Instance SingleApplication créée") if not app.is_primary_instance: + logger.info("Instance secondaire détectée, fermeture de l'application") sys.exit(0) + # Configuration de la boucle d'événements asyncio + logger.debug("Configuration de la boucle d'événements asyncio") event_loop = qasync.QEventLoop(app) asyncio.set_event_loop(event_loop) app_close_event = asyncio.Event() app.aboutToQuit.connect(app_close_event.set) + logger.debug("Signal aboutToQuit connecté à l'événement de fermeture") + # Création de la fenêtre principale + logger.info("Création de la fenêtre principale") window = MainWindow() + # Connecter le signal de fichiers reçus à une méthode de traitement app.files_received.connect(window.handle_files) - if args.files: - window.handle_files(args.files) - window.show() + logger.debug("Signal files_received connecté") + # Traitement des fichiers passés en arguments + if args.files: + logger.info(f"Traitement de {len(args.files)} fichiers passés en arguments") + window.handle_files(args.files) + + # Affichage de la fenêtre + window.show() + logger.info("Fenêtre principale affichée") + + # Exécution de la boucle d'événements + logger.debug("Démarrage de la boucle d'événements") with event_loop: event_loop.run_until_complete(app_close_event.wait()) + + logger.info("Application terminée") diff --git a/src/handler.py b/src/handler.py index e2abf79..0da10b4 100644 --- a/src/handler.py +++ b/src/handler.py @@ -1,4 +1,5 @@ import asyncio +import logging from PySide6.QtCore import QObject, Slot, Signal, QTimer, Property @@ -16,78 +17,125 @@ class WebHandler(QObject): def __init__(self, download_manager, parent=None): super().__init__(parent) + + # Configuration du logger + self.logger = logging.getLogger(__name__) + self.logger.debug("Initialisation de WebHandler") + self.download_manager: "DownloadManager" = download_manager self.conf = self.download_manager.conf + self.logger.debug("Références au gestionnaire de téléchargement et à la configuration établies") + self.site_loaded = False + # Connexion des signaux du gestionnaire de téléchargement self.download_manager.status_updated.connect(lambda data: self.on_message.emit({ "context": "status_updated", "content": data })) + self.logger.debug("Signal status_updated connecté") # self.download_manager.stats_updated.connect(lambda data: self.on_message.emit({ # "context": "stats_updated", # "content": data # })) + # self.logger.debug("Signal stats_updated connecté") self.download_manager.files_updated.connect(lambda: self.on_message.emit({ "context": "files_updated", "content": {file_id: vars(file) for file_id, file in self.download_manager.files.items()} })) + self.logger.debug("Signal files_updated connecté") @Property(dict) def dm_status(self): + self.logger.debug("Accès à la propriété dm_status") return self.download_manager.status @Property(dict) def dm_files(self): + self.logger.debug("Accès à la propriété dm_files") return {file_id: vars(file) for file_id, file in self.download_manager.files.items()} # @Property(dict) # def dm_stats(self): + # self.logger.debug("Accès à la propriété dm_stats") # return self.download_manager.dl_stats @Property(str) def dm_download_location(self): - return self.conf.get_value("download_location", "") + self.logger.debug("Accès à la propriété dm_download_location") + location = self.conf.get_value("download_location", "") + return location @Slot(result=str) def on_site_ready(self): + """Appelé lorsque le site est prêt à interagir""" + self.logger.info("Site signalé comme prêt") self.site_loaded = True self.site_ready.emit() + self.logger.debug("Signal site_ready émis") @Slot(bool) def set_pause(self, value): + """Définit l'état de pause du gestionnaire de téléchargement""" + self.logger.info(f"Demande de changement d'état de pause: {value}") asyncio.create_task(self.download_manager.set_pause(value)) + self.logger.debug("Tâche asynchrone de changement d'état créée") @Slot(list) @Slot(str) def add_files(self, files): + """Ajoute des fichiers au gestionnaire de téléchargement""" + self.logger.info(f"Demande d'ajout de fichiers reçue: type={type(files)}") + if isinstance(files, str): + self.logger.debug("Conversion de la chaîne JSON en liste") files = json.loads(files) + if not isinstance(files, list): + self.logger.debug("Conversion de l'élément unique en liste") files = [files] + + self.logger.info(f"Ajout de {len(files)} fichier(s) au gestionnaire de téléchargement") self.download_manager.add_files(files) @Slot(list) def del_files(self, file_ids): + """Supprime des fichiers du gestionnaire de téléchargement""" + self.logger.info(f"Demande de suppression de fichiers reçue: {file_ids}") + if isinstance(file_ids, str): + self.logger.debug("Conversion de l'identifiant unique en liste") file_ids = [file_ids] + + self.logger.info(f"Suppression de {len(file_ids)} fichier(s) du gestionnaire de téléchargement") self.download_manager.del_files(file_ids) @Slot() def change_path(self): + """Ouvre une boîte de dialogue pour changer le dossier de destination des téléchargements""" + self.logger.info("Ouverture de la boîte de dialogue de sélection de dossier") + + # Récupérer le chemin actuel pour l'utiliser comme point de départ + current_path = self.conf.get_value("download_location", "") + self.logger.debug(f"Chemin actuel: {current_path}") + # Ouvrir la boîte de dialogue pour sélectionner un dossier new_path = QFileDialog.getExistingDirectory( self.parent(), "Sélectionner un dossier de destination", - self.conf.get_value("download_location", "") + current_path ) # Vérifier si l'utilisateur a bien sélectionné un dossier (et n'a pas annulé) if new_path: + self.logger.info(f"Nouveau dossier sélectionné: {new_path}") + # Mettre à jour le chemin de téléchargement dans la configuration self.conf.set_value("download_location", new_path) + self.logger.debug("Chemin de téléchargement mis à jour dans la configuration") + # Sauvegarder la configuration si nécessaire # self.download_manager.save_config() # Décommentez si vous avez une méthode pour sauvegarder la configuration @@ -96,3 +144,6 @@ class WebHandler(QObject): "context": "download_location_updated", "content": new_path }) + self.logger.debug("Signal de mise à jour du chemin de téléchargement émis") + else: + self.logger.debug("Sélection de dossier annulée par l'utilisateur") diff --git a/windows/site_window.py b/windows/site_window.py index b2a9311..e0ae98a 100644 --- a/windows/site_window.py +++ b/windows/site_window.py @@ -1,6 +1,7 @@ import os from pathlib import Path import sqlite3 +import logging from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEngineProfile, QWebEnginePage, QWebEngineCookieStore from PySide6.QtWidgets import QWidget @@ -22,54 +23,83 @@ class SiteWindow(QWebEngineView): def __init__(self, parent=None): super().__init__(parent) + # Configuration du logger + self.logger = logging.getLogger(__name__) + self.logger.debug("Initialisation de SiteWindow") + self.conf = parent.conf self.web_handler = parent.web_handler self.page().profile().cookieStore().deleteAllCookies() + self.logger.debug("Cookies supprimés du profil par défaut") storage_path = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation)) + self.logger.debug(f"Chemin de stockage des données: {storage_path}") if not storage_path.exists(): storage_path.mkdir(parents=True) + self.logger.debug(f"Création du répertoire de stockage: {storage_path}") self.persistent_profile = QWebEngineProfile("OxAppProfile", parent=self) + self.logger.debug("Création du profil web persistant: OxAppProfile") + self.cookie_store = self.persistent_profile.cookieStore() self.cookie_store.cookieAdded.connect(self.on_cookie_added.emit) + self.logger.debug("Signal cookieAdded connecté") + self.persistent_profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.MemoryHttpCache) - self.persistent_profile.setPersistentStoragePath(str(self.conf.app_config_path / "web_cache")) + self.logger.debug("Type de cache HTTP configuré: MemoryHttpCache") + + cache_path = str(self.conf.app_config_path / "web_cache") + self.persistent_profile.setPersistentStoragePath(cache_path) + self.logger.debug(f"Chemin de stockage persistant configuré: {cache_path}") + self.persistent_profile.setPersistentCookiesPolicy(QWebEngineProfile.PersistentCookiesPolicy.AllowPersistentCookies) + self.logger.debug("Politique de cookies persistants configurée: AllowPersistentCookies") + self.persistent_profile.setHttpUserAgent("oxapp25") + self.logger.debug("User-Agent HTTP configuré: oxapp25") custom_page = QWebEnginePage(self.persistent_profile, parent=self) self.setPage(custom_page) + self.logger.debug("Page web personnalisée créée et configurée") + # Configuration des attributs de la page web self.settings().setAttribute(QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled, True) self.settings().setAttribute(QWebEngineSettings.WebAttribute.ScrollAnimatorEnabled, True) self.settings().setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True) - # self.settings().setAttribute(QWebEngineSettin gs.WebAttribute.LocalStorageEnabled, False) + # self.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, False) # self.settings().setAttribute(QWebEngineSettings.WebAttribute.AutoLoadImages, True) # self.settings().setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, False) self.settings().setAttribute(QWebEngineSettings.WebAttribute.ShowScrollBars, True) self.page().setBackgroundColor(Qt.GlobalColor.white) + self.logger.debug("Attributs de la page web configurés") self.web_channel = QWebChannel(self) self.web_channel.registerObject("handler", self.web_handler) self.page().setWebChannel(self.web_channel) + self.logger.debug("Canal web configuré avec le gestionnaire") self.loadFinished.connect(self.on_load_finished) + self.logger.debug("Signal loadFinished connecté") + self.logger.info(f"Chargement de l'URL: {parent.url}") self.load(parent.url) @Slot(bool) def on_load_finished(self, is_success): if is_success: + self.logger.info("Page chargée avec succès") + + # Injection de l'API WebChannel api_file = QFile(":/qtwebchannel/qwebchannel.js") api_file.open(QIODevice.OpenModeFlag.ReadOnly) api_content = api_file.readAll().data().decode("utf-8") api_file.close() self.page().runJavaScript(api_content) + self.logger.debug("API WebChannel injectée dans la page") - # fix main scrollbar + # Correction de la barre de défilement principale css = """ html::-webkit-scrollbar, body::-webkit-scrollbar { @@ -83,13 +113,24 @@ class SiteWindow(QWebEngineView): """ js_code = f"var style = document.createElement('style'); style.textContent = `{css}`; document.head.appendChild(style);" self.page().runJavaScript(js_code) + self.logger.debug("CSS personnalisé injecté pour masquer les barres de défilement") + # Recréation du cookie de session self.recreate_sessionid_cookie() + else: + self.logger.error("Échec du chargement de la page") def sessionid_from_cookie_store(self) -> None|sqlite3.Row: - # Cette approche est expérimentale et dépend de l'implémentation interne + """ + Récupère le cookie de session depuis le stockage de cookies. + Cette approche est expérimentale et dépend de l'implémentation interne. + + Returns: + sqlite3.Row ou None: Les informations du cookie de session ou None si non trouvé + """ profile_path = self.persistent_profile.persistentStoragePath() cookies_db_path = os.path.join(profile_path, "Cookies") + self.logger.debug(f"Recherche du cookie de session dans: {cookies_db_path}") if os.path.exists(cookies_db_path): try: @@ -101,21 +142,35 @@ class SiteWindow(QWebEngineView): conn.close() if result: + self.logger.debug("Cookie de session trouvé dans la base de données") return result # La valeur du cookie + else: + self.logger.debug("Aucun cookie de session trouvé dans la base de données") except Exception as e: - print(f"Erreur lors de l'accès au fichier de cookies: {e}") + self.logger.error(f"Erreur lors de l'accès au fichier de cookies: {e}", exc_info=True) + else: + self.logger.debug(f"Fichier de cookies non trouvé: {cookies_db_path}") return None def recreate_sessionid_cookie(self): + """ + Recrée le cookie de session à partir des informations stockées dans la base de données. + """ + self.logger.debug("Tentative de recréation du cookie de session") cookie_info = self.sessionid_from_cookie_store() + if cookie_info is None: + self.logger.debug("Aucune information de cookie de session disponible, abandon") return + # Créer un nouveau cookie avec le nom et la valeur cookie = QNetworkCookie(b"sessionid", cookie_info['value'].encode()) + self.logger.debug(f"Création d'un nouveau cookie 'sessionid' avec la valeur récupérée") # Configurer toutes les propriétés disponibles cookie.setDomain(cookie_info['host_key']) cookie.setPath(cookie_info['path']) + self.logger.debug(f"Configuration du domaine: {cookie_info['host_key']} et du chemin: {cookie_info['path']}") # Gérer les dates d'expiration (conversion du format si nécessaire) if cookie_info['has_expires']: @@ -124,10 +179,12 @@ class SiteWindow(QWebEngineView): # Vous devrez peut-être ajuster cette conversion selon le format exact expires = QDateTime.fromSecsSinceEpoch(int(cookie_info['expires_utc'] / 1000000) - 11644473600) cookie.setExpirationDate(expires) + self.logger.debug(f"Date d'expiration configurée: {expires.toString()}") # Configurer les attributs de sécurité cookie.setSecure(bool(cookie_info['is_secure'])) cookie.setHttpOnly(bool(cookie_info['is_httponly'])) + self.logger.debug(f"Attributs de sécurité configurés - Secure: {bool(cookie_info['is_secure'])}, HttpOnly: {bool(cookie_info['is_httponly'])}") # Configurer SameSite si disponible dans votre version de PySide6 # Vérifier si l'attribut est disponible (PySide6 ≥ 6.2.0) @@ -141,11 +198,13 @@ class SiteWindow(QWebEngineView): } if samesite_value in samesite_map: cookie.setSameSitePolicy(samesite_map[samesite_value]) + self.logger.debug(f"Politique SameSite configurée: {samesite_map[samesite_value]}") # Ajouter le cookie au store - cela va déclencher le signal cookieAdded self.cookie_store.setCookie(cookie) + self.logger.info("Cookie de session recréé et ajouté au store") def contextMenuEvent(self, event): # Ignorer l'événement pour empêcher l'affichage du menu contextuel + self.logger.debug("Menu contextuel désactivé") event.ignore() -