Refonte : templates, balades, photos web, aperçus, carte globale

- Mise en page sortie du Python : Scripts/templates/ (Jinja2 + CSS/JS
  communs copiés dans assets/ de chaque voyage)
- Voyages rangés dans un sous-dossier (Balades/…) : rubriques sur
  l'accueil, 2 plus récents par rubrique, pages voyages.html et
  balades.html
- prepare_web.py : photos allégées (photos_web/) et images d'aperçu
  des vidéos (apercus/)
- Voyage d'une seule journée : la page du voyage est la carte du jour
- Carte globale de l'accueil (OpenStreetMap) : voyages du site + cartes
  Google My Maps récupérées (mymaps.txt, _mymaps/), moto par voyage
  (moto.txt)
- KML par voyage pour My Maps
- compress.sh : réencode une sortie illisible (encodage interrompu) ;
  repli automatique sur l'autre qualité dans la page
- Corrections : CSV vide (voyage sans photo), racine de genere_site.sh,
  fichiers ._ de macOS
- README réécrit, commentaires allégés

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
This commit is contained in:
FabriceandClaude Opus 5.5 committed 2026-10-09 11:08:01 +00:00
1 parent 5a04f159ca
commit 97b936fd3e
17 files changed
+2042 -829

No files matched your search

+59 -97
View File
@@ -1,51 +1,37 @@
#!/bin/bash
# =============================================================================
# COMPRESSION ROAD-TRIP — STRATÉGIE HYBRIDE + ENCODAGE PARALLÈLE
# Source : GoPro Hero 9, rushs en HEVC 1080p
# COMPRESSION DES RUSHS POUR LE WEB
#
# Sorties (noms de fichiers INCHANGÉS par rapport à l'ancien script) :
# <base>.mp4 1080p natif, HEVC/H.265, tag hvc1
# → gros fichier, là où les 40 % d'économie comptent
# <base>_mobile.mp4 720p, H.264, tag avc1, profil High
# → fichier chargé par défaut, se lit sur TOUS
# les navigateurs sans exception
# Pour chaque rush de <voyage>/rushs, deux fichiers dans <voyage>/videos :
# <base>.mp4 1080p HEVC (tag hvc1) — qualité, ~40 % plus léger que H.264
# <base>_mobile.mp4 720p H.264 (tag avc1) — chargé par défaut, lisible partout
#
# PARALLÉLISME
# Deux niveaux, tous deux actifs par défaut :
# 1. Les deux paliers d'un même rush sont indépendants → lancés ensemble.
# 2. JOBS rushs traités simultanément (pool géré par xargs -P).
# Le nombre de threads par encodeur est borné pour ne pas surcharger :
# x265 sature vite (scaling sublinéaire au-delà de ~6-8 threads), donc
# plusieurs encodages courts valent mieux qu'un seul très parallélisé.
# Un rush dont les deux sorties existent et sont lisibles est sauté
# (FORCE=1 pour refaire).
#
# Variables d'environnement :
# JOBS=N nombre de rushs en parallèle
# (défaut : cœurs performance / 4, minimum 1)
# THREADS=N threads par encodeur (défaut : cœurs / JOBS)
# ENCODER=cpu (défaut) libx265 + libx264 — meilleure qualité/octet
# ENCODER=vt VideoToolbox — ~10x plus rapide sur Apple Silicon
# (⚠️ le parallélisme n'y sert à rien : le bloc média
# de la puce est une ressource fixe → JOBS=1 forcé)
# PRESET=medium preset CPU : ultrafast…placebo
# CRF_HEVC=26 qualité du 1080p HEVC (échelle x265)
# CRF_H264=25 qualité du 720p H.264 (échelle x264 — PAS la même !)
# Variables :
# JOBS=N rushs encodés en parallèle (défaut : cœurs performance / 4)
# THREADS=N threads par encodeur (défaut : cœurs / JOBS)
# ENCODER=cpu libx265 + libx264, meilleure qualité par octet (défaut)
# ENCODER=vt VideoToolbox, ~10× plus rapide sur Apple Silicon, un peu
# moins bon à taille égale ; JOBS=1 (un seul bloc matériel)
# PRESET=medium preset x264/x265
# CRF_HEVC=26 qualité du 1080p (échelle x265)
# CRF_H264=25 qualité du 720p (échelle x264, PAS comparable à x265)
# VTQ_HD=60 VTQ_SD=58 qualité VideoToolbox (1-100, plus haut = meilleur)
# FPS_MAX=60 plafonne le framerate (un rush 30p reste en 30p,
# un rush 120p/240p est ramené à 60)
# FORCE=1 réencode même si le fichier de sortie existe
# FPS_MAX=60 plafond de fréquence d'images (un rush 120p est ramené à 60)
# FORCE=1 réencode même si les sorties existent
#
# Exemples :
# ./compress.sh mon-voyage
# JOBS=4 ./compress.sh mon-voyage
# JOBS=1 ./compress.sh mon-voyage # séquentiel + barre de progression
# ENCODER=vt ./compress.sh mon-voyage
# Exemples (depuis le dossier du voyage) :
# ../../RoadTripsGenerator/Scripts/compress.sh
# JOBS=1 ../../RoadTripsGenerator/Scripts/compress.sh # avec barre de progression
# ENCODER=vt ../../RoadTripsGenerator/Scripts/compress.sh
# =============================================================================
set -uo pipefail
shopt -s nocaseglob nullglob
# --- 0. MODE INTERNE ---------------------------------------------------------
# Quand le script est rappelé par xargs pour traiter UN rush, il arrive ici.
# --- Mode interne : rappelé par xargs pour traiter UN rush ---------------------
if [ "${1:-}" = "--encode-one" ]; then
INTERNAL=1
BASE_DIR="${RT_BASE_DIR:-.}"
@@ -58,7 +44,7 @@ fi
SOURCE_DIR="$BASE_DIR/rushs"
DEST_DIR="$BASE_DIR/videos"
# --- 1. CONFIGURATION --------------------------------------------------------
# --- Configuration -----------------------------------------------------------
ENCODER="${ENCODER:-cpu}"
PRESET="${PRESET:-medium}"
CRF_HEVC="${CRF_HEVC:-26}"
@@ -68,9 +54,8 @@ VTQ_SD="${VTQ_SD:-58}"
FPS_MAX="${FPS_MAX:-60}"
FORCE="${FORCE:-0}"
# Détection des cœurs. Sur Apple Silicon on ne compte que les cœurs
# PERFORMANCE : les cœurs d'efficience sont si lents qu'ils deviennent des
# traînards dans un pool x265 et pénalisent l'encodage entier.
# Sur Apple Silicon on ne compte que les cœurs performance : les cœurs
# d'efficience ralentiraient tout le pool de threads x265.
detect_cores() {
local c=""
c=$(sysctl -n hw.perflevel0.logicalcpu 2>/dev/null); [ -n "$c" ] && { echo "$c"; return; }
@@ -80,8 +65,6 @@ detect_cores() {
}
CORES="${CORES:-$(detect_cores)}"
# En VideoToolbox, le parallélisme ne sert à rien (un seul moteur d'encodage
# matériel sur les puces M de base) — sauf si l'utilisateur force JOBS.
JOBS_EXPLICIT=0
[ -n "${JOBS+x}" ] && JOBS_EXPLICIT=1
if [ "$ENCODER" = "vt" ] && [ "$JOBS_EXPLICIT" = "0" ]; then
@@ -89,64 +72,49 @@ if [ "$ENCODER" = "vt" ] && [ "$JOBS_EXPLICIT" = "0" ]; then
else
JOBS="${JOBS:-$(( CORES / 4 ))}"
fi
[ "$JOBS" -lt 1 ] 2>/dev/null && JOBS=1
[ -z "$JOBS" ] && JOBS=1
[ "$JOBS" -lt 1 ] 2>/dev/null && JOBS=1
THREADS="${THREADS:-$(( CORES / JOBS ))}"
[ "$THREADS" -lt 2 ] 2>/dev/null && THREADS=2
# Le palier 720p H.264 est 3 à 5× plus rapide que le 1080p HEVC : on lui donne
# moitié moins de threads. Il finit largement avant, et la fenêtre pendant
# laquelle les deux paliers tournent ensemble ne surcharge pas la machine.
# Le 720p H.264 est 3 à 5× plus rapide que le 1080p HEVC : moitié moins de threads.
THREADS_SD=$(( THREADS / 2 ))
[ "$THREADS_SD" -lt 2 ] && THREADS_SD=2
# --- 2. FONCTION D'ENCODAGE --------------------------------------------------
# --- Encodage d'un palier ----------------------------------------------------
# encode <entrée> <sortie> <filtre_vf|none> <hevc|h264> <crf> <vtq> <maxrate> <bufsize> <audio>
encode() {
local in="$1" out="$2" vf="$3" codec="$4" crf="$5" vtq="$6" maxrate="$7" bufsize="$8" abr="$9"
local -a vopts vf_args statopt
if [ "$vf" = "none" ]; then vf_args=(); else vf_args=(-vf "$vf"); fi
# Barre de progression uniquement en séquentiel : à plusieurs jobs, les
# sorties de progression s'entremêlent et deviennent illisibles.
# Progression seulement en séquentiel : à plusieurs jobs, les lignes s'entremêlent.
if [ "$JOBS" = "1" ]; then statopt=(-stats); else statopt=(); fi
# ⚠️ Les tableaux VIDES sont développés avec l'idiome ${a[@]+"${a[@]}"}.
# Le /bin/bash d'Apple est en 3.2, où `"${vide[@]}"` sous `set -u` provoque
# une erreur "unbound variable" (corrigé seulement en bash 4.4).
if [ "$codec" = "hevc" ]; then
if [ "$ENCODER" = "vt" ]; then
vopts=(-c:v hevc_videotoolbox -profile:v main -q:v "$vtq" -allow_sw 1 -realtime 0 -tag:v hvc1)
else
vopts=(
-c:v libx265 -preset "$PRESET" -profile:v main -crf "$crf"
# keyint=120 -> 1 image-clé toutes les 2 s à 60 fps.
# scenecut laissé ACTIF (défaut) : un GOP strictement fixe n'est
# utile qu'en streaming segmenté, pas pour du MP4 progressif.
# pools=N borne le pool WPP pour cohabiter avec les autres jobs.
-x265-params "keyint=120:min-keyint=24:aq-mode=3:pools=$THREADS"
-maxrate "$maxrate" -bufsize "$bufsize"
# -tag:v hvc1 est INDISPENSABLE : sans lui ffmpeg écrit 'hev1',
# que Safari et QuickTime refusent de lire.
-tag:v hvc1
)
# keyint=120 : une image-clé toutes les 2 s à 60 fps.
# pools=N borne le pool de threads x265 pour cohabiter avec les autres jobs.
# -tag:v hvc1 : sans lui ffmpeg écrit « hev1 », que Safari refuse.
vopts=(-c:v libx265 -preset "$PRESET" -profile:v main -crf "$crf"
-x265-params "keyint=120:min-keyint=24:aq-mode=3:pools=$THREADS"
-maxrate "$maxrate" -bufsize "$bufsize" -tag:v hvc1)
fi
else
if [ "$ENCODER" = "vt" ]; then
vopts=(-c:v h264_videotoolbox -profile:v high -q:v "$vtq" -allow_sw 1 -realtime 0)
else
vopts=(
-c:v libx264 -preset "$PRESET" -profile:v high -level 4.0 -crf "$crf"
-g 120 -keyint_min 24
-threads "$THREADS_SD"
-maxrate "$maxrate" -bufsize "$bufsize"
)
vopts=(-c:v libx264 -preset "$PRESET" -profile:v high -level 4.0 -crf "$crf"
-g 120 -keyint_min 24 -threads "$THREADS_SD"
-maxrate "$maxrate" -bufsize "$bufsize")
fi
fi
# ${a[@]+"${a[@]}"} : un tableau vide sous `set -u` fait planter le bash 3.2
# livré avec macOS.
ffmpeg -nostdin -hide_banner -loglevel error ${statopt[@]+"${statopt[@]}"} \
-i "$in" \
-map 0:v:0 -map 0:a:0\? \
@@ -163,7 +131,14 @@ encode() {
fsize() { stat -f%z "$1" 2>/dev/null || stat -c%s "$1"; }
# --- 3. TRAITEMENT D'UN RUSH -------------------------------------------------
# Une sortie existante n'est gardée que si ffprobe sait la lire : un encodage
# interrompu laisse un gros fichier sans index (« moov atom not found »).
# Lecture rapide, l'index est en tête de fichier grâce à +faststart.
lisible() {
[ -s "$1" ] && ffprobe -v error -show_entries format=duration -of csv=p=0 "$1" >/dev/null 2>&1
}
# --- Traitement d'un rush : les deux paliers en parallèle --------------------
process_one() {
local f="$1"
local filename base desktop_out mobile_out src_h vf_hd
@@ -172,23 +147,18 @@ process_one() {
desktop_out="$DEST_DIR/${base}.mp4"
mobile_out="$DEST_DIR/${base}_mobile.mp4"
if [ "$FORCE" != "1" ] && [ -s "$desktop_out" ] && [ -s "$mobile_out" ]; then
if [ "$FORCE" != "1" ] && lisible "$desktop_out" && lisible "$mobile_out"; then
echo "⏭️ $filename — déjà encodé (FORCE=1 pour refaire)"
return 0
fi
src_h=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$f" 2>/dev/null | head -1)
src_h=${src_h:-1080}
# Rushs déjà en 1080p : pas de rééchelonnage, on garde le pixel natif.
# Si un rush est plus grand (2.7K/4K), on redescend proprement à 1080p.
# 1080p natif conservé tel quel ; une source 2.7K/4K est ramenée à 1080p.
if [ "$src_h" -gt 1080 ]; then vf_hd="scale=-2:1080:flags=lanczos"; else vf_hd="none"; fi
echo "🎬 $filename (${src_h}p) — encodage des 2 paliers en parallèle…"
# Les deux paliers sont indépendants : même source, sorties distinctes.
# On les lance ensemble — le 720p H.264, bien plus léger, se glisse dans
# les trous laissés par le 1080p HEVC.
local pid_hd pid_sd rc_hd rc_sd
encode "$f" "$desktop_out" "$vf_hd" hevc "$CRF_HEVC" "$VTQ_HD" 8M 16M 96k &
pid_hd=$!
@@ -210,7 +180,6 @@ process_one() {
fi
}
# Point de sortie du mode interne (un seul rush, appelé par xargs)
if [ "$INTERNAL" = "1" ]; then
process_one "$TARGET"
exit $?
@@ -225,9 +194,8 @@ if [ ! -d "$SOURCE_DIR" ]; then
fi
mkdir -p "$DEST_DIR"
# Vérification des encodeurs disponibles.
# (on stocke la liste : un `| grep -q` déclencherait un SIGPIPE que pipefail
# transformerait en faux négatif)
# Liste stockée plutôt que `ffmpeg … | grep -q` : le SIGPIPE de grep -q,
# combiné à pipefail, donnerait un faux « encodeur absent ».
ENCODERS_LIST="$(ffmpeg -hide_banner -encoders 2>/dev/null)"
has_enc() { printf '%s' "$ENCODERS_LIST" | grep -q "$1"; }
@@ -239,10 +207,12 @@ else
has_enc "libx264" || { echo "❌ libx264 absent de ton ffmpeg (brew install ffmpeg)."; exit 1; }
fi
# Liste des rushs
files=()
for f in "$SOURCE_DIR"/*.mp4 "$SOURCE_DIR"/*.mov; do
[ -e "$f" ] && files+=("$f")
[ -e "$f" ] || continue
# « ._nom » : métadonnées que macOS dépose sur les disques non-HFS (exFAT).
case "$(basename "$f")" in ._*) continue ;; esac
files+=("$f")
done
echo "--- COMPRESSION ROAD-TRIP (HYBRIDE HEVC / H.264) ---"
@@ -250,10 +220,6 @@ echo "📂 Source : $SOURCE_DIR"
echo "📤 Destination : $DEST_DIR"
if [ "$ENCODER" = "vt" ]; then
echo "⚙️ Encodeur : VideoToolbox (q=$VTQ_HD / $VTQ_SD)"
[ "$JOBS" = "1" ] && [ "$JOBS_EXPLICIT" = "0" ] && \
echo " ℹ️ JOBS=1 : le bloc média de la puce est une ressource fixe,"
[ "$JOBS" = "1" ] && [ "$JOBS_EXPLICIT" = "0" ] && \
echo " paralléliser ne ferait que mettre les jobs en file d'attente."
else
echo "⚙️ Encodeur : CPU preset=$PRESET (HEVC CRF $CRF_HEVC / H.264 CRF $CRF_H264)"
fi
@@ -272,14 +238,13 @@ RT_STATS="$(mktemp "${TMPDIR:-/tmp}/rtstats.XXXXXX")"
trap 'rm -f "$RT_STATS"' EXIT
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
export RT_BASE_DIR="$BASE_DIR" RT_STATS
export ENCODER PRESET CRF_HEVC CRF_H264 VTQ_HD VTQ_SD FPS_MAX FORCE JOBS THREADS THREADS_SD CORES
T0=$(date +%s)
# xargs -P gère le pool de jobs : portable (y compris le bash 3.2 d'Apple,
# qui ne connaît pas `wait -n`) et il relance un job dès qu'un autre se termine.
# xargs -P gère le pool de jobs : portable, y compris avec le bash 3.2 de macOS
# qui ne connaît pas `wait -n`.
printf '%s\0' "${files[@]}" | xargs -0 -P "$JOBS" -n1 "$SCRIPT_PATH" --encode-one
rc=$?
@@ -305,8 +270,5 @@ echo
echo "🔍 Contrôle des tags (attendu : hvc1 pour les 1080p, avc1 pour les _mobile) :"
echo " ffprobe -v error -select_streams v:0 -show_entries stream=codec_tag_string \\"
echo " -of csv=p=0 $DEST_DIR/*.mp4"
echo
echo "💡 Pour trouver ton JOBS optimal, chronomètre 3-4 rushs à JOBS=1, 2 puis 4 :"
echo " le gain plafonne vite, et au-delà le cache thrashe."
exit $rc