Compare commits
10 Commits
1.0
...
last_async
| Author | SHA1 | Date | |
|---|---|---|---|
| be72749b53 | |||
| dc2adfbca5 | |||
| a693338891 | |||
| bfa6865109 | |||
| 9031e1d04d | |||
| 3155154083 | |||
| 012ab977a7 | |||
| ce8d9d639a | |||
| a061ee20a8 | |||
| ed5a381262 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
.venv
|
||||
OxApp.dist
|
||||
OxApp.dist
|
||||
installer
|
||||
OxApp.bin
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,7 +3,7 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.13 (oxapp25)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 virtualenv at D:\Dev\oxapp25\.venv" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (oxapp25)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
||||
2
.idea/oxapp25.iml
generated
2
.idea/oxapp25.iml
generated
@@ -4,7 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 virtualenv at D:\Dev\oxapp25\.venv" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 (oxapp25)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
32
Makefile
Normal file
32
Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
# Makefile pour OxApp - équivalent Linux du build.bat
|
||||
|
||||
.PHONY: help resources deploy pyinstaller installer
|
||||
|
||||
# Commande par défaut - affiche l'aide
|
||||
help:
|
||||
@echo "Commandes disponibles:"
|
||||
@echo " make resources - Compile les ressources"
|
||||
@echo " make deploy - Déploie l'application"
|
||||
@echo " make pyinstaller - Crée l'exécutable avec PyInstaller"
|
||||
@echo " make installer - Crée l'installateur"
|
||||
|
||||
# Compile les ressources
|
||||
resources:
|
||||
pyside6-rcc resources.qrc -o resources_rc.py
|
||||
|
||||
# Déploie l'application avec pyside6-deploy
|
||||
deploy:
|
||||
pyside6-deploy -c pysidedeploy_linux.spec
|
||||
|
||||
# Crée l'exécutable avec PyInstaller
|
||||
pyinstaller:
|
||||
pyinstaller OxApp.spec --noconfirm
|
||||
|
||||
# Crée l'installateur
|
||||
# Note: Inno Setup est spécifique à Windows, vous devrez utiliser
|
||||
# un équivalent Linux comme makeself, AppImage, ou Flatpak
|
||||
installer:
|
||||
@echo "La création d'installateur sur Linux nécessite un outil différent d'Inno Setup."
|
||||
@echo "Options recommandées: makeself, AppImage, ou Flatpak"
|
||||
@echo "Exemple avec makeself (si installé):"
|
||||
@echo " makeself.sh ./dist/OxApp \"OxApp\" \"OxApp Installation\" ./dist/Ox
|
||||
@@ -5,8 +5,12 @@ if "%1"=="resources" (
|
||||
pyside6-deploy.exe -c pysidedeploy.spec
|
||||
) else if "%1"=="pyinstaller" (
|
||||
pyinstaller OxApp.spec --noconfirm
|
||||
) else if "%1"=="installer" (
|
||||
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "setup.iss"
|
||||
) else (
|
||||
echo Commandes disponibles:
|
||||
echo build.bat resources - Compile les ressources
|
||||
echo build.bat deploy - Déploie l'application
|
||||
echo build.bat pyinstaller - Crée l'exécutable avec PyInstaller
|
||||
echo build.bat installer - Crée l'installateur avec Inno Setup
|
||||
)
|
||||
|
||||
116
main.py
116
main.py
@@ -1,4 +1,4 @@
|
||||
from PySide6.QtCore import QStandardPaths, QDataStream, QByteArray, QIODevice, Signal, Qt, QTimer, QCryptographicHash
|
||||
from PySide6.QtCore import QDataStream, QIODevice, Signal, QTimer
|
||||
from PySide6.QtGui import QPalette, QColor
|
||||
from PySide6.QtNetwork import QLocalServer, QLocalSocket
|
||||
from PySide6.QtWidgets import QApplication
|
||||
@@ -7,14 +7,8 @@ import qasync
|
||||
import sys
|
||||
import asyncio
|
||||
import os
|
||||
import platform
|
||||
import argparse
|
||||
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
|
||||
@@ -63,9 +57,6 @@ class SingleApplication(QApplication):
|
||||
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()
|
||||
|
||||
@@ -77,7 +68,8 @@ class SingleApplication(QApplication):
|
||||
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")
|
||||
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)
|
||||
if self.server.listen(self.app_id):
|
||||
@@ -90,78 +82,6 @@ class SingleApplication(QApplication):
|
||||
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
|
||||
|
||||
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é
|
||||
|
||||
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:
|
||||
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:
|
||||
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
|
||||
@@ -180,12 +100,13 @@ class SingleApplication(QApplication):
|
||||
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")
|
||||
# Envoi des arguments sans chiffrement
|
||||
args_str = ";".join(args)
|
||||
self.logger.debug("Arguments à envoyer: " + args_str)
|
||||
|
||||
# Envoyer les arguments à l'instance primaire
|
||||
stream = QDataStream(socket)
|
||||
stream.writeQString(encrypt_args)
|
||||
stream.writeQString(args_str)
|
||||
socket.flush()
|
||||
self.logger.debug("Données envoyées à l'instance primaire")
|
||||
|
||||
@@ -216,22 +137,17 @@ class SingleApplication(QApplication):
|
||||
self.logger.debug("Données disponibles pour lecture")
|
||||
stream = QDataStream(socket)
|
||||
|
||||
encrypted_args = stream.readQString()
|
||||
self.logger.debug(f"Arguments chiffrés reçus (longueur: {len(encrypted_args)})")
|
||||
# Lecture des arguments sans déchiffrement
|
||||
args_str = stream.readQString()
|
||||
self.logger.debug(f"Arguments reçus: {args_str}")
|
||||
|
||||
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")
|
||||
# É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.warning("Échec du déchiffrement des arguments")
|
||||
self.logger.debug("Aucun argument à traiter")
|
||||
else:
|
||||
self.logger.warning("Délai d'attente dépassé pour la lecture des données")
|
||||
|
||||
|
||||
BIN
oxpanel.png
Normal file
BIN
oxpanel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
@@ -17,7 +17,7 @@ input_file = D:\Dev\oxapp25\main.py
|
||||
exec_directory = .
|
||||
|
||||
# path to .pyproject project file
|
||||
project_file =
|
||||
project_file =
|
||||
|
||||
# application icon
|
||||
icon = D:\Dev\oxapp25\oxpanel.ico
|
||||
@@ -37,13 +37,13 @@ android_packages = buildozer==1.5.0,cython==0.29.33
|
||||
|
||||
# comma separated path to qml files required
|
||||
# normally all the qml files required by the project are added automatically
|
||||
qml_files =
|
||||
qml_files =
|
||||
|
||||
# excluded qml plugin binaries
|
||||
excluded_qml_plugins =
|
||||
excluded_qml_plugins =
|
||||
|
||||
# qt modules used. comma separated
|
||||
modules = Gui,Network,Core,Widgets,WebChannel,WebEngineWidgets,WebEngineCore
|
||||
modules = Network,WebEngineCore,Widgets,Gui,WebEngineWidgets,Core,WebChannel
|
||||
|
||||
# qt plugins used by the application. only relevant for desktop deployment. for qt plugins used
|
||||
# in android application see [android][plugins]
|
||||
@@ -52,26 +52,26 @@ plugins = networkinformation,styles,networkaccess,tls
|
||||
[android]
|
||||
|
||||
# path to pyside wheel
|
||||
wheel_pyside =
|
||||
wheel_pyside =
|
||||
|
||||
# path to shiboken wheel
|
||||
wheel_shiboken =
|
||||
wheel_shiboken =
|
||||
|
||||
# plugins to be copied to libs folder of the packaged application. comma separated
|
||||
plugins =
|
||||
plugins =
|
||||
|
||||
[nuitka]
|
||||
|
||||
# usage description for permissions requested by the app as found in the info.plist file
|
||||
# of the app bundle
|
||||
# eg = extra_args = --show-modules --follow-stdlib
|
||||
macos.permissions =
|
||||
macos.permissions =
|
||||
|
||||
# mode of using nuitka. accepts standalone or onefile. default is onefile.
|
||||
mode = standalone
|
||||
|
||||
# (str) specify any extra nuitka arguments
|
||||
extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable --output-filename=oxapp
|
||||
extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable --output-filename=oxapp --company-name=Oxpanel --product-name=OxApp --file-description="Application légitime OxApp" --copyright="Oxpanel (c) 2023" --file-version=1.0.0 --product-version=1.0.0 --unstripped --no-deployment-flag=self-execution --disable-ccache --disable-console
|
||||
|
||||
[buildozer]
|
||||
|
||||
@@ -81,22 +81,21 @@ extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable
|
||||
mode = release
|
||||
|
||||
# contrains path to pyside6 and shiboken6 recipe dir
|
||||
recipe_dir =
|
||||
recipe_dir =
|
||||
|
||||
# path to extra qt android jars to be loaded by the application
|
||||
jars_dir =
|
||||
jars_dir =
|
||||
|
||||
# if empty uses default ndk path downloaded by buildozer
|
||||
ndk_path =
|
||||
ndk_path =
|
||||
|
||||
# if empty uses default sdk path downloaded by buildozer
|
||||
sdk_path =
|
||||
sdk_path =
|
||||
|
||||
# other libraries to be loaded. comma separated.
|
||||
# loaded at app startup
|
||||
local_libs =
|
||||
local_libs =
|
||||
|
||||
# architecture of deployed platform
|
||||
# possible values = ["aarch64", "armv7a", "i686", "x86_64"]
|
||||
arch =
|
||||
|
||||
arch =
|
||||
|
||||
99
pysidedeploy_linux.spec
Executable file
99
pysidedeploy_linux.spec
Executable file
@@ -0,0 +1,99 @@
|
||||
[app]
|
||||
|
||||
# title of your application
|
||||
title = OxApp
|
||||
|
||||
# project directory. the general assumption is that project_dir is the parent directory
|
||||
# of input_file
|
||||
project_dir = /home/nell/dev/oxapp25
|
||||
|
||||
# source file path
|
||||
input_file = /home/nell/dev/oxapp25/main.py
|
||||
|
||||
# directory where the executable output is generated
|
||||
exec_directory = .
|
||||
|
||||
# path to .pyproject project file
|
||||
project_file =
|
||||
|
||||
# application icon
|
||||
icon = /home/nell/dev/oxapp25/oxpanel.ico
|
||||
|
||||
[python]
|
||||
|
||||
# python path
|
||||
python_path = /home/nell/dev/oxapp25/.venv/bin/python
|
||||
|
||||
# python packages to install
|
||||
packages = Nuitka==2.5.1
|
||||
|
||||
# buildozer = for deploying Android application
|
||||
android_packages = buildozer==1.5.0,cython==0.29.33
|
||||
|
||||
[qt]
|
||||
|
||||
# comma separated path to qml files required
|
||||
# normally all the qml files required by the project are added automatically
|
||||
qml_files =
|
||||
|
||||
# excluded qml plugin binaries
|
||||
excluded_qml_plugins =
|
||||
|
||||
# qt modules used. comma separated
|
||||
modules = Positioning,WebChannel,Quick,QuickWidgets,QmlMeta,OpenGL,DBus,Core,QmlModels,QmlWorkerScript,Qml,Network,Widgets,Gui,WebEngineWidgets,PrintSupport,WebEngineCore
|
||||
|
||||
# qt plugins used by the application. only relevant for desktop deployment. for qt plugins used
|
||||
# in android application see [android][plugins]
|
||||
plugins = networkinformation,scenegraph,platforms/darwin,iconengines,tls,xcbglintegrations,qmltooling,position,platformthemes,imageformats,platforms,platforminputcontexts,accessiblebridge,generic,networkaccess,styles,printsupport,egldeviceintegrations
|
||||
|
||||
[android]
|
||||
|
||||
# path to pyside wheel
|
||||
wheel_pyside =
|
||||
|
||||
# path to shiboken wheel
|
||||
wheel_shiboken =
|
||||
|
||||
# plugins to be copied to libs folder of the packaged application. comma separated
|
||||
plugins =
|
||||
|
||||
[nuitka]
|
||||
|
||||
# usage description for permissions requested by the app as found in the info.plist file
|
||||
# of the app bundle
|
||||
# eg = extra_args = --show-modules --follow-stdlib
|
||||
macos.permissions =
|
||||
|
||||
# mode of using nuitka. accepts standalone or onefile. default is onefile.
|
||||
mode = onefile
|
||||
|
||||
# (str) specify any extra nuitka arguments
|
||||
extra_args = --quiet --noinclude-qt-translations --jobs=auto --lto=yes
|
||||
|
||||
[buildozer]
|
||||
|
||||
# build mode
|
||||
# possible options = [release, debug]
|
||||
# release creates an aab, while debug creates an apk
|
||||
mode = debug
|
||||
|
||||
# contrains path to pyside6 and shiboken6 recipe dir
|
||||
recipe_dir =
|
||||
|
||||
# path to extra qt android jars to be loaded by the application
|
||||
jars_dir =
|
||||
|
||||
# if empty uses default ndk path downloaded by buildozer
|
||||
ndk_path =
|
||||
|
||||
# if empty uses default sdk path downloaded by buildozer
|
||||
sdk_path =
|
||||
|
||||
# other libraries to be loaded. comma separated.
|
||||
# loaded at app startup
|
||||
local_libs =
|
||||
|
||||
# architecture of deployed platform
|
||||
# possible values = ["aarch64", "armv7a", "i686", "x86_64"]
|
||||
arch =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PySide6<6.9,>=6.8.0
|
||||
qasync>=0.27.1
|
||||
httpx
|
||||
httpx[http2]
|
||||
anyio
|
||||
@@ -39,11 +39,11 @@ Name: "french"; MessagesFile: "compiler:Languages\French.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
Name: "associatetorrent"; Description: "Associer les fichiers .torrent à {#MyAppName}"; GroupDescription: "Association de fichiers:"; Flags: unchecked
|
||||
Name: "associatetorrent"; Description: "Associer les fichiers .torrent à {#MyAppName}"; GroupDescription: "Association de fichiers:";
|
||||
|
||||
[Files]
|
||||
; Ajoutez tous les fichiers nécessaires à votre application
|
||||
Source: "D:\Dev\oxapp25\dist\OxApp.dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "D:\Dev\oxapp25\OxApp.dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
|
||||
@@ -186,6 +186,7 @@ class DownloadManager(QObject):
|
||||
follow_redirects=True,
|
||||
verify=False,
|
||||
cookies=self.cookies,
|
||||
http2=True,
|
||||
) as client:
|
||||
# requête pour le téléchargement
|
||||
async with client.stream("GET", file.url, headers=headers) as response:
|
||||
|
||||
Reference in New Issue
Block a user