File size: 5,692 Bytes
73e5fea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""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 = "<user>/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()