"""loader.py — le Space public, le code privé. Un Space public expose ses fichiers. Pour garder la méthode fermée tout en laissant l'outil utilisable, ce fichier est le SEUL point d'entrée public : il télécharge la charge utile depuis un dépôt Hugging Face **privé** au démarrage, puis lance l'application. Ce qui reste PUBLIC, délibérément : - ce chargeur, le Dockerfile, requirements.txt ; - `falrun.py` — les 100 lignes qui manipulent la clé fal du visiteur. C'est l'argument qui fait qu'on ose coller une clé ; le cacher coûterait plus que ce qu'il protégerait (il ne contient aucun savoir-faire) ; - les vidéos d'exemple et les poids publics (GNM de Google, MediaPipe). Ce qui part en PRIVÉ : tout ce qui encode la méthode — l'alignement yeux-sur-yeux, le fondu de raccord, les compounds des presets, le scanner de fenêtres, la calibration en boucle fermée, et le stage 3D. Configuration (Settings → Variables and secrets) : PRIVATE_REPO = "/retakes-core" (variable) HF_TOKEN = un token avec accès LECTURE à ce dépôt (secret) Le token ne quitte jamais le conteneur : il n'est lu que par ce fichier, et la page servie au visiteur ne contient que l'interface Gradio. """ from __future__ import annotations import os import runpy import sys APP_DIR = os.path.dirname(os.path.abspath(__file__)) REPO = os.environ.get('PRIVATE_REPO', '').strip() TOKEN = (os.environ.get('HF_TOKEN') or os.environ.get('HUGGINGFACE_TOKEN') or '').strip() REVISION = os.environ.get('PRIVATE_REVISION', 'main').strip() or 'main' def _placeholder(title: str, body: str) -> None: """Un Space mal configuré doit l'ANNONCER, pas mourir sur une trace de pile. Sinon le visiteur voit « Runtime error » et le propriétaire cherche dans les logs de build alors que le problème est un secret manquant. """ import gradio as gr with gr.Blocks(title='Retakes — configuration') as demo: gr.Markdown(f'# {title}\n\n{body}') demo.launch(server_name='0.0.0.0', server_port=7860) sys.exit(0) def main() -> None: app = os.path.join(APP_DIR, 'app.py') # DÉVELOPPEMENT : si la charge utile est déjà là (dépôt complet en local), on # la lance sans rien télécharger. L'image publique, elle, ne contient jamais # app.py — deploy_split.sh l'exclut — donc elle passe toujours par le dépôt # privé. Même commande dans les deux cas, aucun chemin de code divergent. if os.path.exists(app): sys.path.insert(0, APP_DIR) runpy.run_path(app, run_name='__main__') return if not REPO or not TOKEN: missing = ' et '.join( x for x in (('`PRIVATE_REPO` (variable)' if not REPO else ''), ('`HF_TOKEN` (secret)' if not TOKEN else '')) if x) _placeholder( '⚙️ Configuration incomplète', f'Ce Space charge son code depuis un dépôt privé. Il manque {missing} ' 'dans *Settings → Variables and secrets*.\n\nAucune donnée visiteur ' "n'est concernée : l'application n'a pas démarré.") from huggingface_hub import HfApi, snapshot_download # VÉRIFIER L'ACCÈS D'ABORD. `snapshot_download` ne lève PAS quand `local_dir` # existe déjà : il journalise « Returning existing local_dir » et rend la main # comme si tout allait bien (constaté le 04/08). Sans ce contrôle, un token # sans droits donnait le message « le dépôt ne contient pas app.py » — un # diagnostic faux, qui envoie chercher le problème au mauvais endroit. try: HfApi().repo_info(repo_id=REPO, repo_type='model', token=TOKEN) except Exception as e: # noqa: BLE001 name = type(e).__name__ denied = 'RepositoryNotFound' in name or 'GatedRepo' in name or '401' in str(e)[:80] _placeholder( '⚙️ Le code privé est inaccessible', (f'Le Space ne peut pas lire `{REPO}`.\n\n' '**Cause la plus fréquente** : un token *fine-grained* qui ne liste pas ' 'ce dépôt. Il faut lui cocher explicitement l\'accès en **lecture** à ' 'ce dépôt précis — ou utiliser un token *classic* avec la portée ' '`read`.\n\nVérifie aussi que `PRIVATE_REPO` est exact.' if denied else f'Erreur `{name}`. Vérifie `PRIVATE_REPO` et le token dans ' '*Settings → Variables and secrets*.') + '\n\nAucune donnée visiteur n\'est concernée : l\'application n\'a ' 'pas démarré.') try: # local_dir=APP_DIR : la charge utile atterrit AU MÊME endroit que le # chargeur, donc `import pipeline` et les chemins relatifs (redo/, derisk/) # fonctionnent sans rien réécrire — le code privé est identique à celui # qu'on exécute en local pendant le développement. snapshot_download(repo_id=REPO, repo_type='model', revision=REVISION, token=TOKEN, local_dir=APP_DIR) except Exception as e: # noqa: BLE001 # On n'imprime QUE le type : les logs d'un Space public sont lisibles, et un # message verbeux peut recracher une URL signée ou un en-tête d'autorisation. _placeholder('⚙️ Téléchargement du code échoué', f'`{type(e).__name__}`. Le dépôt est accessible mais le ' 'téléchargement a échoué — réessaie en redémarrant le Space.') if not os.path.exists(app): _placeholder('⚙️ Charge utile incomplète', f'`{REPO}` est lisible, mais ne contient pas `app.py` à sa ' 'racine. Vérifie ce que `deploy_split.sh` y a poussé.') sys.path.insert(0, APP_DIR) runpy.run_path(app, run_name='__main__') if __name__ == '__main__': main()