docs: add motion storyboard plan
7d84a31 parent: c123cdd added
docs/plan-motion-storyboard.md +215 -0 | new file mode 100644 | ||
| @@ -0,0 +1,215 @@ | ||
| 1 | +# Plan — storyboard de motion design et bibliothèque d'inspirations | |
| 2 | + | |
| 3 | +Reprise d'idées d'**editing-os** (dossier `editing-os-starter/`, kit Claude Code + HyperFrames d'un autre | |
| 4 | +créateur, licence MIT, analysé le 09/10/2026). On en reprend les méthodes, pas les fichiers : ses scripts | |
| 5 | +supposent macOS et ses cartes sont en anglais, calées sur ses styles. | |
| 6 | + | |
| 7 | +## Où on en est | |
| 8 | + | |
| 9 | +**Fait (10/10/2026)** | |
| 10 | +- Dérush automatique : onglet **Daryl** de l'étape Dérush (`backend/studio/modules/projects/autoderush.py`, | |
| 11 | + `web/src/project/steps/derush/AutoDerush.vue`). Recalage des mots sur le son, silences avec respiration | |
| 12 | + selon le contexte, bégaiements, lecture des prises par Claude (coupes désignées en texte + ancre | |
| 13 | + `before`, jamais en timecode), contrôles (phrase encore dite deux fois, contenu coupé sans équivalent, | |
| 14 | + coupe introuvable). Rien n'est coupé sans « Appliquer ». Testé sur « Lever l'anonymat ? » : 35 coupes | |
| 15 | + de prises toutes retrouvées, 1 avertissement, ~1 min. | |
| 16 | +- Les transcriptions enregistrent leur moteur (`state.engine`, `transcript/tasks.py`). Dans « Lever | |
| 17 | + l'anonymat ? », les rushes 0021, 0024, 0025, 0026 et 0028 sont encore en Whisper (à refaire en ElevenLabs). | |
| 18 | + | |
| 19 | +- Phase 1.1 + 1.2 : `backend/studio/modules/projects/storyboard.py` (job `project_storyboard`, file | |
| 20 | + `storyboard`), routes `GET/POST /{pid}/montage/storyboard` et `POST …/storyboard/stop`. Le transcript de | |
| 21 | + la timeline est reconstruit mot à mot (`timeline_words`) ; Claude donne `line` + `anchor` + `until` | |
| 22 | + (jamais de timecode) ; `at` = mot d'ancre − 0,2 s, fin = dernier mot couvert + 0,5 s (2,5 à 15 s) ; | |
| 23 | + `fit` raccourcit un beat qui déborde ; `check` (refait à chaque lecture) signale `anchor`, `late` | |
| 24 | + (hors −0,2 / +1,8 s), `overlap`, `busy` (motion déjà là), `density`. Testé sur « Lever l'anonymat ? » | |
| 25 | + (17 min, densité équilibrée) : 32 beats en 2 min, toutes les ancres retrouvées, aucun avertissement. | |
| 26 | +- Phase 1.3 : le storyboard est **une étape à part** (`storyboard`, phase « long », avant Montage) : | |
| 27 | + `web/src/project/steps/StoryboardStep.vue` + `montage/useStoryboard.ts`. Beats en cartes larges | |
| 28 | + (quand / il dit + on montre / place, type, base du kit, son), aperçu fixe à droite avec la place du | |
| 29 | + beat dessinée dessus, bande de rythme en haut, ↑/↓ d'un beat à l'autre. Retouches par | |
| 30 | + `PUT …/storyboard`. « Valider → Montage » termine l'étape, sans rien générer. | |
| 31 | +- Places d'un beat : `full`, `left`, `right`, `top`, `lower` (l'ancien `side` est lu comme `right`). | |
| 32 | + Choisies sur un petit écran 16:9 cliquable. À la génération, Daryl reçoit 3 images de la vidéo | |
| 33 | + (20 / 50 / 80 % de la durée) pour mettre le visuel du côté libre. Les zooms restent au Montage. | |
| 34 | +- Chat du storyboard, sous l'aperçu (`storyboard_chat.py`, job `{pid}-storyboard-chat`, file `storyboard` ; | |
| 35 | + `web/src/project/steps/storyboard/StoryboardChat.vue`) : demande sur toute la vidéo ou sur le moment | |
| 36 | + choisi (beat sélectionné, sinon tête de lecture), avec l'image de la vidéo source à ce moment | |
| 37 | + (ffmpeg → `montage/frames/frame-<ms>.jpg`, lue par Daryl avec Read). Réponse structurée | |
| 38 | + (`add` / `update` / `remove`) : un beat ajouté est placé par ses mots exacts (`locate`), une ancre | |
| 39 | + introuvable est signalée. Les retouches à la main sont bloquées pendant sa réponse. Session reprise | |
| 40 | + d'un tour à l'autre (le transcript n'est envoyé qu'au premier). Pas encore testé avec Claude. | |
| 41 | +- Phase 1.4, dans le Montage (c'est là qu'on teste le rendu) : les beats cochés sans animation sont en | |
| 42 | + pointillés sur la piste motion (clic = la lecture va sur le beat) et en slide sur l'aperçu, à leur place | |
| 43 | + (`Preview.vue`, props `beats` / `create` / `anims`), avec « Créer l'animation », et « Créer les n | |
| 44 | + animations » dans la barre d'outils. `POST …/storyboard/animate` (`beats` : ids, sinon tous) : | |
| 45 | + `storyboard.compose` pose un motion par beat (`beat` = id du beat, place et durée validées) et son son | |
| 46 | + d'entrée (0,15 s avant, −12 dB), puis **une tâche par beat** (`beat_anim.py`, job `{pid}-anim-{beat}`) | |
| 47 | + écrit `motion/<id>.html` avec une fiche courte (`storyboard.brief`, mots du beat avec leurs instants, | |
| 48 | + `MOTION_RULES`, charte, modèle du kit). 3 files (`beat_anim_0..2`) : 3 créations à la fois, les autres | |
| 49 | + en file. État par beat dans `view.anim_jobs` (file, en cours + activité, échec) : sur la slide et dans | |
| 50 | + la barre « Animations » du Montage, avec Arrêter / Relancer. Un beat compte comme animé quand son HTML | |
| 51 | + existe (`storyboard.animated`) : un motion posé mais jamais écrit se relance. Le chat du monteur ne | |
| 52 | + compose plus de beats ; `contexte.md` liste les motions en cours de création pour qu'il n'y touche pas. | |
| 53 | + Lois chiffrées d'editing-os ajoutées à `MOTION_RULES`, « Fais le travail » retiré. | |
| 54 | +- **Types de beats** (`type`) : `motion`, `zoom`, `broll`, `transition` (un ancien beat est un motion). | |
| 55 | + - zoom : `zoom {scale 1,05–1,5, x, y}`, le centre borné pour que le cadre reste dans l'image (`zoom_of`), | |
| 56 | + 2 à 8 s, 0,1 s avant le mot ; posé tel quel sur la piste `zooms` (champ `beat`). | |
| 57 | + - broll : `src` (média visuel du projet ou de la bibliothèque, `storyboard.visuals`) ou, vide, une | |
| 58 | + suggestion « à trouver » avec `search`. Une vidéo va sur la piste `broll` ; une image devient un motion | |
| 59 | + animé par Daryl (copiée dans `motion/media/<beat>-<nom>`, plein écran ou sur un côté comme un portrait). | |
| 60 | + Les plans Image / Écran / B-roll du script deviennent des broll avec leur média attaché. | |
| 61 | + - transition : plein écran, 1,2 s, 0,9 s avant les premiers mots de la nouvelle partie ; un motion animé | |
| 62 | + par Daryl avec une fiche dédiée. | |
| 63 | + - Contrôles : chevauchement entre ce qui occupe l'écran (motion, broll, transition) et entre zooms, | |
| 64 | + `hidden` (zoom sous un plein écran), `busy` par piste ; la densité ne compte que les motions. | |
| 65 | + - Génération : choix des types à proposer (`types`). Écran : filtres par type, couleurs, carte par type | |
| 66 | + (zoom : grossissement + cadre cliquable sur l'image de la vidéo ; broll : média, aperçu, recherche à | |
| 67 | + copier, lien Assets). Montage : « Créer les n animations » et « Poser n zooms et B-roll » ; un zoom ou | |
| 68 | + un B-roll vidéo pas encore posé se voit déjà dans l'aperçu. `POST …/animate` rend | |
| 69 | + `{queued, placed, skipped, missing}`. Pas encore testé avec une vraie génération de Claude. | |
| 70 | +- **Suite décidée** : retours image par image dans le Montage (temps local du motion + beat + HTML | |
| 71 | + envoyés à Daryl, sans rendu ; retours accumulés puis envoyés ; un retour généralisable devient une règle). | |
| 72 | +- Plans du script : les plans Animation / Texte à l'écran de `script.json` sont les beats de départ | |
| 73 | + (`origin: "script"`, `plan` = id du plan), repérés dans la timeline par vote de triplets de mots | |
| 74 | + (`find_passage`) ; Daryl les détaille sans les remplacer et n'ajoute des beats (`origin: "daryl"`) que | |
| 75 | + pour la densité ; un plan qu'il oublie est posé tel quel ; un plan introuvable est signalé. Les plans | |
| 76 | + Image / Écran / B-roll / Insert / Plan de coupe occupent l'écran : contrôle `cover`. Les plans sont | |
| 77 | + aussi listés dans `contexte.md` pour le chat du monteur. « Lever l'anonymat ? » : 12 plans repris sur 13 | |
| 78 | + (« 35 millions de comptes » dit autrement), 21 beats ajoutés, aucun avertissement. | |
| 79 | + | |
| 80 | +- **Bibliothèque de transitions** (10/10/2026) : `backend/studio/modules/projects/transitions.py`, les 55 | |
| 81 | + transitions `xfade` de ffmpeg 7.1 en 5 familles (fondus, balayages, glissements, formes, effets ; `squeezev` | |
| 82 | + retirée, elle fait planter ffmpeg). Timeline : `transition` de l'A-roll = `{fx, dur}` (un ancien nombre est lu | |
| 83 | + comme un fondu), `enter` / `exit` sur B-roll et motions. Rendu (`render.overlay_groups`) : entrée et sortie = | |
| 84 | + `xfade` contre une image transparente, un plan collé au précédent de sa piste avec une entrée s'enchaîne avec | |
| 85 | + lui (centré sur la coupe, vraies images du média autour). Galerie : démos rendues par ffmpeg | |
| 86 | + (`data/cache/transitions/`), onglet Transitions de la Bibliothèque, sélecteur dans l'inspecteur du Montage et | |
| 87 | + sur les cartes motion / B-roll du storyboard. Aperçu du Montage en CSS (`web/src/lib/transitions.ts`, | |
| 88 | + `layersAt`), approché pour lamelles, vent, pixels, dissolution. Daryl propose `enter_fx` / `exit_fx` au | |
| 89 | + storyboard et connaît le catalogue au chat du Montage. Resolve : fondu enchaîné à la place des autres | |
| 90 | + transitions de l'A-roll, coupes franches pour les entrées / sorties (signalé à l'export). | |
| 91 | + | |
| 92 | +**Pas encore fait, à garder en tête** | |
| 93 | +- Vérification après rendu (re-transcrire le MP4 exporté et vérifier que les coupes ont porté). | |
| 94 | +- Mastering -14 LUFS (loudnorm deux passes, puis `aresample=48000`, `alimiter`, `aformat=s16` avant l'AAC : | |
| 95 | + voir `editing-os-starter/scripts/master.py`). | |
| 96 | +- Export de la timeline du dérush : remis à plus tard par l'utilisateur. | |
| 97 | + | |
| 98 | +## Constat de départ (étape Montage) | |
| 99 | + | |
| 100 | +- Daryl crée les motions **directement** dans `montage/timeline.json` + `montage/motion/<id>.html`. Son | |
| 101 | + prompt dit « Fais le travail, ne propose pas seulement » (`montage_tasks.py`), ce qui contredit la règle | |
| 102 | + de l'utilisateur : l'IA propose, l'utilisateur décide. | |
| 103 | +- Une seule charte (`animations/kit/style.css`) et 2 exemples (`exemple-arbre`, `exemple-chronologie`). | |
| 104 | +- Les shorts ont leur propre flux de slides (`animations/service.py`), avec des densités sobre / équilibrée / dense. | |
| 105 | + | |
| 106 | +## Phase 1 — Storyboard dans l'étape Montage | |
| 107 | + | |
| 108 | +But : Daryl propose d'abord un storyboard, l'utilisateur le valide, et seulement ensuite il compose. | |
| 109 | +Méthode reprise de `editing-os-starter/.claude/skills/design-beats/SKILL.md`. | |
| 110 | + | |
| 111 | +### 1.1 Génération (job `project_storyboard`, sortie structurée) | |
| 112 | +- Entrées : transcript en temps timeline (comme `contexte.md`), chapitres, script, charte, densité choisie, | |
| 113 | + sélection d'inspirations (phase 2). | |
| 114 | +- La question pour chaque passage : « qu'est-ce qu'on **montre** pendant qu'il dit ça ? », jamais « quel | |
| 115 | + texte on affiche ». Grille de traduction adaptée à la vidéo longue éducative : chiffre → compteur qui se | |
| 116 | + construit ; processus → schéma qui s'assemble ; comparaison → A/B ; date → chronologie ; outil cité → | |
| 117 | + capture ou logo ; liste → éléments qui se cochent. Interdits : carte-label statique, texte qui répète la | |
| 118 | + parole, décor. | |
| 119 | +- Un beat : `{id, at, dur, anchor (mots exacts du départ), says, show, kind, place (plein écran | côté | | |
| 120 | + bas), motion (entrée / entretien / sortie), sfx, ref (inspiration, phase 2), why}`. | |
| 121 | +- Stockage : `montage/storyboard.json`. | |
| 122 | + | |
| 123 | +### 1.2 Contrôles automatiques | |
| 124 | +- L'ancre existe dans le transcript, et le beat démarre entre 0,2 s avant et 1,8 s après le mot ancré | |
| 125 | + (« Gate 0 » d'editing-os) : sinon on recale ou on signale. | |
| 126 | +- Pas de chevauchement ; densité réelle comparée à la densité demandée. | |
| 127 | +- Ancre introuvable : signalée, pas devinée (comme le dérush automatique). | |
| 128 | + | |
| 129 | +### 1.3 Écran (onglet Storyboard de l'étape Montage, à côté du chat) | |
| 130 | +- Une ligne par beat : timecode cliquable, « il dit », « on montre » modifiable, type, place, case à cocher, | |
| 131 | + supprimer, « refaire ce beat ». | |
| 132 | +- Les beats en fantôme sur la piste motion de la timeline, pour juger le rythme. | |
| 133 | +- Bouton « Composer les n beats validés ». | |
| 134 | + | |
| 135 | +### 1.4 Composition depuis le storyboard | |
| 136 | +- Daryl ne compose que les beats validés, en suivant « on montre » ; tout écart est re-proposé. | |
| 137 | +- Le chat garde l'édition directe pour les petites demandes explicites ; une création d'ensemble passe par | |
| 138 | + le storyboard. Retirer « Fais le travail, ne propose pas seulement ». | |
| 139 | +- Ajouter à `MOTION_RULES` les lois chiffrées d'editing-os (`MOTION_PHILOSOPHY.md`, skill `motion-doctrine`) : | |
| 140 | + une idée par beat ; visuel 0,2 s avant le mot ; sortie ≈ 75 % de la durée d'entrée ; pas d'oscillation | |
| 141 | + décorative (`repeat: -1`, yoyo) ; immobilité de 0,3 à 0,75 s avant un climax ; la timeline doit durer | |
| 142 | + toute la composition (`tl.to({}, {duration}, 0)`), sinon flash noir. | |
| 143 | + | |
| 144 | +## Phase 2 — Bibliothèque d'inspirations | |
| 145 | + | |
| 146 | +But : une galerie de références de motion (démo, mécanique, prompt) dans laquelle l'utilisateur choisit et | |
| 147 | +annote ce que Daryl doit suivre. Modèle : `editing-os-starter/motion-library/` et | |
| 148 | +`scripts/motion-library/` (import, catégories, planches), `editing-os/lib/selection.mjs`. | |
| 149 | + | |
| 150 | +### 2.1 Données | |
| 151 | +- Base : `editing-os-starter/motion-library/analyse.json` — 270 références analysées (54 pépites, | |
| 152 | + 159 solides, 57 faibles ; 15 usages : explicatif, data, typo, UI, produit…). Chaque fiche : | |
| 153 | + `titre, resume, usage, usages, ambiance, mecanique {transitions, rythme, texte, techniques}, niveau, | |
| 154 | + pourquoi`. | |
| 155 | +- Import porté en Python : démos, posters et prompts depuis julienpocinho.fr et prompt-motion.com | |
| 156 | + (décodage des morceaux RSC Next.js, voir `import.mjs`). 4 requêtes à la fois au plus. | |
| 157 | +- Planche de 12 images (4×3, 480×270) par démo avec ffmpeg, lue directement depuis l'URL ; vignette = le | |
| 158 | + JPEG le plus lourd entre 25 et 85 % de la durée (les images noires pèsent presque rien). | |
| 159 | +- Stockage local `data/library/inspirations/` : **contenu de tiers, jamais dans le dépôt** (on ne versionne | |
| 160 | + que l'importeur). | |
| 161 | +- Références de l'utilisateur : URL ou clip (téléchargement yt-dlp existant) ; Claude regarde la planche et | |
| 162 | + remplit la fiche « mécanique » au même format. | |
| 163 | + | |
| 164 | +### 2.2 Écran (onglet Inspirations de la page Bibliothèque) | |
| 165 | +- Grille de posters, démo au survol ; filtres usage / niveau / ambiance ; les faibles masqués par défaut. | |
| 166 | +- Fiche : démo, planche, mécanique, prompt d'origine. « Ajouter à ma sélection » avec une note. | |
| 167 | + | |
| 168 | +### 2.3 Lien avec le storyboard | |
| 169 | +- Daryl reçoit la sélection et ses notes (prioritaires), puis l'index par usage ; il cite une référence par | |
| 170 | + beat avancé, affichée en vignette sur la ligne du beat. | |
| 171 | +- On reprend la mécanique (mouvement, rythme, transitions), jamais l'habillage : la charte reste celle de | |
| 172 | + l'utilisateur. | |
| 173 | + | |
| 174 | +## Plus tard — styles (design system) | |
| 175 | +Plusieurs chartes au choix par projet (`tokens.css` + `DESIGN.md`), puis des cartes à emplacements de texte | |
| 176 | +(`data-slot` + `maxChars`), rangées par tier (t1 plein écran opaque, t2 transparent qui ne couvre pas le | |
| 177 | +visage) et par usage, avec une registry et un tirage reproductible sans répétition consécutive (voir | |
| 178 | +`editing-os-starter/style-library/GUIDE.md`, `.claude/skills/motion-graphics/`). Le champ `kind` / `place` | |
| 179 | +du storyboard deviendra le choix d'une carte. | |
| 180 | + | |
| 181 | +## Ordre | |
| 182 | +1. Phase 1.1 + 1.2, testées sur un projet réel sans interface. | |
| 183 | +2. Phase 1.3 + 1.4 : utilisable pour de vrai. | |
| 184 | +3. Phase 2.1. | |
| 185 | +4. Phase 2.2 + 2.3. | |
| 186 | + | |
| 187 | +## Décisions (10/10/2026) | |
| 188 | +1. **Vidéo longue uniquement** pour l'instant (les shorts plus tard). | |
| 189 | +2. Densité : reprendre les trois densités des slides (`animations/service.py`, `DENSITIES`) : sobre | |
| 190 | + (~1 motion / 1 min 30), équilibrée (~1 / 30-45 s), dense (~1 / 15-20 s). Le rythme des reels | |
| 191 | + d'editing-os (un événement toutes les 1 à 2 s) est trop dense pour une vidéo longue. **« Équilibrée » | |
| 192 | + par défaut, réglable à chaque génération** (validé). | |
| 193 | +3. **Import complet** des inspirations (démos, posters, prompts des deux sites, en local). | |
| 194 | + | |
| 195 | +## Notes d'outillage (réponses du 10/10/2026) | |
| 196 | +- **Analyser une animation** : editing-os ne décompose pas image par image. ffmpeg tire une planche de | |
| 197 | + 12 images réparties sur la démo, Claude la regarde (vision) avec le prompt d'origine et écrit la fiche | |
| 198 | + « mécanique ». Ça capte la composition et les états, pas les courbes ni les timings fins. Pour reproduire | |
| 199 | + une transition précise : une planche dense autour du moment (8 à 12 images par seconde sur 1 à 2 s), | |
| 200 | + repéré par détection de changement de plan (`select='gt(scene,0.15)'`). | |
| 201 | +- **Détourage** : CLI HyperFrames, `npx hyperframes remove-background` (modèle u2net_human_seg, ~168 Mo, | |
| 202 | + local, gratuit). Sortie ProRes 4444 ou WebM VP9 avec alpha. Lent : ~2 images/s en 1080p sur CPU, donc | |
| 203 | + à réserver aux passages qui en ont besoin. Re-encoder d'abord une source à cadence variable (sinon sujet | |
| 204 | + dédoublé), et appliquer les mêmes coupes à la vidéo et au détourage. Usages : texte derrière la personne | |
| 205 | + (on rend vidéo + texte, puis on recolle la personne détourée par-dessus), personne qui dépasse d'une carte. | |
| 206 | +- **Musique et sons** : | |
| 207 | + - editing-os : un pack de SFX maison nommés par rôle (`whoosh`, `pop`, `click`, `cash`, `impact`, | |
| 208 | + `riser`, `buzzer`…) avec une table son ↔ animation et un volume par son (`asset-library/sfx/README.md`) ; | |
| 209 | + la musique via `npx hyperframes media-use resolve --type bgm` (catalogue HeyGen, compte heygen | |
| 210 | + requis) ; le mixage (ducking, fondus) via le skill `hyperframes-audio`. | |
| 211 | + - Studio : bibliothèque `data/library/` (musique, sfx), Daryl ne place que des fichiers listés dans | |
| 212 | + `contexte.md`, gains fixes, pas de ducking. | |
| 213 | + - À reprendre : nommer les SFX par rôle et donner la table son ↔ animation à Daryl ; un `sfx` par beat | |
| 214 | + dans le storyboard ; ducking de la musique sous la voix au rendu (`sidechaincompress` ffmpeg) ; le | |
| 215 | + catalogue HeyGen seulement si l'utilisateur veut un compte de plus. | |
| new file mode 100644 | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | +# Plan — storyboard de motion design et bibliothèque d'inspirations | ||
| 2 | + | ||
| 3 | +Reprise d'idées d'**editing-os** (dossier `editing-os-starter/`, kit Claude Code + HyperFrames d'un autre | ||
| 4 | +créateur, licence MIT, analysé le 09/10/2026). On en reprend les méthodes, pas les fichiers : ses scripts | ||
| 5 | +supposent macOS et ses cartes sont en anglais, calées sur ses styles. | ||
| 6 | + | ||
| 7 | +## Où on en est | ||
| 8 | + | ||
| 9 | +**Fait (10/10/2026)** | ||
| 10 | +- Dérush automatique : onglet **Daryl** de l'étape Dérush (`backend/studio/modules/projects/autoderush.py`, | ||
| 11 | + `web/src/project/steps/derush/AutoDerush.vue`). Recalage des mots sur le son, silences avec respiration | ||
| 12 | + selon le contexte, bégaiements, lecture des prises par Claude (coupes désignées en texte + ancre | ||
| 13 | + `before`, jamais en timecode), contrôles (phrase encore dite deux fois, contenu coupé sans équivalent, | ||
| 14 | + coupe introuvable). Rien n'est coupé sans « Appliquer ». Testé sur « Lever l'anonymat ? » : 35 coupes | ||
| 15 | + de prises toutes retrouvées, 1 avertissement, ~1 min. | ||
| 16 | +- Les transcriptions enregistrent leur moteur (`state.engine`, `transcript/tasks.py`). Dans « Lever | ||
| 17 | + l'anonymat ? », les rushes 0021, 0024, 0025, 0026 et 0028 sont encore en Whisper (à refaire en ElevenLabs). | ||
| 18 | + | ||
| 19 | +- Phase 1.1 + 1.2 : `backend/studio/modules/projects/storyboard.py` (job `project_storyboard`, file | ||
| 20 | + `storyboard`), routes `GET/POST /{pid}/montage/storyboard` et `POST …/storyboard/stop`. Le transcript de | ||
| 21 | + la timeline est reconstruit mot à mot (`timeline_words`) ; Claude donne `line` + `anchor` + `until` | ||
| 22 | + (jamais de timecode) ; `at` = mot d'ancre − 0,2 s, fin = dernier mot couvert + 0,5 s (2,5 à 15 s) ; | ||
| 23 | + `fit` raccourcit un beat qui déborde ; `check` (refait à chaque lecture) signale `anchor`, `late` | ||
| 24 | + (hors −0,2 / +1,8 s), `overlap`, `busy` (motion déjà là), `density`. Testé sur « Lever l'anonymat ? » | ||
| 25 | + (17 min, densité équilibrée) : 32 beats en 2 min, toutes les ancres retrouvées, aucun avertissement. | ||
| 26 | +- Phase 1.3 : le storyboard est **une étape à part** (`storyboard`, phase « long », avant Montage) : | ||
| 27 | + `web/src/project/steps/StoryboardStep.vue` + `montage/useStoryboard.ts`. Beats en cartes larges | ||
| 28 | + (quand / il dit + on montre / place, type, base du kit, son), aperçu fixe à droite avec la place du | ||
| 29 | + beat dessinée dessus, bande de rythme en haut, ↑/↓ d'un beat à l'autre. Retouches par | ||
| 30 | + `PUT …/storyboard`. « Valider → Montage » termine l'étape, sans rien générer. | ||
| 31 | +- Places d'un beat : `full`, `left`, `right`, `top`, `lower` (l'ancien `side` est lu comme `right`). | ||
| 32 | + Choisies sur un petit écran 16:9 cliquable. À la génération, Daryl reçoit 3 images de la vidéo | ||
| 33 | + (20 / 50 / 80 % de la durée) pour mettre le visuel du côté libre. Les zooms restent au Montage. | ||
| 34 | +- Chat du storyboard, sous l'aperçu (`storyboard_chat.py`, job `{pid}-storyboard-chat`, file `storyboard` ; | ||
| 35 | + `web/src/project/steps/storyboard/StoryboardChat.vue`) : demande sur toute la vidéo ou sur le moment | ||
| 36 | + choisi (beat sélectionné, sinon tête de lecture), avec l'image de la vidéo source à ce moment | ||
| 37 | + (ffmpeg → `montage/frames/frame-<ms>.jpg`, lue par Daryl avec Read). Réponse structurée | ||
| 38 | + (`add` / `update` / `remove`) : un beat ajouté est placé par ses mots exacts (`locate`), une ancre | ||
| 39 | + introuvable est signalée. Les retouches à la main sont bloquées pendant sa réponse. Session reprise | ||
| 40 | + d'un tour à l'autre (le transcript n'est envoyé qu'au premier). Pas encore testé avec Claude. | ||
| 41 | +- Phase 1.4, dans le Montage (c'est là qu'on teste le rendu) : les beats cochés sans animation sont en | ||
| 42 | + pointillés sur la piste motion (clic = la lecture va sur le beat) et en slide sur l'aperçu, à leur place | ||
| 43 | + (`Preview.vue`, props `beats` / `create` / `anims`), avec « Créer l'animation », et « Créer les n | ||
| 44 | + animations » dans la barre d'outils. `POST …/storyboard/animate` (`beats` : ids, sinon tous) : | ||
| 45 | + `storyboard.compose` pose un motion par beat (`beat` = id du beat, place et durée validées) et son son | ||
| 46 | + d'entrée (0,15 s avant, −12 dB), puis **une tâche par beat** (`beat_anim.py`, job `{pid}-anim-{beat}`) | ||
| 47 | + écrit `motion/<id>.html` avec une fiche courte (`storyboard.brief`, mots du beat avec leurs instants, | ||
| 48 | + `MOTION_RULES`, charte, modèle du kit). 3 files (`beat_anim_0..2`) : 3 créations à la fois, les autres | ||
| 49 | + en file. État par beat dans `view.anim_jobs` (file, en cours + activité, échec) : sur la slide et dans | ||
| 50 | + la barre « Animations » du Montage, avec Arrêter / Relancer. Un beat compte comme animé quand son HTML | ||
| 51 | + existe (`storyboard.animated`) : un motion posé mais jamais écrit se relance. Le chat du monteur ne | ||
| 52 | + compose plus de beats ; `contexte.md` liste les motions en cours de création pour qu'il n'y touche pas. | ||
| 53 | + Lois chiffrées d'editing-os ajoutées à `MOTION_RULES`, « Fais le travail » retiré. | ||
| 54 | +- **Types de beats** (`type`) : `motion`, `zoom`, `broll`, `transition` (un ancien beat est un motion). | ||
| 55 | + - zoom : `zoom {scale 1,05–1,5, x, y}`, le centre borné pour que le cadre reste dans l'image (`zoom_of`), | ||
| 56 | + 2 à 8 s, 0,1 s avant le mot ; posé tel quel sur la piste `zooms` (champ `beat`). | ||
| 57 | + - broll : `src` (média visuel du projet ou de la bibliothèque, `storyboard.visuals`) ou, vide, une | ||
| 58 | + suggestion « à trouver » avec `search`. Une vidéo va sur la piste `broll` ; une image devient un motion | ||
| 59 | + animé par Daryl (copiée dans `motion/media/<beat>-<nom>`, plein écran ou sur un côté comme un portrait). | ||
| 60 | + Les plans Image / Écran / B-roll du script deviennent des broll avec leur média attaché. | ||
| 61 | + - transition : plein écran, 1,2 s, 0,9 s avant les premiers mots de la nouvelle partie ; un motion animé | ||
| 62 | + par Daryl avec une fiche dédiée. | ||
| 63 | + - Contrôles : chevauchement entre ce qui occupe l'écran (motion, broll, transition) et entre zooms, | ||
| 64 | + `hidden` (zoom sous un plein écran), `busy` par piste ; la densité ne compte que les motions. | ||
| 65 | + - Génération : choix des types à proposer (`types`). Écran : filtres par type, couleurs, carte par type | ||
| 66 | + (zoom : grossissement + cadre cliquable sur l'image de la vidéo ; broll : média, aperçu, recherche à | ||
| 67 | + copier, lien Assets). Montage : « Créer les n animations » et « Poser n zooms et B-roll » ; un zoom ou | ||
| 68 | + un B-roll vidéo pas encore posé se voit déjà dans l'aperçu. `POST …/animate` rend | ||
| 69 | + `{queued, placed, skipped, missing}`. Pas encore testé avec une vraie génération de Claude. | ||
| 70 | +- **Suite décidée** : retours image par image dans le Montage (temps local du motion + beat + HTML | ||
| 71 | + envoyés à Daryl, sans rendu ; retours accumulés puis envoyés ; un retour généralisable devient une règle). | ||
| 72 | +- Plans du script : les plans Animation / Texte à l'écran de `script.json` sont les beats de départ | ||
| 73 | + (`origin: "script"`, `plan` = id du plan), repérés dans la timeline par vote de triplets de mots | ||
| 74 | + (`find_passage`) ; Daryl les détaille sans les remplacer et n'ajoute des beats (`origin: "daryl"`) que | ||
| 75 | + pour la densité ; un plan qu'il oublie est posé tel quel ; un plan introuvable est signalé. Les plans | ||
| 76 | + Image / Écran / B-roll / Insert / Plan de coupe occupent l'écran : contrôle `cover`. Les plans sont | ||
| 77 | + aussi listés dans `contexte.md` pour le chat du monteur. « Lever l'anonymat ? » : 12 plans repris sur 13 | ||
| 78 | + (« 35 millions de comptes » dit autrement), 21 beats ajoutés, aucun avertissement. | ||
| 79 | + | ||
| 80 | +- **Bibliothèque de transitions** (10/10/2026) : `backend/studio/modules/projects/transitions.py`, les 55 | ||
| 81 | + transitions `xfade` de ffmpeg 7.1 en 5 familles (fondus, balayages, glissements, formes, effets ; `squeezev` | ||
| 82 | + retirée, elle fait planter ffmpeg). Timeline : `transition` de l'A-roll = `{fx, dur}` (un ancien nombre est lu | ||
| 83 | + comme un fondu), `enter` / `exit` sur B-roll et motions. Rendu (`render.overlay_groups`) : entrée et sortie = | ||
| 84 | + `xfade` contre une image transparente, un plan collé au précédent de sa piste avec une entrée s'enchaîne avec | ||
| 85 | + lui (centré sur la coupe, vraies images du média autour). Galerie : démos rendues par ffmpeg | ||
| 86 | + (`data/cache/transitions/`), onglet Transitions de la Bibliothèque, sélecteur dans l'inspecteur du Montage et | ||
| 87 | + sur les cartes motion / B-roll du storyboard. Aperçu du Montage en CSS (`web/src/lib/transitions.ts`, | ||
| 88 | + `layersAt`), approché pour lamelles, vent, pixels, dissolution. Daryl propose `enter_fx` / `exit_fx` au | ||
| 89 | + storyboard et connaît le catalogue au chat du Montage. Resolve : fondu enchaîné à la place des autres | ||
| 90 | + transitions de l'A-roll, coupes franches pour les entrées / sorties (signalé à l'export). | ||
| 91 | + | ||
| 92 | +**Pas encore fait, à garder en tête** | ||
| 93 | +- Vérification après rendu (re-transcrire le MP4 exporté et vérifier que les coupes ont porté). | ||
| 94 | +- Mastering -14 LUFS (loudnorm deux passes, puis `aresample=48000`, `alimiter`, `aformat=s16` avant l'AAC : | ||
| 95 | + voir `editing-os-starter/scripts/master.py`). | ||
| 96 | +- Export de la timeline du dérush : remis à plus tard par l'utilisateur. | ||
| 97 | + | ||
| 98 | +## Constat de départ (étape Montage) | ||
| 99 | + | ||
| 100 | +- Daryl crée les motions **directement** dans `montage/timeline.json` + `montage/motion/<id>.html`. Son | ||
| 101 | + prompt dit « Fais le travail, ne propose pas seulement » (`montage_tasks.py`), ce qui contredit la règle | ||
| 102 | + de l'utilisateur : l'IA propose, l'utilisateur décide. | ||
| 103 | +- Une seule charte (`animations/kit/style.css`) et 2 exemples (`exemple-arbre`, `exemple-chronologie`). | ||
| 104 | +- Les shorts ont leur propre flux de slides (`animations/service.py`), avec des densités sobre / équilibrée / dense. | ||
| 105 | + | ||
| 106 | +## Phase 1 — Storyboard dans l'étape Montage | ||
| 107 | + | ||
| 108 | +But : Daryl propose d'abord un storyboard, l'utilisateur le valide, et seulement ensuite il compose. | ||
| 109 | +Méthode reprise de `editing-os-starter/.claude/skills/design-beats/SKILL.md`. | ||
| 110 | + | ||
| 111 | +### 1.1 Génération (job `project_storyboard`, sortie structurée) | ||
| 112 | +- Entrées : transcript en temps timeline (comme `contexte.md`), chapitres, script, charte, densité choisie, | ||
| 113 | + sélection d'inspirations (phase 2). | ||
| 114 | +- La question pour chaque passage : « qu'est-ce qu'on **montre** pendant qu'il dit ça ? », jamais « quel | ||
| 115 | + texte on affiche ». Grille de traduction adaptée à la vidéo longue éducative : chiffre → compteur qui se | ||
| 116 | + construit ; processus → schéma qui s'assemble ; comparaison → A/B ; date → chronologie ; outil cité → | ||
| 117 | + capture ou logo ; liste → éléments qui se cochent. Interdits : carte-label statique, texte qui répète la | ||
| 118 | + parole, décor. | ||
| 119 | +- Un beat : `{id, at, dur, anchor (mots exacts du départ), says, show, kind, place (plein écran | côté | | ||
| 120 | + bas), motion (entrée / entretien / sortie), sfx, ref (inspiration, phase 2), why}`. | ||
| 121 | +- Stockage : `montage/storyboard.json`. | ||
| 122 | + | ||
| 123 | +### 1.2 Contrôles automatiques | ||
| 124 | +- L'ancre existe dans le transcript, et le beat démarre entre 0,2 s avant et 1,8 s après le mot ancré | ||
| 125 | + (« Gate 0 » d'editing-os) : sinon on recale ou on signale. | ||
| 126 | +- Pas de chevauchement ; densité réelle comparée à la densité demandée. | ||
| 127 | +- Ancre introuvable : signalée, pas devinée (comme le dérush automatique). | ||
| 128 | + | ||
| 129 | +### 1.3 Écran (onglet Storyboard de l'étape Montage, à côté du chat) | ||
| 130 | +- Une ligne par beat : timecode cliquable, « il dit », « on montre » modifiable, type, place, case à cocher, | ||
| 131 | + supprimer, « refaire ce beat ». | ||
| 132 | +- Les beats en fantôme sur la piste motion de la timeline, pour juger le rythme. | ||
| 133 | +- Bouton « Composer les n beats validés ». | ||
| 134 | + | ||
| 135 | +### 1.4 Composition depuis le storyboard | ||
| 136 | +- Daryl ne compose que les beats validés, en suivant « on montre » ; tout écart est re-proposé. | ||
| 137 | +- Le chat garde l'édition directe pour les petites demandes explicites ; une création d'ensemble passe par | ||
| 138 | + le storyboard. Retirer « Fais le travail, ne propose pas seulement ». | ||
| 139 | +- Ajouter à `MOTION_RULES` les lois chiffrées d'editing-os (`MOTION_PHILOSOPHY.md`, skill `motion-doctrine`) : | ||
| 140 | + une idée par beat ; visuel 0,2 s avant le mot ; sortie ≈ 75 % de la durée d'entrée ; pas d'oscillation | ||
| 141 | + décorative (`repeat: -1`, yoyo) ; immobilité de 0,3 à 0,75 s avant un climax ; la timeline doit durer | ||
| 142 | + toute la composition (`tl.to({}, {duration}, 0)`), sinon flash noir. | ||
| 143 | + | ||
| 144 | +## Phase 2 — Bibliothèque d'inspirations | ||
| 145 | + | ||
| 146 | +But : une galerie de références de motion (démo, mécanique, prompt) dans laquelle l'utilisateur choisit et | ||
| 147 | +annote ce que Daryl doit suivre. Modèle : `editing-os-starter/motion-library/` et | ||
| 148 | +`scripts/motion-library/` (import, catégories, planches), `editing-os/lib/selection.mjs`. | ||
| 149 | + | ||
| 150 | +### 2.1 Données | ||
| 151 | +- Base : `editing-os-starter/motion-library/analyse.json` — 270 références analysées (54 pépites, | ||
| 152 | + 159 solides, 57 faibles ; 15 usages : explicatif, data, typo, UI, produit…). Chaque fiche : | ||
| 153 | + `titre, resume, usage, usages, ambiance, mecanique {transitions, rythme, texte, techniques}, niveau, | ||
| 154 | + pourquoi`. | ||
| 155 | +- Import porté en Python : démos, posters et prompts depuis julienpocinho.fr et prompt-motion.com | ||
| 156 | + (décodage des morceaux RSC Next.js, voir `import.mjs`). 4 requêtes à la fois au plus. | ||
| 157 | +- Planche de 12 images (4×3, 480×270) par démo avec ffmpeg, lue directement depuis l'URL ; vignette = le | ||
| 158 | + JPEG le plus lourd entre 25 et 85 % de la durée (les images noires pèsent presque rien). | ||
| 159 | +- Stockage local `data/library/inspirations/` : **contenu de tiers, jamais dans le dépôt** (on ne versionne | ||
| 160 | + que l'importeur). | ||
| 161 | +- Références de l'utilisateur : URL ou clip (téléchargement yt-dlp existant) ; Claude regarde la planche et | ||
| 162 | + remplit la fiche « mécanique » au même format. | ||
| 163 | + | ||
| 164 | +### 2.2 Écran (onglet Inspirations de la page Bibliothèque) | ||
| 165 | +- Grille de posters, démo au survol ; filtres usage / niveau / ambiance ; les faibles masqués par défaut. | ||
| 166 | +- Fiche : démo, planche, mécanique, prompt d'origine. « Ajouter à ma sélection » avec une note. | ||
| 167 | + | ||
| 168 | +### 2.3 Lien avec le storyboard | ||
| 169 | +- Daryl reçoit la sélection et ses notes (prioritaires), puis l'index par usage ; il cite une référence par | ||
| 170 | + beat avancé, affichée en vignette sur la ligne du beat. | ||
| 171 | +- On reprend la mécanique (mouvement, rythme, transitions), jamais l'habillage : la charte reste celle de | ||
| 172 | + l'utilisateur. | ||
| 173 | + | ||
| 174 | +## Plus tard — styles (design system) | ||
| 175 | +Plusieurs chartes au choix par projet (`tokens.css` + `DESIGN.md`), puis des cartes à emplacements de texte | ||
| 176 | +(`data-slot` + `maxChars`), rangées par tier (t1 plein écran opaque, t2 transparent qui ne couvre pas le | ||
| 177 | +visage) et par usage, avec une registry et un tirage reproductible sans répétition consécutive (voir | ||
| 178 | +`editing-os-starter/style-library/GUIDE.md`, `.claude/skills/motion-graphics/`). Le champ `kind` / `place` | ||
| 179 | +du storyboard deviendra le choix d'une carte. | ||
| 180 | + | ||
| 181 | +## Ordre | ||
| 182 | +1. Phase 1.1 + 1.2, testées sur un projet réel sans interface. | ||
| 183 | +2. Phase 1.3 + 1.4 : utilisable pour de vrai. | ||
| 184 | +3. Phase 2.1. | ||
| 185 | +4. Phase 2.2 + 2.3. | ||
| 186 | + | ||
| 187 | +## Décisions (10/10/2026) | ||
| 188 | +1. **Vidéo longue uniquement** pour l'instant (les shorts plus tard). | ||
| 189 | +2. Densité : reprendre les trois densités des slides (`animations/service.py`, `DENSITIES`) : sobre | ||
| 190 | + (~1 motion / 1 min 30), équilibrée (~1 / 30-45 s), dense (~1 / 15-20 s). Le rythme des reels | ||
| 191 | + d'editing-os (un événement toutes les 1 à 2 s) est trop dense pour une vidéo longue. **« Équilibrée » | ||
| 192 | + par défaut, réglable à chaque génération** (validé). | ||
| 193 | +3. **Import complet** des inspirations (démos, posters, prompts des deux sites, en local). | ||
| 194 | + | ||
| 195 | +## Notes d'outillage (réponses du 10/10/2026) | ||
| 196 | +- **Analyser une animation** : editing-os ne décompose pas image par image. ffmpeg tire une planche de | ||
| 197 | + 12 images réparties sur la démo, Claude la regarde (vision) avec le prompt d'origine et écrit la fiche | ||
| 198 | + « mécanique ». Ça capte la composition et les états, pas les courbes ni les timings fins. Pour reproduire | ||
| 199 | + une transition précise : une planche dense autour du moment (8 à 12 images par seconde sur 1 à 2 s), | ||
| 200 | + repéré par détection de changement de plan (`select='gt(scene,0.15)'`). | ||
| 201 | +- **Détourage** : CLI HyperFrames, `npx hyperframes remove-background` (modèle u2net_human_seg, ~168 Mo, | ||
| 202 | + local, gratuit). Sortie ProRes 4444 ou WebM VP9 avec alpha. Lent : ~2 images/s en 1080p sur CPU, donc | ||
| 203 | + à réserver aux passages qui en ont besoin. Re-encoder d'abord une source à cadence variable (sinon sujet | ||
| 204 | + dédoublé), et appliquer les mêmes coupes à la vidéo et au détourage. Usages : texte derrière la personne | ||
| 205 | + (on rend vidéo + texte, puis on recolle la personne détourée par-dessus), personne qui dépasse d'une carte. | ||
| 206 | +- **Musique et sons** : | ||
| 207 | + - editing-os : un pack de SFX maison nommés par rôle (`whoosh`, `pop`, `click`, `cash`, `impact`, | ||
| 208 | + `riser`, `buzzer`…) avec une table son ↔ animation et un volume par son (`asset-library/sfx/README.md`) ; | ||
| 209 | + la musique via `npx hyperframes media-use resolve --type bgm` (catalogue HeyGen, compte heygen | ||
| 210 | + requis) ; le mixage (ducking, fondus) via le skill `hyperframes-audio`. | ||
| 211 | + - Studio : bibliothèque `data/library/` (musique, sfx), Daryl ne place que des fichiers listés dans | ||
| 212 | + `contexte.md`, gains fixes, pas de ducking. | ||
| 213 | + - À reprendre : nommer les SFX par rôle et donner la table son ↔ animation à Daryl ; un `sfx` par beat | ||
| 214 | + dans le storyboard ; ducking de la musique sous la voix au rendu (`sidechaincompress` ffmpeg) ; le | ||
| 215 | + catalogue HeyGen seulement si l'utilisateur veut un compte de plus. | ||