From 3332ec77504cb01a58f13f23c8d64d580a7893e7 Mon Sep 17 00:00:00 2001 From: Korrigan Date: Mon, 19 Jan 2026 09:11:57 +0000 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9verser=20les=20fichiers=20vers=20?= =?UTF-8?q?"Scripts"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Scripts/compress.sh | 63 +++++++++ Scripts/extraction_infos.sh | 52 +++++++ Scripts/genere_carte.py | 238 ++++++++++++++++++++++++++++++++ Scripts/genere_index_general.py | 48 +++++++ 4 files changed, 401 insertions(+) create mode 100644 Scripts/compress.sh create mode 100644 Scripts/extraction_infos.sh create mode 100644 Scripts/genere_carte.py create mode 100644 Scripts/genere_index_general.py diff --git a/Scripts/compress.sh b/Scripts/compress.sh new file mode 100644 index 0000000..1844755 --- /dev/null +++ b/Scripts/compress.sh @@ -0,0 +1,63 @@ +#!/bin/bash +shopt -s nocaseglob + +# --- 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 par rapport à la base +SOURCE_DIR="$BASE_DIR/rushs" +DEST_DIR="$BASE_DIR/videos" + +# Vérification que le dossier source existe +if [ ! -d "$SOURCE_DIR" ]; then + echo "❌ Erreur : Le dossier source $SOURCE_DIR n'existe pas." + exit 1 +fi + +# Création du dossier de destination s'il n'existe pas +mkdir -p "$DEST_DIR" + +echo "--- DÉBUT DE LA COMPRESSION ---" +echo "📂 Répertoire racine : $BASE_DIR" +echo "📥 Source : $SOURCE_DIR" +echo "📤 Destination : $DEST_DIR" + +# 2. BOUCLE DE COMPRESSION +for f in "$SOURCE_DIR"/*.{mp4,mov}; do + # Vérification si des fichiers existent + [ -e "$f" ] || continue + + # Récupération du nom de fichier sans le chemin + filename=$(basename "$f") + + echo "🎬 Compression de : $filename ..." + + # Compression avec accélération matérielle Mac (videotoolbox) + ffmpeg -i "$f" \ + -tag:v hvc1 \ + -c:v hevc_videotoolbox \ + -b:v 10000k \ + -g 60 \ + -bf 2 \ + -profile:v main \ + -pix_fmt yuv420p \ + -r 60 \ + -c:a aac \ + -b:a 128k \ + -movflags +faststart \ + "$DEST_DIR/$filename" \ + -y -loglevel error + + # Comparaison de taille + if [ -f "$DEST_DIR/$filename" ]; then + orig_size=$(du -h "$f" | cut -f1) + new_size=$(du -h "$DEST_DIR/$filename" | cut -f1) + echo "✅ Terminé : $filename ($orig_size -> $new_size)" + else + echo "⚠️ Erreur lors de la compression de $filename" + fi +done + +shopt -u nocaseglob +echo "--- OPÉRATION TERMINÉE ---" diff --git a/Scripts/extraction_infos.sh b/Scripts/extraction_infos.sh new file mode 100644 index 0000000..481de88 --- /dev/null +++ b/Scripts/extraction_infos.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# --- 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 + +echo "--- DÉBUT DE L'EXTRACTION BRUTE ---" +echo "📂 Répertoire racine : $BASE_DIR" + +# 2. RÉINITIALISATION DES FICHIERS CSV (Optionnel mais recommandé) +# Si tu veux cumuler sans effacer, commente les deux lignes suivantes : +> "$CSV_PHOTOS" +> "$CSV_VIDEOS" + +# 3. PHOTOS (Fichiers .jpg, .jpeg) +echo "📸 Traitement des photos dans : $PHOTOS_DIR" +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 +else + 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 +else + echo "⚠️ Dossier rushs non trouvé, étape ignorée." +fi + +# On désactive l'option nocaseglob +shopt -u nocaseglob + +echo "--- TERMINÉ ---" +echo "✅ Fichiers générés : $CSV_PHOTOS et $CSV_VIDEOS" diff --git a/Scripts/genere_carte.py b/Scripts/genere_carte.py new file mode 100644 index 0000000..8c1993f --- /dev/null +++ b/Scripts/genere_carte.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +import pandas as pd +import folium +from folium import plugins +from folium.plugins import MarkerCluster +import os +import gpxpy +import re +import sys +import markdown2 +import numpy as np + +# --- 1. CONFIGURATION --- +base_dir = sys.argv[1] if len(sys.argv) > 1 else "./" +base_dir = os.path.abspath(base_dir) + +routes_dir = os.path.join(base_dir, "routes") +html_output_dir = os.path.join(base_dir, "html") +csv_videos = os.path.join(base_dir, "export_videos.csv") +csv_photos = os.path.join(base_dir, "export_photos.csv") +file_notes = os.path.join(base_dir, "voyage.md") + +if not os.path.exists(html_output_dir): + os.makedirs(html_output_dir) + +COULEURS_JOURS = ['#FF5733', '#2ECC71', '#3498DB', '#9B59B6', '#F1C40F', '#E67E22', '#1ABC9C', '#34495E'] +noms_colonnes = ['Fichier', 'Date_Heure', 'Latitude', 'Longitude', 'Altitude', 'Vitesse'] + +# --- FONCTIONS UTILES --- +def nettoyer_et_trier(df): + if df is None or df.empty: + return pd.DataFrame(columns=noms_colonnes + ['DT', 'Jour']) + df = df[(df['Latitude'].notnull()) & (df['Longitude'].notnull())] + df = df[(df['Latitude'] != 0) & (df['Longitude'] != 0)] + df['DT'] = pd.to_datetime(df['Date_Heure'].str.split('.').str[0], format='%Y:%m:%d %H:%M:%S', errors='coerce') + df = df.dropna(subset=['DT']) + df['Jour'] = df['DT'].dt.date + return df.sort_values(by='DT') + +def create_pin(color, icon_name): + html_code = f"""
+
+ +
+
+
""" + return folium.DivIcon(html=html_code, icon_anchor=(12, 40)) + +def inject_common_assets(m): + custom_js = """ + + """ + custom_css = """ + + """ + m.get_root().header.add_child(folium.Element(custom_css + custom_js)) + +def get_journal_ui(): + journal_html = "" + if os.path.exists(file_notes): + with open(file_notes, 'r', encoding='utf-8') as f: + journal_html = markdown2.markdown(f.read()) + return f""" +
{journal_html}
+
+ +
+ """ + +# --- 2. CHARGEMENT --- +try: + df_v = pd.read_csv(csv_videos, names=noms_colonnes, header=None) if os.path.exists(csv_videos) else pd.DataFrame() + df_p = pd.read_csv(csv_photos, names=noms_colonnes, header=None) if os.path.exists(csv_photos) else pd.DataFrame() + df_v, df_p = nettoyer_et_trier(df_v), nettoyer_et_trier(df_p) + + jours_gpx = set() + if os.path.exists(routes_dir): + for f in os.listdir(routes_dir): + match = re.search(r'(\d{4}-\d{2}-\d{2})', f) + if match: jours_gpx.add(pd.to_datetime(match.group(1)).date()) + tous_les_jours = sorted(list(set(df_v['Jour'].unique()) | set(df_p['Jour'].unique()) | jours_gpx)) +except Exception as e: + print(f"❌ Erreur : {e}"); sys.exit() + +# --- 3. GÉNÉRATION --- +m_global = folium.Map(tiles=None) +folium.TileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr='Esri', name='Satellite').add_to(m_global) +folium.TileLayer('OpenStreetMap', name='Plan').add_to(m_global) +inject_common_assets(m_global) + +m_mini = folium.Map(tiles='OpenStreetMap', zoom_control=False, control_scale=False, attribution_control=False) + +all_global_coords, sidebar_links_html = [], "" + +for i, jour in enumerate(tous_les_jours): + day_str = jour.strftime('%Y-%m-%d') + file_name = f'carte_{day_str}.html' + color_day = COULEURS_JOURS[i % len(COULEURS_JOURS)] + sidebar_links_html += f'{day_str}' + + m_day = folium.Map(tiles=None) + folium.TileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attr='Esri', name='Satellite').add_to(m_day) + folium.TileLayer('OpenStreetMap', name='Plan').add_to(m_day) + inject_common_assets(m_day) + + marker_cluster = MarkerCluster(name="Médias").add_to(m_day) + day_coords = [] + current_gpx_filename = None + + if os.path.exists(routes_dir): + for gpx_file in os.listdir(routes_dir): + if gpx_file.startswith(day_str) and gpx_file.endswith('.gpx'): + current_gpx_filename = gpx_file + try: + with open(os.path.join(routes_dir, gpx_file), 'r') as f: + gpx = gpxpy.parse(f) + for track in gpx.tracks: + pts = [] + for segment in track.segments: + # OPTIMISATION : On ne garde qu'un point sur 5 +# pts.extend([[p.latitude, p.longitude] for i, p in enumerate(segment.points) if i % 5 == 0]) + pts.extend([[p.latitude, p.longitude] for p in segment.points]) + if pts: + day_coords.extend(pts); all_global_coords.extend(pts) + popup_html = f"
📅 {day_str}

Détails
" + folium.PolyLine(pts, color=color_day, weight=7, opacity=0.8, popup=folium.Popup(popup_html, max_width=200)).add_to(m_global) + folium.PolyLine(pts, color=color_day, weight=3, opacity=0.8).add_to(m_mini) + folium.PolyLine(pts, color="#FF0000", weight=4, opacity=0.8).add_to(m_day) + except: pass + + m_day.get_root().html.add_child(folium.Element('
🏠 Accueil
' + get_journal_ui())) + + if current_gpx_filename: + m_day.get_root().html.add_child(folium.Element(f'
📥 GPX
')) + + # REGROUPEMENT PHOTOS + day_p = df_p[df_p['Jour'] == jour].copy() + if not day_p.empty: + tol = 0.0002 + while not day_p.empty: + ref = day_p.iloc[0] + masque = (np.abs(day_p['Latitude'] - ref['Latitude']) < tol) & (np.abs(day_p['Longitude'] - ref['Longitude']) < tol) + groupe = day_p[masque] + fichiers = groupe['Fichier'].tolist() + nb = len(fichiers) + +# slides = "".join([f'
{p.strip()}
' for idx, p in enumerate(fichiers)]) +# OPTIMISATION : Ajout de loading="lazy" dans la balise img + slides = "".join([f'
{p.strip()}
' for idx, p in enumerate(fichiers)]) + btns = f'
1/{nb}
' if nb > 1 else "" + folium.Marker(location=[ref['Latitude'], ref['Longitude']], popup=folium.Popup(f'
{slides}
{btns}
', max_width="100%"), icon=create_pin("#FF3B30", "camera" if nb==1 else "images")).add_to(marker_cluster) + day_p = day_p[~masque] + + # Vidéos + day_v = df_v[df_v['Jour'] == jour] + for v_name, group in day_v.groupby('Fichier'): + pts_v = group[['Latitude', 'Longitude']].values.tolist() +# v_pop = f'
{v_name.strip()}
' + v_pop = f'
{v_name.strip()}
' + folium.Marker(location=pts_v[0], popup=folium.Popup(v_pop, max_width="100%"), icon=create_pin("#007AFF", "play")).add_to(marker_cluster) + + if day_coords: m_day.fit_bounds(day_coords, padding=(50, 50)) + folium.LayerControl(position='topright').add_to(m_day) + plugins.Fullscreen(position='topright').add_to(m_day) + m_day.save(os.path.join(html_output_dir, file_name)) + +# --- FINALISATION --- +if all_global_coords: + m_global.fit_bounds(all_global_coords, padding=(50, 50)) + m_mini.fit_bounds(all_global_coords, padding=(20, 20)) + +sidebar_html = f'
⬅ Retour
PAR JOURS
{sidebar_links_html}
' +m_global.get_root().html.add_child(folium.Element(sidebar_html + get_journal_ui())) +folium.LayerControl().add_to(m_global) + +m_global.save(os.path.join(base_dir, "index.html")) +m_mini.save(os.path.join(base_dir, "mini.html")) diff --git a/Scripts/genere_index_general.py b/Scripts/genere_index_general.py new file mode 100644 index 0000000..246a618 --- /dev/null +++ b/Scripts/genere_index_general.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import os + +EXCLURE = ['html', 'Scripts', '__pycache__', '.git'] +OUTPUT_FILE = "index.html" + +def generer_index_general(): + road_trips = [] + for d in sorted(os.listdir('.')): + if os.path.isdir(d) and d not in EXCLURE: + # On cherche mini.html en priorité + if os.path.exists(os.path.join(d, 'mini.html')): + road_trips.append(d) + + html_content = f""" + + + + + Nos Road-Trips + + + +

🌍 Nos Road-Trips

+
+ """ + for trip in road_trips: + html_content += f""" +
+ +
+

{trip.replace('-', ' ')}

+ Voir le voyage +
+
+ """ + html_content += "
" + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: f.write(html_content) + +if __name__ == "__main__": + generer_index_general()