docs: arbitrage e4 (sans répétition, -r FPS) dans spec et plan

This commit is contained in:
herel committed 2026-08-31 21:14:27 +02:00
1 parent 8c6d053aaf
commit 51cb34a163
2 files changed
+5 -6

No files matched your search

@@ -4,7 +4,7 @@
**Goal :** un script bash `stopmotion.sh` qui génère une vidéo stop motion MP4 (H.264) à partir d'un dossier d'images (jpg/jpeg/png/heic), avec options CLI. **Goal :** un script bash `stopmotion.sh` qui génère une vidéo stop motion MP4 (H.264) à partir d'un dossier d'images (jpg/jpeg/png/heic), avec options CLI.
**Architecture :** un seul fichier `stopmotion.sh` à la racine. Découverte des images via `find` trié → fichier `ffconcat` temporaire (une entrée `duration` par image, dernière image répétée — contournement du concat demuxer qui ignore sinon la dernière frame) → encodage ffmpeg (libx264, yuv420p, +faststart). Le fichier ffconcat est créé dans le dossier du fichier de sortie (les chemins d'images y sont absolus, le concat demuxer résout les chemins relativement à l'emplacement de la liste). **Architecture :** un seul fichier `stopmotion.sh` à la racine. Découverte des images via `find` trié → fichier `ffconcat` temporaire (une entrée `duration` par image) → encodage ffmpeg (libx264, yuv420p, +faststart, `-r FPS` en sortie pour une cadence CFR qui garantit la durée complète de la dernière frame). Le fichier ffconcat est créé dans le dossier du fichier de sortie (les chemins d'images y sont absolus, le concat demuxer résout les chemins relativement à l'emplacement de la liste). (Amendement 2026-08-31, arbitrage utilisateur : la répétition de la dernière image prévue initialement créait une 43e frame dupliquée avec la timebase 1/25 des HEIC — variante e4 validée par framehash.)
**Tech Stack :** bash 3.2 (bash système de macOS — pas de tableaux associatifs, pas de `${var,,}`), ffmpeg 8.x (Homebrew, décode le HEIC), `find` BSD. **Tech Stack :** bash 3.2 (bash système de macOS — pas de tableaux associatifs, pas de `${var,,}`), ffmpeg 8.x (Homebrew, décode le HEIC), `find` BSD.
@@ -16,7 +16,7 @@
- Aucun commentaire dans le code. - Aucun commentaire dans le code.
- Compatible bash 3.2 (macOS) : pas de fonctionnalités bash 4+. - Compatible bash 3.2 (macOS) : pas de fonctionnalités bash 4+.
- Fonctionnement vérifié avec le build ffmpeg 8.1.2 Homebrew (`-pattern_type glob` non supporté → concat demuxer obligatoire). - Fonctionnement vérifié avec le build ffmpeg 8.1.2 Homebrew (`-pattern_type glob` non supporté → concat demuxer obligatoire).
- Encodage : `-c:v libx264 -pix_fmt yuv420p -movflags +faststart`, filtre `scale=S:S:force_original_aspect_ratio=decrease:force_divisible_by=2`. - Encodage : `-c:v libx264 -pix_fmt yuv420p -movflags +faststart`, `-r "$FPS"` en sortie, filtre `scale=S:S:force_original_aspect_ratio=decrease:force_divisible_by=2`.
- Validation stricte avant tout traitement : `-f` et `-s` numériques strictement positifs. - Validation stricte avant tout traitement : `-f` et `-s` numériques strictement positifs.
- Scénario de référence (validation finale) : `./images` (42 HEIC 3056x3056) sans option → 42 frames, 12 fps, 1080x1080, ~3,5 s. - Scénario de référence (validation finale) : `./images` (42 HEIC 3056x3056) sans option → 42 frames, 12 fps, 1080x1080, ~3,5 s.
@@ -210,13 +210,12 @@ DURATION=$(awk -v f="$FPS" 'BEGIN{printf "%.6f", 1/f}')
quote_ffconcat "$img" quote_ffconcat "$img"
printf 'duration %s\n' "$DURATION" printf 'duration %s\n' "$DURATION"
done <<< "$IMAGES" done <<< "$IMAGES"
last=$(printf '%s\n' "$IMAGES" | tail -n 1)
quote_ffconcat "$last"
} > "$LIST_FILE" } > "$LIST_FILE"
if ! ffmpeg -y -hide_banner -loglevel error -stats \ if ! ffmpeg -y -hide_banner -loglevel error -stats \
-f concat -safe 0 -i "$LIST_FILE" \ -f concat -safe 0 -i "$LIST_FILE" \
-vf "scale=${SIZE}:${SIZE}:force_original_aspect_ratio=decrease:force_divisible_by=2" \ -vf "scale=${SIZE}:${SIZE}:force_original_aspect_ratio=decrease:force_divisible_by=2" \
-r "$FPS" \
-c:v libx264 -pix_fmt yuv420p -movflags +faststart \ -c:v libx264 -pix_fmt yuv420p -movflags +faststart \
"$OUT_DIR/$OUT_NAME"; then "$OUT_DIR/$OUT_NAME"; then
rm -f "$OUT_DIR/$OUT_NAME" rm -f "$OUT_DIR/$OUT_NAME"
@@ -35,8 +35,8 @@ Objectif : un script réutilisable pour regénérer la vidéo depuis n'importe q
1. Vérifie la présence de `ffmpeg` dans le PATH ; sinon message clair + exit 1. 1. Vérifie la présence de `ffmpeg` dans le PATH ; sinon message clair + exit 1.
2. Liste les images du dossier d'entrée (extensions `jpg`, `jpeg`, `png`, `heic`, insensible à la casse, premier niveau uniquement), triées par nom (`LC_ALL=C sort`). 2. Liste les images du dossier d'entrée (extensions `jpg`, `jpeg`, `png`, `heic`, insensible à la casse, premier niveau uniquement), triées par nom (`LC_ALL=C sort`).
3. Erreur explicite si le dossier n'existe pas ou ne contient aucune image (exit 1). 3. Erreur explicite si le dossier n'existe pas ou ne contient aucune image (exit 1).
4. Génère un fichier `ffconcat` temporaire (mktemp + trap de nettoyage) : en-tête `ffconcat version 1.0`, puis pour chaque image `file <chemin>` + `duration 1/fps` ; dernière image répétée sans durée (contrainte du concat demuxer qui sinon ignore la dernière frame). Les chemins contenant des apostrophes sont échappés (`'` → `'\''`). 4. Génère un fichier `ffconcat` temporaire (mktemp + trap de nettoyage) : en-tête `ffconcat version 1.0`, puis pour chaque image `file <chemin>` + `duration 1/fps`. Les chemins contenant des apostrophes sont échappés (`'` → `'\''`). (Amendement du 2026-08-31, arbitrage utilisateur : la « dernière image répétée » prévue initialement créait une 43e frame dupliquée sur ce build ffmpeg — timebase 1/25 des HEIC ; la cadence finale est garantie par `-r FPS` en sortie, qui couvre aussi la dernière frame.)
5. Encode avec ffmpeg : `-f concat -safe 0`, `libx264`, `yuv420p`, `-movflags +faststart`, filtre `scale=S:S:force_original_aspect_ratio=decrease:force_divisible_by=2`. 5. Encode avec ffmpeg : `-f concat -safe 0`, `libx264`, `yuv420p`, `-movflags +faststart`, `-r FPS`, filtre `scale=S:S:force_original_aspect_ratio=decrease:force_divisible_by=2`.
6. Affiche un récapitulatif : nombre d'images, fps, durée de la vidéo, taille du fichier de sortie. 6. Affiche un récapitulatif : nombre d'images, fps, durée de la vidéo, taille du fichier de sortie.
## Gestion des erreurs ## Gestion des erreurs