- requirements.txt: certifi come dep esplicita - main.py: setta SSL_CERT_FILE/REQUESTS_CA_BUNDLE su certifi.where() al boot - build_windows.py + build_macos.py: --collect-data certifi nel PyInstaller Bugfix: senza certifi bundlato, le chiamate HTTPS dal build Windows falliscono con CERTIFICATE_VERIFY_FAILED (attivazione licenza impossibile).
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
"""MusicTools — Entry point (pywebview + WebKit/EdgeChromium)."""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# ============================================================
|
|
# Fix SSL CA bundle: quando l'app e' frozen (PyInstaller) i moduli
|
|
# ssl e requests non trovano nessun CA bundle di default -> tutte
|
|
# le chiamate HTTPS falliscono con CERTIFICATE_VERIFY_FAILED.
|
|
# Puntiamo esplicitamente al bundle di certifi (che il build script
|
|
# include via `--collect-data certifi`).
|
|
# ============================================================
|
|
try:
|
|
import certifi as _certifi
|
|
_ca_bundle = _certifi.where()
|
|
os.environ.setdefault("SSL_CERT_FILE", _ca_bundle)
|
|
os.environ.setdefault("REQUESTS_CA_BUNDLE", _ca_bundle)
|
|
except Exception as _e:
|
|
print(f"[bootstrap] certifi setup failed: {_e}")
|
|
|
|
# Windows: forza il caricamento del runtime pythonnet PRIMA di importare
|
|
# webview. Con PyInstaller il lazy-loader fallisce con
|
|
# "Failed to resolve Python.Runtime.Loader.Initialize" perche'
|
|
# Python.Runtime.dll viene bundlata ma il loader non sa risolverla
|
|
# senza l'inizializzazione esplicita. Fix conferma:
|
|
# https://github.com/pythonnet/pythonnet/issues/2178
|
|
if sys.platform == "win32":
|
|
# ============================================================
|
|
# Fix Windows: rimuovi Mark of the Web (MOTW) dalle DLL bundled.
|
|
# Quando l'utente scarica il .zip dal browser, Windows aggiunge
|
|
# uno stream NTFS Zone.Identifier a TUTTI i file estratti.
|
|
# .NET Framework 4.x rifiuta silenziosamente di risolvere i
|
|
# symbol export degli assembly managed con MOTW -> il loader
|
|
# netfx di pythonnet fallisce con "Failed to resolve
|
|
# Python.Runtime.Loader.Initialize".
|
|
# Soluzione: cancelliamo lo stream :Zone.Identifier da ogni
|
|
# .dll/.exe del bundle al primo avvio (operazione idempotente
|
|
# e sicura: l'utente ha gia eseguito l'exe esplicitamente).
|
|
# Vedi: https://github.com/r0x0r/pywebview/issues/1215
|
|
# ============================================================
|
|
if getattr(sys, "frozen", False):
|
|
try:
|
|
import ctypes
|
|
_kernel32 = ctypes.windll.kernel32
|
|
_bundle = Path(sys._MEIPASS) # type: ignore[attr-defined]
|
|
for _f in _bundle.rglob("*"):
|
|
if _f.suffix.lower() in (".dll", ".exe", ".pyd"):
|
|
_kernel32.DeleteFileW(f"{_f}:Zone.Identifier")
|
|
except Exception as _e:
|
|
print(f"[bootstrap] MOTW cleanup failed: {_e}")
|
|
|
|
try:
|
|
if getattr(sys, "frozen", False):
|
|
os.environ["PYTHONNET_RUNTIME"] = "netfx"
|
|
from pythonnet import load as _pythonnet_load
|
|
_pythonnet_load("netfx")
|
|
import clr # noqa: F401 - inizializza il loader
|
|
except Exception as _e:
|
|
print(f"[bootstrap] preload pythonnet failed: {_e}")
|
|
|
|
import webview
|
|
|
|
from api.bridge import Api
|
|
|
|
|
|
def _resource_path(*parts) -> str:
|
|
"""Trova il path di una risorsa sia in dev che bundled (PyInstaller)."""
|
|
if getattr(sys, "frozen", False):
|
|
base = Path(sys._MEIPASS) # type: ignore[attr-defined]
|
|
else:
|
|
base = Path(__file__).resolve().parent
|
|
return str(base.joinpath(*parts))
|
|
|
|
|
|
def main():
|
|
api = Api()
|
|
index_path = _resource_path("webui", "index.html")
|
|
|
|
window = webview.create_window(
|
|
title="MusicTools",
|
|
url=index_path,
|
|
js_api=api,
|
|
width=1080,
|
|
height=720,
|
|
min_size=(900, 600),
|
|
background_color="#0A0A0A",
|
|
resizable=True,
|
|
)
|
|
api.window = window
|
|
|
|
# Disable native menu / set dark on macOS
|
|
webview.start(debug=False)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|