"""MusicTools — Entry point (pywebview + WebKit/EdgeChromium).""" import os import sys from pathlib import Path # ============================================================ # Logging persistente su file (rotazione giornaliera, 7 giorni). # Chiamato SUBITO cosi' ogni traceback o print da qui in poi # finisce nel file, anche in modalita' windowed. # ============================================================ try: from core.applog import setup as _setup_log _setup_log() except Exception as _e: print(f"[bootstrap] log setup failed: {_e}") # ============================================================ # 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()