This commit is contained in:
2025-04-13 19:06:01 +02:00
commit cc91de9d52
29 changed files with 4049 additions and 0 deletions

91
windows/main_window.py Normal file
View File

@@ -0,0 +1,91 @@
import asyncio
import sys
from pathlib import Path
import logging
import base64
from PySide6.QtCore import QTimer
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QMainWindow
from src.conf import ConfManager
from src.download import DownloadManager
from src.handler import WebHandler
from windows.site_window import SiteWindow
import resources_rc
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# Création d'un logger spécifique pour cette classe
self.logger = logging.getLogger("MainWindow")
self.logger.info("Initialisation de la fenêtre principale")
# initialise main app
self.setWindowTitle("Oxpanel")
self.setMinimumSize(1024, 768)
self.setWindowIcon(QIcon(":/oxpanel.ico"))
self.url = "http://127.0.0.1:8000/" if "--dev" in sys.argv else "https://oxpanel.com/"
self.logger.debug(f"URL configurée: {self.url}")
# initialise every module
self.logger.debug("Initialisation des modules")
self.conf = ConfManager(self)
self.download_manager = DownloadManager(self)
self.web_handler = WebHandler(self.download_manager, self)
# initialise windows
self.logger.debug("Initialisation des fenêtres")
self.site_window = SiteWindow(self)
self.setCentralWidget(self.site_window)
# connection des signaux
self.site_window.on_cookie_added.connect(lambda cookie: asyncio.ensure_future(self.download_manager.add_cookie(cookie)))
self.logger.debug("Signaux connectés")
# initialisation du gestionnaire de téléchargement
self.dm_loop = None
QTimer.singleShot(0, self.setup_async_tasks)
self.logger.info("Fenêtre principale initialisée avec succès")
def setup_async_tasks(self):
# Lancer les tâches asyncio une fois que l'application est prête
self.logger.debug("Configuration des tâches asynchrones")
self.dm_loop = asyncio.ensure_future(self.download_manager.loop_queue())
self.logger.debug("File d'attente de téléchargement démarrée")
def handle_files(self, file_paths):
"""
Traite les fichiers reçus via "Ouvrir avec" ou d'une autre instance.
Args:
file_paths (list): Liste des chemins de fichiers à traiter
"""
if not self.web_handler.site_ready:
QTimer.singleShot(100, lambda: self.handle_files(file_paths))
self.logger.info("Site pas prêt, report de l'envoie des fichiers")
return
self.logger.info(f"Traitement de {len(file_paths)} fichiers reçus")
for file_path in file_paths:
file_path = Path(file_path)
self.logger.debug(f"Traitement du fichier : {file_path}")
# Exemple: vérifier si le fichier existe
if file_path.exists():
self.logger.debug(f"Le fichier {file_path} existe")
try:
with file_path.open("rb") as file:
file_content = file.read()
encoded_content = base64.b64encode(file_content).decode("utf-8")
self.web_handler.on_message.emit({
"context": "upload_torrent",
"content": encoded_content
})
except Exception as e:
self.logger.error(f"Erreur lors de la lecture ou de l'encodage du fichier {file_path}: {e}")
else:
self.logger.warning(f"Le fichier {file_path} n'existe pas")

157
windows/site_window.py Normal file
View File

@@ -0,0 +1,157 @@
import os
from pathlib import Path
import sqlite3
from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEngineProfile, QWebEnginePage, QWebEngineCookieStore
from PySide6.QtWidgets import QWidget
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWebChannel import QWebChannel
from PySide6.QtCore import Slot, QFile, QIODevice, Signal, Qt, QStandardPaths, QTimer, QDateTime
from PySide6.QtNetwork import QNetworkCookie
import sys
from src.conf import ConfManager
from src.handler import WebHandler
# https://gitea.devpanel.fr/oxpanel/app/src/branch/master/window/site.py
class SiteWindow(QWebEngineView):
on_cookie_added = Signal(QNetworkCookie)
def __init__(self, parent=None):
super().__init__(parent)
self.conf = parent.conf
self.web_handler = parent.web_handler
self.page().profile().cookieStore().deleteAllCookies()
storage_path = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation))
if not storage_path.exists():
storage_path.mkdir(parents=True)
self.persistent_profile = QWebEngineProfile("OxAppProfile", parent=self)
self.cookie_store = self.persistent_profile.cookieStore()
self.cookie_store.cookieAdded.connect(self.on_cookie_added.emit)
self.cookie_store.cookieAdded.connect(self.test_cookie)
self.persistent_profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.MemoryHttpCache)
self.persistent_profile.setPersistentStoragePath(str(self.conf.app_config_path / "web_cache"))
self.persistent_profile.setPersistentCookiesPolicy(QWebEngineProfile.PersistentCookiesPolicy.AllowPersistentCookies)
self.persistent_profile.setHttpUserAgent("oxapp25")
custom_page = QWebEnginePage(self.persistent_profile, parent=self)
self.setPage(custom_page)
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.AutoLoadImages, True)
# self.settings().setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, False)
self.settings().setAttribute(QWebEngineSettings.WebAttribute.ShowScrollBars, True)
self.page().setBackgroundColor(Qt.GlobalColor.white)
self.web_channel = QWebChannel(self)
self.web_channel.registerObject("handler", self.web_handler)
self.page().setWebChannel(self.web_channel)
self.loadFinished.connect(self.on_load_finished)
self.load(parent.url)
def test_cookie(self, *args, **kwargs):
print("cook", *args, **kwargs)
# self.cookie_store.loadAllCookies()
@Slot(bool)
def on_load_finished(self, is_success):
print("load finished")
if is_success:
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)
# fix main scrollbar
css = """
html::-webkit-scrollbar,
body::-webkit-scrollbar {
display: none;
}
html, body {
scrollbar-width: none; /* Pour Firefox */
-ms-overflow-style: none; /* Pour Internet Explorer et Edge */
overflow: auto; /* Conserver la capacité de défilement */
}
"""
js_code = f"var style = document.createElement('style'); style.textContent = `{css}`; document.head.appendChild(style);"
self.page().runJavaScript(js_code)
self.recreate_sessionid_cookie()
def sessionid_from_cookie_store(self) -> None|sqlite3.Row:
# Cette approche est expérimentale et dépend de l'implémentation interne
profile_path = self.persistent_profile.persistentStoragePath()
cookies_db_path = os.path.join(profile_path, "Cookies")
if os.path.exists(cookies_db_path):
try:
conn = sqlite3.connect(cookies_db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM cookies WHERE name='sessionid'")
result = cursor.fetchone()
conn.close()
if result:
return result # La valeur du cookie
except Exception as e:
print(f"Erreur lors de l'accès au fichier de cookies: {e}")
return None
def recreate_sessionid_cookie(self):
cookie_info = self.sessionid_from_cookie_store()
if cookie_info is None:
return
# Créer un nouveau cookie avec le nom et la valeur
cookie = QNetworkCookie(b"sessionid", cookie_info['value'].encode())
# Configurer toutes les propriétés disponibles
cookie.setDomain(cookie_info['host_key'])
cookie.setPath(cookie_info['path'])
# Gérer les dates d'expiration (conversion du format si nécessaire)
if cookie_info['has_expires']:
# Convertir depuis le format UNIX en QDateTime
# Attention: Chromium stocke les dates en microseconds depuis 1601-01-01 UTC
# Vous devrez peut-être ajuster cette conversion selon le format exact
expires = QDateTime.fromSecsSinceEpoch(int(cookie_info['expires_utc'] / 1000000) - 11644473600)
cookie.setExpirationDate(expires)
# Configurer les attributs de sécurité
cookie.setSecure(bool(cookie_info['is_secure']))
cookie.setHttpOnly(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)
if hasattr(cookie, 'setSameSitePolicy'):
# Conversion des valeurs numériques en constantes SameSite
samesite_value = cookie_info['samesite']
samesite_map = {
0: QNetworkCookie.SameSite.None_, # None
1: QNetworkCookie.SameSite.Lax, # Lax
2: QNetworkCookie.SameSite.Strict # Strict
}
if samesite_value in samesite_map:
cookie.setSameSitePolicy(samesite_map[samesite_value])
# Ajouter le cookie au store - cela va déclencher le signal cookieAdded
self.cookie_store.setCookie(cookie)
def contextMenuEvent(self, event):
# Ignorer l'événement pour empêcher l'affichage du menu contextuel
event.ignore()