E1 · Engines
5 minRun "download site-movimento and test the engines".
Done when: 4/4 motores OK and saída: 0.
Lesson 6 · 18 min
A long opening film comes from several short clips chained together, each starting on the last real frame of the one before. The risk is a hidden cut in the middle of a clip. The site-movimento skill has gates that catch it. You run them on rehearsal clips, one continuous and one with a cut, without spending credits.
Start the lesson
Goal: chain clips into a continuous film and prove with the site-movimento gates that there is no hidden cut.
Step 4 of the skill (video). Click each stage.
Without a video engine, the skill makes the film in code (steps 1, 2, 5, 6, and 7 are the same). Pilot numbers: 300 Kling 3.0 credits at 720p, 285 frames in the horizontal film and 361 in the vertical one (README, 10/18/2026).
With DRY_RUN=1 each engine only shows what it would do. The test checks all four.
E=${TMPDIR:-/tmp}/ensaio-sites/a6; rm -rf "$E"; mkdir -p "$E"; cd "$E"
git clone -q https://github.com/inematds/site-movimento && git -C site-movimento checkout -q d9a43a4; echo "clone: $?"
bash site-movimento/skill/site-movimento/tests/motores-contrato.sh; echo "saída: $?"clone: 0 OK kling OK agnes OK kie OK custom 4/4 motores OK saída: 0
No service was called: DRY_RUN exits before reading any key. Actually generating uses the account and credits of the engine you choose; the course doesn't do that.
The rehearsal imitates the chaining: clip 2 starts at the zoom where clip 1 ended.
K=~/projetos/formacao-cinema-ia-sites-v72/kit
E=${TMPDIR:-/tmp}/ensaio-sites/a6; SM=$E/site-movimento/skill/site-movimento/scripts
# dois clipes encadeados: o 2º começa exatamente onde o 1º terminou (zoom 1,25)
bash "$K/scripts/clipe.sh" "$K/exemplos/lente.jpg" "$E/c1.mp4" 4 1.0 1.25
bash "$K/scripts/clipe.sh" "$K/exemplos/lente.jpg" "$E/c2.mp4" 4 1.25 1.5
python3 "$SM/cortes.py" "$E/c1.mp4" "$E/c2.mp4" | sed "s#$E/##"; echo "cortes: ${PIPESTATUS[0]}"
rm -rf "$E/site"; cp -r "$K/site" "$E/site"
python3 "$SM/montar.py" "$E/site" "$E/c1.mp4:0:-1" "$E/c2.mp4:1:-1" | head -1 | sed "s#$E/##"
python3 "$SM/continuidade.py" "$E/site/frames" | sed "s#$E/##"; echo "saída: ${PIPESTATUS[0]}"c1.mp4: 4,0 s · 24 fps · 1920×1080 c2.mp4: 4,0 s · 24 fps · 1920×1080 c1.mp4 quadros 97, mediana 1.00, piores [(77, 0.99), (90, 0.99)] ok c2.mp4 quadros 97, mediana 1.00, piores [(1, 0.99), (95, 0.99)] ok cortes: 0 site: 191 quadros, 3.4 MB, cor da emenda #050605 continuidade — site/frames: 48 quadros (a cada 4), mediana 0.983 PASSA — um movimento só saída: 0
4 s + 4 s = 191 frames (frame 0 of clip 2 is dropped so it isn't repeated). montar.py writes to site/frames, the folder the kit page already reads.
A clip that jumps scenes in the middle: the lens in the dark studio and, suddenly, on the light background.
K=~/projetos/formacao-cinema-ia-sites-v72/kit
E=${TMPDIR:-/tmp}/ensaio-sites/a6; SM=$E/site-movimento/skill/site-movimento/scripts
# a clip with a hidden cut in the middle: the lens in the dark studio and, suddenly, on the light background
bash "$K/scripts/clipe.sh" "$K/exemplos/lente.jpg" "$E/a.mp4" 2 1.0 1.1
bash "$K/scripts/clipe.sh" "$K/exemplos/lente-clara.jpg" "$E/b.mp4" 2 1.1 1.2
ffmpeg -v error -y -i "$E/a.mp4" -i "$E/b.mp4" -filter_complex "[0:v][1:v]concat=n=2:v=1[v]" -map "[v]" -c:v libx264 -crf 18 "$E/c3.mp4"
python3 "$SM/cortes.py" "$E/c3.mp4" | sed "s#$E/##"; echo "saída: ${PIPESTATUS[0]}"a.mp4: 2,0 s · 24 fps · 1920×1080 b.mp4: 2,0 s · 24 fps · 1920×1080 c3.mp4 quadros 97, mediana 1.00, piores [(48, 0.46), (72, 0.99)] CORTE no(s) quadro(s) [48] saída: 1
The frame pair at 48 drops to 0.46 against a median of 1.00: a cut. A cut between two dark, similar frames can slip past cortes.py; that's why the skill also runs continuidade.py and you watch the film.
Paid clips go out in order, one at a time. Run it, interrupt it, and run it again: only what's missing comes back.
Simulation in the browser: nothing is generated. The failure is on purpose, to show that only the clip with the cut comes back.
The site-movimento version of lesson 3's gate: it reads Portuguese, English, and Spanish.
K=~/projetos/formacao-cinema-ia-sites-v72/kit
P=${TMPDIR:-/tmp}/ensaio-sites/a6/site-movimento/skill/site-movimento/scripts/portao-texto.js
node "$P" "$K/site/index.html"; echo "saída: $?"
node "$P" "$K/exemplos/texto-ruim-pt.html" 2>&1 | head -5; echo "saída: ${PIPESTATUS[0]}"✓ ~/projetos/formacao-cinema-ia-sites-v72/kit/site/index.html — texto limpo
saída: 0
✗ ~/projetos/formacao-cinema-ia-sites-v72/kit/exemplos/texto-ruim-pt.html — 3 defeito(s)
manda a pessoa rolar
…Aurora 35 Role para baixo e veja a lente de perto.…
narra a câmera
…e de perto. Um plano-sequência A câmera avança pelo vidro até o diafragma…
saída: 1The gate also fails an em dash, AI jargon ("revolucionário"), placeholder text, and the film frame repeated as an image right below it.
The film's gates, without generating anything.
Run "download site-movimento and test the engines".
Done when: 4/4 motores OK and saída: 0.
Run "chain, assemble, and prove continuity". Then serve the ensaio's site/ folder and run verificar.mjs in it.
Done when: PASSA — um movimento só with saída: 0, and the verifier answers quadros: 191 (frames/) and OK: 3 de 3 posições.
Run "a clip with a cut". Then swap lente-clara.jpg for lente2.jpg (another dark photo) and run it again: note the worst pair and say why it passed.
Done when: With the light lens: CORTE no(s) quadro(s) [48] and saída: 1.
Run the copy gate on the page you're building.
Done when: texto limpo and saída: 0.
The explanation appears after you answer.
Question 1 of 4
Where should clip 2 start from?
Question 2 of 4
cortes.py found a cut in the middle of clip 3. What does the skill tell you to do?
Question 3 of 4
Why does continuidade.py compare against the local median instead of a fixed threshold?
Question 4 of 4
What does DRY_RUN=1 guarantee in the engine test?
Choose an option. The explanation appears after your attempt.