Files
copydev-imposing-tool/main.py
Jules c5236c1e54 Fix splash screen bloquant la saisie de licence
Le splash avec WindowStaysOnTopHint cachait la boite de dialogue
de saisie de licence. On cache le splash pendant la verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 19:57:58 +01:00

236 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""Copydev Imposing Tool — Point d'entree."""
import sys
import os
import json
import time
import urllib.request
import urllib.error
import urllib.parse
# Support PyInstaller (chemin des resources)
if getattr(sys, 'frozen', False):
BASE_DIR = os.path.dirname(sys.executable)
BUNDLE_DIR = sys._MEIPASS
else:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BUNDLE_DIR = BASE_DIR
sys.path.insert(0, BUNDLE_DIR)
from PyQt6.QtWidgets import QApplication, QInputDialog, QMessageBox, QLineEdit
import hashlib
import platform
import socket
import uuid
LICENSE_API = "https://vision.copydev.fr/api/license.php"
LICENSE_FILE = os.path.join(BASE_DIR, ".license")
def _get_machine_id():
"""Generer un identifiant unique pour cette machine."""
raw = ""
try:
raw += platform.node()
raw += str(uuid.getnode()) # MAC address
except Exception:
raw += "unknown"
return hashlib.sha256(raw.encode()).hexdigest()[:32]
def _get_hostname():
"""Recuperer le nom NetBIOS / hostname."""
try:
return platform.node() or socket.gethostname()
except Exception:
return "inconnu"
def _load_license_cache():
"""Charger le cache de licence local."""
if not os.path.exists(LICENSE_FILE):
return None
try:
with open(LICENSE_FILE, "r") as f:
return json.load(f)
except Exception:
return None
def _save_license_cache(data):
"""Sauvegarder le cache de licence local."""
try:
with open(LICENSE_FILE, "w") as f:
json.dump(data, f)
except Exception:
pass
def _check_license_online(key):
"""Verifier la licence aupres du serveur Vision."""
try:
mid = _get_machine_id()
hname = _get_hostname()
url = f"{LICENSE_API}?key={urllib.parse.quote(key)}&machine_id={urllib.parse.quote(mid)}&hostname={urllib.parse.quote(hname)}"
req = urllib.request.Request(url, headers={"User-Agent": "CopydevImposingTool/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
data = json.loads(resp.read().decode())
if data.get("valid"):
cache = {
"key": key,
"client": data.get("client", ""),
"product": data.get("product", ""),
"offline_timeout_hours": data.get("offline_timeout_hours", 72),
"last_validated": time.time(),
}
_save_license_cache(cache)
return True, data.get("client", "")
return False, "Licence invalide"
except urllib.error.HTTPError as e:
try:
body = json.loads(e.read().decode())
return False, body.get("error", "Erreur serveur")
except Exception:
return False, f"Erreur HTTP {e.code}"
except Exception as e:
return None, str(e) # None = hors-ligne
def _check_license():
"""Verifier la licence au lancement."""
cache = _load_license_cache()
# Si on a un cache, essayer de valider en ligne
if cache and cache.get("key"):
key = cache["key"]
result, msg = _check_license_online(key)
if result is True:
# Licence valide en ligne
return True
if result is False:
# Licence refusee par le serveur
QMessageBox.critical(
None, "Licence invalide",
f"Votre licence a ete refusee :\n{msg}\n\n"
"Contactez CopyDev : contact@copydev.fr"
)
return False
# result is None = hors-ligne, verifier le timeout
timeout_hours = cache.get("offline_timeout_hours", 72)
last_validated = cache.get("last_validated", 0)
elapsed_hours = (time.time() - last_validated) / 3600
if elapsed_hours < timeout_hours:
remaining = timeout_hours - elapsed_hours
return True
# Timeout depasse
QMessageBox.critical(
None, "Licence expiree hors-ligne",
f"Impossible de verifier votre licence depuis {int(elapsed_hours)}h.\n"
f"Le delai hors-ligne autorise est de {timeout_hours}h.\n\n"
"Connectez-vous a internet ou contactez CopyDev :\n"
"contact@copydev.fr"
)
return False
# Pas de cache — demander la cle de licence
key, ok = QInputDialog.getText(
None,
"Copydev Imposing Tool — Activation",
"Entrez votre cle de licence :",
QLineEdit.EchoMode.Normal,
)
if not ok or not key.strip():
return False
key = key.strip()
result, msg = _check_license_online(key)
if result is True:
QMessageBox.information(
None, "Licence activee",
f"Bienvenue, {msg} !\nVotre licence est activee."
)
return True
if result is False:
QMessageBox.critical(
None, "Licence invalide",
f"{msg}\n\nContactez CopyDev : contact@copydev.fr"
)
return False
# Hors-ligne a la premiere activation
QMessageBox.critical(
None, "Connexion requise",
"Impossible de contacter le serveur de licences.\n"
"Une connexion internet est necessaire pour la premiere activation.\n\n"
"Contactez CopyDev : contact@copydev.fr"
)
return False
def main():
app = QApplication(sys.argv)
app.setApplicationName("Copydev Imposing Tool")
app.setOrganizationName("Copydev")
# Icone application
from PyQt6.QtGui import QIcon
icon_path = os.path.join(BUNDLE_DIR, "resources", "logo.png")
if os.path.exists(icon_path):
app.setWindowIcon(QIcon(icon_path))
# Splash screen
from app.splash import SplashScreen
splash = SplashScreen()
splash.show()
QApplication.processEvents()
splash.set_status("Verification de la licence...")
QApplication.processEvents()
splash.hide()
if not _check_license():
sys.exit(1)
splash.show()
QApplication.processEvents()
splash.set_status("Recherche de mises a jour...")
# Verification de mise a jour silencieuse
from app.updater import check_for_update, VERSION
update_available, remote_version = check_for_update()
if update_available:
reply = QMessageBox.question(
None, "Mise a jour disponible",
f"Une nouvelle version est disponible : {remote_version}\n"
f"(version actuelle : {VERSION})\n\n"
"Voulez-vous mettre a jour maintenant ?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
from app.updater import do_update
success, msg = do_update()
if success:
QMessageBox.information(None, "Mise a jour", f"{msg}\n\nL'application va redemarrer.")
os.execv(sys.executable, [sys.executable] + sys.argv)
else:
QMessageBox.warning(None, "Mise a jour", f"Echec de la mise a jour :\n{msg}")
splash.set_status("Chargement de l'interface...")
from app.main_window import MainWindow
window = MainWindow()
window.show()
splash.close()
sys.exit(app.exec())
if __name__ == "__main__":
main()