mise à jours HEVC pour 1080p
This commit is contained in:
Regular → Executable
+224
-34
@@ -1,52 +1,242 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# EXTRACTION DES MÉTADONNÉES — photos EXIF + traces GPS embarquées GoPro
|
||||
#
|
||||
# Produit deux CSV sans en-tête, consommés par genere_carte.py :
|
||||
# export_photos.csv : fichier, date, lat, lon, alt
|
||||
# export_videos.csv : fichier, date GPS, lat, lon, alt, vitesse
|
||||
#
|
||||
# Usage : ./extraction_infos.sh [dossier_voyage]
|
||||
#
|
||||
# Variables :
|
||||
# VIDEO_GPS=first (défaut) un seul point GPS par rush — RAPIDE.
|
||||
# C'est tout ce que genere_carte.py utilise pour poser
|
||||
# le marqueur : `folium.Marker(location=pts_v[0])`.
|
||||
# VIDEO_GPS=full déroule toute la trace GPS de chaque rush.
|
||||
# Lit l'intégralité de chaque fichier : compter plusieurs
|
||||
# minutes par rush sur un disque externe.
|
||||
# VARIANTE=D|A|B|C force le mode d'extraction vidéo (voir plus bas)
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
shopt -s nocaseglob nullglob
|
||||
|
||||
# --- 1. CONFIGURATION DU RÉPERTOIRE ---
|
||||
# On récupère l'argument 1 s'il existe, sinon on reste sur le répertoire courant
|
||||
BASE_DIR="${1:-.}"
|
||||
|
||||
# Définition des dossiers et fichiers par rapport à la base
|
||||
PHOTOS_DIR="$BASE_DIR/photos"
|
||||
RUSHS_DIR="$BASE_DIR/rushs"
|
||||
CSV_PHOTOS="$BASE_DIR/export_photos.csv"
|
||||
CSV_VIDEOS="$BASE_DIR/export_videos.csv"
|
||||
|
||||
# On active l'insensibilité à la casse pour les jokers (*.mp4, etc.)
|
||||
shopt -s nocaseglob
|
||||
VIDEO_GPS="${VIDEO_GPS:-first}"
|
||||
VARIANTE="${VARIANTE:-}"
|
||||
|
||||
echo "--- DÉBUT DE L'EXTRACTION BRUTE ---"
|
||||
echo "📂 Répertoire racine : $BASE_DIR"
|
||||
if ! command -v exiftool >/dev/null 2>&1; then
|
||||
echo "❌ exiftool introuvable. Installe-le : brew install exiftool"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. RÉINITIALISATION DES FICHIERS CSV (Optionnel mais recommandé)
|
||||
# Si tu veux cumuler sans effacer, commente les deux lignes suivantes :
|
||||
> "$CSV_PHOTOS"
|
||||
> "$CSV_VIDEOS"
|
||||
echo "--- EXTRACTION DES MÉTADONNÉES ---"
|
||||
echo "📂 Répertoire : $BASE_DIR"
|
||||
|
||||
# 3. PHOTOS (Fichiers .jpg, .jpeg)
|
||||
echo "📸 Traitement des photos dans : $PHOTOS_DIR"
|
||||
TMPD="$(mktemp -d "${TMPDIR:-/tmp}/extr.XXXXXX")"
|
||||
ERRLOG="$TMPD/err.log"
|
||||
: > "$ERRLOG"
|
||||
trap 'rm -rf "$TMPD"' EXIT
|
||||
|
||||
nlines() { awk 'END{print NR+0}' "$1" 2>/dev/null || echo 0; }
|
||||
|
||||
# =============================================================================
|
||||
# FORMATS
|
||||
#
|
||||
# `exiftool -p` supprime la LIGNE ENTIÈRE dès qu'UN SEUL tag du format est
|
||||
# absent. Sur un rush GoPro, [Main] porte GPSLatitude/GPSLongitude mais PAS
|
||||
# GPSDateTime, alors que [Doc1] porte tout. Un format à six tags peut donc ne
|
||||
# rien produire sur un fichier parfaitement géolocalisé.
|
||||
#
|
||||
# ⚠️ NE JAMAIS ajouter -f pour « forcer » l'affichage. Vérifié en test :
|
||||
# `-ee -p '$FileName,$SampleRate'` sur un fichier multi-documents donne
|
||||
# 0 ligne sans -f, mais UNE SEULE ligne « fichier,- » AVEC -f — l'option fait
|
||||
# perdre à -ee son itération par document. C'est pire que le mal.
|
||||
#
|
||||
# La parade : trois modes, essayés sur les premiers rushs ; on garde celui
|
||||
# qui produit quelque chose.
|
||||
# A = format complet (date, lat, lon, alt, vitesse)
|
||||
# B = format réduit (date, lat, lon) — tolère l'absence d'alt/vitesse
|
||||
# C = format réduit + -ee3, qui détaille davantage les sous-documents
|
||||
# =============================================================================
|
||||
FMT_PHOTOS='"$FileName",$DateTimeOriginal,$GPSLatitude,$GPSLongitude,$GPSAltitude'
|
||||
FMT_PHOTOS_MIN='"$FileName",$DateTimeOriginal,$GPSLatitude,$GPSLongitude'
|
||||
FMT_V_COMPLET='"$FileName",$GPSDateTime,$GPSLatitude,$GPSLongitude,$GPSAltitude,$GPSSpeed'
|
||||
FMT_V_MINIMAL='"$FileName",$GPSDateTime,$GPSLatitude,$GPSLongitude'
|
||||
|
||||
PARSE_GPS='
|
||||
# Reconstruit des lignes CSV depuis la sortie « Tag : valeur » de exiftool.
|
||||
# Robuste au format long ("GPS Date Time") comme court ("GPSDateTime"), et au
|
||||
# prefixe de groupe [Main] / [Doc1].
|
||||
{
|
||||
ligne = $0
|
||||
sub(/^\[[^]]*\][ \t]*/, "", ligne)
|
||||
if (index(ligne, ":") == 0) next
|
||||
tag = ligne; sub(/[ \t]*:.*/, "", tag); gsub(/[ \t]/, "", tag)
|
||||
val = ligne; sub(/^[^:]*:[ \t]*/, "", val); gsub(/[ \t]+$/, "", val)
|
||||
|
||||
if (tag == "GPSDateTime" && dt == "") dt = val
|
||||
else if (tag == "CreateDate" && cd == "") cd = val
|
||||
else if (tag == "GPSLatitude") lat = val
|
||||
else if (tag == "GPSLongitude") {
|
||||
if (lat != "") {
|
||||
n++
|
||||
if (mode == "full") { plat[n] = lat; plon[n] = val }
|
||||
else if (flat == "") { flat = lat; flon = val }
|
||||
lat = ""
|
||||
}
|
||||
}
|
||||
else if (tag == "GPSAltitude") { if (mode == "full" && n > 0) palt[n] = val }
|
||||
else if (tag == "GPSSpeed") { if (mode == "full" && n > 0) pspd[n] = val }
|
||||
|
||||
if (mode != "full" && flat != "" && dt != "") {
|
||||
print f "," dt "," flat "," flon ",,"; emis = 1; exit
|
||||
}
|
||||
}
|
||||
END {
|
||||
d = (dt != "" ? dt : cd)
|
||||
if (d == "") exit
|
||||
if (mode == "full") { for (i = 1; i <= n; i++) print f "," d "," plat[i] "," plon[i] "," palt[i] "," pspd[i] }
|
||||
else if (!emis && flat != "") print f "," d "," flat "," flon ",,"
|
||||
}
|
||||
'
|
||||
|
||||
# extrait_video <mode> <fichier> -> lignes CSV sur stdout
|
||||
#
|
||||
# Mode D (par defaut) : on demande les tags un par un et on parse la sortie
|
||||
# « Tag : valeur ». C est la seule methode qui atteint reellement [Doc1] :
|
||||
# `-p` ne voit que [Main], lequel porte la position mais PAS GPSDateTime, si
|
||||
# bien qu un format reclamant la date ne produit jamais rien. Verifie sur une
|
||||
# sortie exiftool reelle de GoPro Hero 9 (GX011569.MP4).
|
||||
# En mode rapide, awk sort des qu il a une date et une position : exiftool
|
||||
# recoit SIGPIPE et cesse de lire le fichier.
|
||||
extrait_video() {
|
||||
local m="$1" f="$2" nom
|
||||
nom=$(basename "$f")
|
||||
case "$m" in
|
||||
D) exiftool -ee -n -q -s -api largefilesupport=1 \
|
||||
-GPSDateTime -GPSLatitude -GPSLongitude -GPSAltitude -GPSSpeed -CreateDate \
|
||||
"$f" 2>>"$ERRLOG" \
|
||||
| awk -v f="\"$nom\"" -v mode="$VIDEO_GPS" "$PARSE_GPS" ;;
|
||||
A) exiftool -ee -n -q -api largefilesupport=1 -p "$FMT_V_COMPLET" "$f" 2>>"$ERRLOG" ;;
|
||||
B) exiftool -ee -n -q -api largefilesupport=1 -p "$FMT_V_MINIMAL" "$f" 2>>"$ERRLOG" | awk '{print $0 ",,"}' ;;
|
||||
C) exiftool -ee3 -n -q -api largefilesupport=1 -p "$FMT_V_MINIMAL" "$f" 2>>"$ERRLOG" | awk '{print $0 ",,"}' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- 1. PHOTOS ---------------------------------------------------------------
|
||||
: > "$CSV_PHOTOS"
|
||||
if [ -d "$PHOTOS_DIR" ]; then
|
||||
for f in "$PHOTOS_DIR"/*.jp*g; do
|
||||
[ -e "$f" ] || continue
|
||||
# Utilisation du chemin complet pour exiftool mais basename pour le CSV
|
||||
exiftool -n -p "$(basename "$f")"',$DateTimeOriginal,$GPSLatitude,$GPSLongitude,$GPSAltitude' "$f" >> "$CSV_PHOTOS"
|
||||
done
|
||||
echo "📸 Photos : analyse en cours…"
|
||||
# UN SEUL appel exiftool pour tout le dossier : chaque lancement du binaire
|
||||
# coûte 150-250 ms (Perl). Mesuré sur 150 photos : 29,3 s → 0,97 s.
|
||||
exiftool -n -q -ext jpg -ext jpeg -p "$FMT_PHOTOS" "$PHOTOS_DIR" \
|
||||
2>>"$ERRLOG" > "$TMPD/p1.csv"
|
||||
|
||||
# Seconde passe, format réduit : rattrape les photos écartées par la
|
||||
# première faute d'altitude. Deux appels batch restent sous la seconde.
|
||||
exiftool -n -q -ext jpg -ext jpeg -p "$FMT_PHOTOS_MIN" "$PHOTOS_DIR" \
|
||||
2>>"$ERRLOG" | awk '{print $0 ","}' > "$TMPD/p2.csv"
|
||||
|
||||
# Fusion : tout p1, plus les fichiers de p2 absents de p1.
|
||||
cat "$TMPD/p1.csv" > "$CSV_PHOTOS"
|
||||
awk -F, 'NR==FNR {vu[$1]=1; next} !($1 in vu)' "$TMPD/p1.csv" "$TMPD/p2.csv" >> "$CSV_PHOTOS"
|
||||
|
||||
n1=$(nlines "$TMPD/p1.csv"); nt=$(nlines "$CSV_PHOTOS")
|
||||
printf "📸 Photos : %d ligne(s)" "$nt"
|
||||
[ "$nt" -gt "$n1" ] && printf " (dont %d récupérée(s) sans altitude)" "$((nt - n1))"
|
||||
echo
|
||||
else
|
||||
echo "⚠️ Dossier photos non trouvé, étape ignorée."
|
||||
echo "⚠️ Dossier photos non trouvé, étape ignorée."
|
||||
fi
|
||||
|
||||
# 4. VIDÉOS (Fichiers .mp4, .mov avec flux GoPro)
|
||||
echo "🎬 Traitement des vidéos dans : $RUSHS_DIR"
|
||||
if [ -d "$RUSHS_DIR" ]; then
|
||||
for f in "$RUSHS_DIR"/*.{mp4,mov}; do
|
||||
[ -e "$f" ] || continue
|
||||
# Ta commande exiftool exacte pour le flux GoPro
|
||||
exiftool -ee -n -p "$(basename "$f")"',$GPSDateTime,$GPSLatitude,$GPSLongitude,$GPSAltitude,$GPSSpeed' "$f" >> "$CSV_VIDEOS"
|
||||
done
|
||||
# --- 2. VIDÉOS ---------------------------------------------------------------
|
||||
: > "$CSV_VIDEOS"
|
||||
if [ ! -d "$RUSHS_DIR" ]; then
|
||||
echo "⚠️ Dossier rushs non trouvé, étape ignorée."
|
||||
else
|
||||
echo "⚠️ Dossier rushs non trouvé, étape ignorée."
|
||||
rushs=()
|
||||
for f in "$RUSHS_DIR"/*.mp4 "$RUSHS_DIR"/*.mov; do
|
||||
[ -e "$f" ] || continue
|
||||
# macOS depose un fichier AppleDouble « ._nom » a cote de chaque
|
||||
# fichier sur les disques non-HFS (exFAT). Ce ne sont pas des videos.
|
||||
case "$(basename "$f")" in ._*) continue ;; esac
|
||||
rushs+=("$f")
|
||||
done
|
||||
total="${#rushs[@]}"
|
||||
|
||||
if [ "$total" -eq 0 ]; then
|
||||
echo "⚠️ Aucun rush trouvé dans $RUSHS_DIR"
|
||||
else
|
||||
if [ "$VIDEO_GPS" = "full" ]; then
|
||||
echo "🎬 Vidéos : $total rush(s), trace GPS COMPLÈTE"
|
||||
echo " ⏳ Chaque fichier est lu intégralement."
|
||||
else
|
||||
echo "🎬 Vidéos : $total rush(s), premier point GPS seulement"
|
||||
echo " (VIDEO_GPS=full pour dérouler la trace entière)"
|
||||
fi
|
||||
|
||||
# --- Choix du mode d'extraction ---
|
||||
# Testé sur 3 rushs au maximum : inutile de parcourir 552 fichiers
|
||||
# pour découvrir qu'un mode ne marche pas.
|
||||
if [ -z "$VARIANTE" ]; then
|
||||
echo " 🔎 recherche du mode d'extraction…"
|
||||
for essai in D A B C; do
|
||||
k=0
|
||||
for f in "${rushs[@]}"; do
|
||||
k=$((k + 1)); [ "$k" -gt 3 ] && break
|
||||
if [ -n "$(extrait_video "$essai" "$f" | head -n 1)" ]; then
|
||||
VARIANTE="$essai"; break
|
||||
fi
|
||||
done
|
||||
[ -n "$VARIANTE" ] && break
|
||||
done
|
||||
fi
|
||||
|
||||
case "${VARIANTE:-}" in
|
||||
D) echo " ✔ mode D : lecture directe des tags (atteint [Doc1])" ;;
|
||||
A) echo " ✔ mode A : format complet (date, lat, lon, alt, vitesse)" ;;
|
||||
B) echo " ✔ mode B : format réduit (date, lat, lon)" ;;
|
||||
C) echo " ✔ mode C : format réduit + -ee3 (sous-documents détaillés)" ;;
|
||||
*) echo " ⚠️ Aucun mode ne donne de résultat sur les 3 premiers rushs."
|
||||
echo " Diagnostic : exiftool -ee -G3 -n <un_rush> | grep -i gps"
|
||||
VARIANTE=A ;;
|
||||
esac
|
||||
|
||||
i=0; avec=0; sans=0
|
||||
T0=$(date +%s)
|
||||
for f in "${rushs[@]}"; do
|
||||
i=$((i + 1))
|
||||
printf " [%3d/%3d] %-28s " "$i" "$total" "$(basename "$f")"
|
||||
avant=$(nlines "$CSV_VIDEOS")
|
||||
|
||||
if [ "$VIDEO_GPS" = "full" ]; then
|
||||
extrait_video "$VARIANTE" "$f" >> "$CSV_VIDEOS"
|
||||
else
|
||||
# `head -n 1` ferme le tuyau : exiftool reçoit SIGPIPE et
|
||||
# s'arrête sans lire la fin du fichier.
|
||||
extrait_video "$VARIANTE" "$f" | head -n 1 >> "$CSV_VIDEOS"
|
||||
fi
|
||||
|
||||
ajoutes=$(( $(nlines "$CSV_VIDEOS") - avant ))
|
||||
if [ "$ajoutes" -gt 0 ]; then
|
||||
if [ "$VIDEO_GPS" = "full" ]; then printf "✓ %d points\n" "$ajoutes"; else echo "✓"; fi
|
||||
avec=$((avec + 1))
|
||||
else
|
||||
echo "— pas de GPS"; sans=$((sans + 1))
|
||||
fi
|
||||
done
|
||||
T1=$(date +%s); EL=$((T1 - T0))
|
||||
|
||||
printf "🎬 Vidéos : %d point(s) GPS, %d rush(s) localisé(s)" "$(nlines "$CSV_VIDEOS")" "$avec"
|
||||
[ "$sans" -gt 0 ] && printf ", %d sans GPS" "$sans"
|
||||
printf " — %d min %02d s\n" $((EL / 60)) $((EL % 60))
|
||||
fi
|
||||
fi
|
||||
|
||||
# On désactive l'option nocaseglob
|
||||
shopt -u nocaseglob
|
||||
|
||||
echo "--- TERMINÉ ---"
|
||||
echo "✅ Fichiers générés : $CSV_PHOTOS et $CSV_VIDEOS"
|
||||
echo "✅ Générés : $CSV_PHOTOS et $CSV_VIDEOS"
|
||||
|
||||
Reference in New Issue
Block a user