Ctrl K
ComunidadGuías…

blink2video.spec

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Recette de construction du bundle autonome. Voir build.py, qui prépare
# l'environnement isolé puis appelle PyInstaller sur ce fichier.
#
# Choix du mode : un dossier, pas un fichier unique. L'outil se relance
# lui-même (la surveillance appelle le téléchargement, l'assemblage, puis
# l'interface), et un exécutable « onefile » réextrait la centaine de méga-
# octets du bundle à *chaque* lancement, y compris pour ces relances. Le
# dossier se compresse tout aussi bien pour la diffusion, et démarre
# instantanément.

from __future__ import annotations  # Python 3.8 (build Windows 7) : les annotations "X | None" ne s'évaluent qu'à l'écriture des chaînes, jamais à l'exécution.

import os
from pathlib import Path

from PyInstaller.utils.hooks import collect_data_files
from build_support import ecrire_version_info


SRC = Path(SPECPATH)
APP_ICON = SRC / "assets" / "blink2video.png"
# Favicon de l'interface web (serve.py, route /favicon.ico) : embarqué sous
# assets/ pour que le chemin lu au démarrage (runtime.resource_dir() /
# "assets" / ...) soit le même, figé ou depuis les sources.
FAVICON = SRC / "assets" / "blink2video.ico"
# Marqueur absent des bundles officiels. L'édition Windows 7 s'en sert pour
# s'identifier et pour refuser une mise à jour vers le runtime Python 3.12.
WIN7_MARKER = SRC / "assets" / "windows7-build.txt"
# CSS/JS de la page web (serve.py), extraits pour ne plus vivre en dur dans
# un .py : lus par runtime.resource_dir() à la racine du bundle, comme
# FFMPEG ci-dessous, jamais sous assets/ (contrairement à FAVICON).
SERVE_STYLE = SRC / "serve_style.css"
SERVE_APP_JS = SRC / "serve_app.js"

# ffmpeg est propre à la plateforme : on demande le binaire à imageio_ffmpeg,
# installé dans l'environnement de construction, qui fournit celui de l'hôte.
# À défaut, on reprend la copie déposée dans _vendor. PyInstaller ne sachant
# pas produire pour un autre système que le sien, ce choix se fait à la
# construction et vaut pour la plateforme courante uniquement.
def _ffmpeg() -> str:
    # build.py a déjà choisi un binaire sachant incruster du texte, et l'a
    # désigné ici : c'est lui qui fait autorité.
    import os

    choisi = os.environ.get("BLINK_FFMPEG")
    if choisi:
        return choisi
    try:
        import imageio_ffmpeg

        return imageio_ffmpeg.get_ffmpeg_exe()
    except Exception:
        import glob

        trouves = sorted(glob.glob("_vendor/imageio_ffmpeg/binaries/ffmpeg-*"))
        if not trouves:
            raise SystemExit(
                "ffmpeg introuvable : installez imageio-ffmpeg dans "
                "l'environnement de construction."
            )
        return trouves[0]


def _ffprobe() -> str | None:
    # Best-effort seulement (secours Linux de build.py, voir ffmpeg_utilisable) :
    # merge_daily._outil_validation_media() sait déjà s'en passer, en retombant
    # sur une vérification ffmpeg plus stricte.
    import os

    return os.environ.get("BLINK_FFPROBE") or None


def _runtime():
    import importlib.util

    charge = importlib.util.spec_from_file_location("runtime", "runtime.py")
    runtime = importlib.util.module_from_spec(charge)
    charge.loader.exec_module(runtime)
    return runtime


def _modules(runtime) -> list:
    return sorted({verbe.module for verbe in runtime.VERBES.values()}
                  - {runtime.ENTREE} | {"runtime", "tzdata"})


FFMPEG = _ffmpeg()
FFPROBE = _ffprobe()
RUNTIME = _runtime()

analysis = Analysis(
    ["blink2video.py"],
    pathex=["."],
    # ffmpeg (et ffprobe, quand build.py en a trouvé un) voyagent à la racine
    # du bundle : merge_daily.find_ffmpeg/_outil_validation_media les y
    # cherchent en premier, avant le PATH de la machine cible.
    binaries=[(FFMPEG, ".")] + ([(FFPROBE, ".")] if FFPROBE else []),
    # Windows n'a pas de base de fuseaux horaires système : sans ces données,
    # ZoneInfo("Europe/Paris") échoue et tout l'horodatage avec.
    datas=(collect_data_files("tzdata", include_py_files=False)
           # Non optionnels (contrairement à APP_ICON/FAVICON ci-dessous) :
           # serve.py les lit sans condition au chargement du module, leur
           # absence du bundle serait un crash au démarrage, pas une
           # dégradation propre.
           + [(str(SERVE_STYLE), "."), (str(SERVE_APP_JS), ".")]
           + ([(str(APP_ICON), ".")] if APP_ICON.exists() else [])
           + ([(str(FAVICON), "assets")] if FAVICON.exists() else [])
           + ([(str(WIN7_MARKER), ".")]
              if os.environ.get("BLINK_BUILD_TARGET") == "windows7" else [])),
    # Les verbes sont résolus par importlib au moment de l'appel : l'analyse
    # statique de PyInstaller ne peut pas les voir, il faut les nommer.
    # Déduits de runtime.VERBES : les verbes sont résolus par importlib au
    # moment de l'appel, donc invisibles à l'analyse statique. Les énumérer ici
    # à la main créerait une liste parallèle de plus, et c'est exactement ce
    # qui a fait échouer la CI après le renommage de « review » en « serve ».
    hiddenimports=_modules(RUNTIME),
    hookspath=[],
    runtime_hooks=[],
    # imageio_ffmpeg est écarté : son seul intérêt est de fournir un binaire,
    # déjà copié à la racine ci-dessus. Le laisser entrer le ferait embarquer
    # une seconde fois, 84 Mo pour rien. find_ffmpeg trouve celui de la racine
    # avant même de tenter cet import.
    excludes=["tkinter", "PyInstaller", "pytest", "imageio_ffmpeg"],
    noarchive=False,
)

pyz = PYZ(analysis.pure)

executable = EXE(
    pyz,
    analysis.scripts,
    [],
    exclude_binaries=True,
    name="blink2video",
    debug=False,
    strip=False,
    upx=False,
    # Sans fenêtre, comme pythonw.exe (déjà substitué à python.exe pour
    # l'exécution depuis les sources, voir autostart.py) : avec console=True,
    # même l'entrée de démarrage automatique d'un exe installé ouvrait une
    # console à chaque connexion, ce que toute l'icône de zone de
    # notification est justement censée éviter (constaté en conditions
    # réelles sur la VM Windows 7, et signalé par un utilisateur sur
    # Reddit). print() ne casse rien sans console : runtime.py compte déjà
    # dessus pour tourner sous pythonw.exe, un chemin déjà éprouvé, pas
    # nouveau avec ce changement.
    console=False,
    # Même mécanisme que lidar2map : PNG source portable, converti par
    # PyInstaller en ressource native sur la plateforme de construction.
    icon=str(APP_ICON),
    version=str(ecrire_version_info(RUNTIME.VERSION, SRC / ".version_info.txt")),
)

COLLECT(
    executable,
    analysis.binaries,
    analysis.datas,
    strip=False,
    upx=False,
    name="blink2video",
)