Téléverser les fichiers vers "Scripts"
This commit is contained in:
63
Scripts/compress.sh
Normal file
63
Scripts/compress.sh
Normal file
@@ -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 ---"
|
||||
52
Scripts/extraction_infos.sh
Normal file
52
Scripts/extraction_infos.sh
Normal file
@@ -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"
|
||||
238
Scripts/genere_carte.py
Normal file
238
Scripts/genere_carte.py
Normal file
@@ -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"""<div style="position: relative; height: 40px; width: 24px;">
|
||||
<div style="background-color: {color}; width: 24px; height: 24px; border-radius: 50%; border: 2px solid white;
|
||||
display: flex; align-items: center; justify-content: center; color: white; font-size: 12px;
|
||||
position: absolute; top: 0; z-index: 2; box-shadow: 0 2px 4px rgba(0,0,0,0.4);">
|
||||
<i class="fa fa-{icon_name}"></i>
|
||||
</div>
|
||||
<div style="width: 3px; height: 18px; background-color: {color}; position: absolute; top: 22px; left: 10.5px; z-index: 1;"></div>
|
||||
</div>"""
|
||||
return folium.DivIcon(html=html_code, icon_anchor=(12, 40))
|
||||
|
||||
def inject_common_assets(m):
|
||||
custom_js = """
|
||||
<script>
|
||||
document.addEventListener('click', function (e) {
|
||||
setTimeout(function() {
|
||||
var videos = document.querySelectorAll('video');
|
||||
videos.forEach(function(video) {
|
||||
if (video.hasAttribute('autoplay')) {
|
||||
video.play().catch(function(error) { console.log("Autoplay bloqué"); });
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
}, true);
|
||||
|
||||
function toggleJournal(open) {
|
||||
var panel = document.getElementById('journal-panel');
|
||||
if (open) {
|
||||
panel.classList.add('open');
|
||||
localStorage.setItem('journalOpen', 'true');
|
||||
} else {
|
||||
panel.classList.remove('open');
|
||||
localStorage.setItem('journalOpen', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
// Persistance du journal sur toutes les pages
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
if (localStorage.getItem('journalOpen') === 'true') {
|
||||
var panel = document.getElementById('journal-panel');
|
||||
if (panel) panel.classList.add('open');
|
||||
}
|
||||
});
|
||||
|
||||
function moveSlide(btn, step) {
|
||||
var container = btn.parentElement.querySelector('.slides');
|
||||
var slides = container.querySelectorAll('.slide');
|
||||
var activeIndex = Array.from(slides).findIndex(s => s.style.display !== 'none');
|
||||
if (activeIndex === -1) activeIndex = 0;
|
||||
slides[activeIndex].style.display = 'none';
|
||||
var nextIndex = (activeIndex + step + slides.length) % slides.length;
|
||||
slides[nextIndex].style.display = 'block';
|
||||
btn.parentElement.querySelector('.slide-counter').innerText = (nextIndex + 1) + '/' + slides.length;
|
||||
}
|
||||
</script>
|
||||
"""
|
||||
custom_css = """
|
||||
<style>
|
||||
.leaflet-top.leaflet-left { top: auto !important; bottom: 20px !important; left: 15px !important; }
|
||||
.leaflet-popup-content-wrapper { border-radius: 12px; max-width: 95vw !important; }
|
||||
#journal-panel {
|
||||
position: fixed; top: 0; right: -100%; width: 400px; height: 100%;
|
||||
background: white; z-index: 10005; transition: 0.4s;
|
||||
box-shadow: -5px 0 15px rgba(0,0,0,0.2); padding: 25px;
|
||||
overflow-y: auto; font-family: sans-serif;
|
||||
}
|
||||
#journal-panel.open { right: 0; }
|
||||
@media (max-width: 600px) { #journal-panel { width: 85%; } }
|
||||
.close-btn { position: absolute; top: 15px; right: 15px; font-size: 24px; cursor: pointer; border: none; background: none; }
|
||||
.file-header { background: #f0f0f0; padding: 4px 8px; border-radius: 4px; margin-bottom: 8px; font-size: 11px; font-family: monospace; display: block; overflow: hidden; text-overflow: ellipsis; }
|
||||
.slider-container { position: relative; width: calc(85vw - 60px); max-width: 800px; text-align: center; min-height: 200px; }
|
||||
.slide img { max-width: 100%; max-height: 65vh; border-radius: 8px; display: block; margin: auto; }
|
||||
.nav-btn { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.6); color: white; border: 2px solid white; padding: 12px; cursor: pointer; border-radius: 50%; z-index: 10; font-size: 18px; }
|
||||
.prev { left: -10px; } .next { right: -10px; }
|
||||
.slide-counter { margin-top: 8px; font-size: 13px; font-weight: bold; color: #333; font-family: sans-serif; }
|
||||
</style>
|
||||
"""
|
||||
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"""
|
||||
<div id="journal-panel"><button class="close-btn" onclick="toggleJournal(false)">×</button><div class="journal-content">{journal_html}</div></div>
|
||||
<div style="position: fixed; bottom: 25px; right: 15px; z-index: 10000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px;">
|
||||
<button onclick="toggleJournal(true)" style="background: #FFD700; border: 2px solid white; padding: 12px 20px; border-radius: 50px; font-weight: bold; font-size: 14px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.3); font-family:sans-serif;">📖 Journal</button>
|
||||
</div>
|
||||
"""
|
||||
|
||||
# --- 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'<a href="html/{file_name}" target="_top" style="display:block; margin-bottom:8px; padding:12px; background:{color_day}; color:white; text-decoration:none; border-radius:8px; font-weight:bold; text-align:center; font-family:sans-serif;">{day_str}</a>'
|
||||
|
||||
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"<div style='text-align:center; font-family:sans-serif;'><b>📅 {day_str}</b><br><br><a href='html/{file_name}' target='_top' style='background:{color_day}; color:white; padding:8px 12px; border-radius:4px; text-decoration:none; font-weight:bold;'>Détails</a></div>"
|
||||
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('<div style="position: fixed; top: 15px; left: 15px; z-index: 10001;"><a href="../index.html" target="_top" style="text-decoration: none; background: #333; color: white; padding: 12px 20px; border-radius: 10px; font-family: sans-serif; font-weight: bold; box-shadow: 0 4px 10px rgba(0,0,0,0.5); border: 2px solid white; display: block;">🏠 Accueil</a></div>' + get_journal_ui()))
|
||||
|
||||
if current_gpx_filename:
|
||||
m_day.get_root().html.add_child(folium.Element(f'<div style="position: fixed; bottom: 85px; right: 15px; z-index: 10001;"><a href="../routes/{current_gpx_filename}" download style="text-decoration: none; background: #28a745; color: white; padding: 12px 20px; border-radius: 50px; font-family: sans-serif; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.4); border: 2px solid white; display: block;">📥 GPX</a></div>'))
|
||||
|
||||
# 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'<div class="slide" style="display:{"block" if idx == 0 else "none"};"><div class="file-header">{p.strip()}</div><a href="../photos/{p.strip()}" target="_blank"><img src="../photos/{p.strip()}"></a></div>' for idx, p in enumerate(fichiers)])
|
||||
# OPTIMISATION : Ajout de loading="lazy" dans la balise img
|
||||
slides = "".join([f'<div class="slide" style="display:{"block" if idx == 0 else "none"};"><div class="file-header">{p.strip()}</div><a href="../photos/{p.strip()}" target="_blank"><img src="../photos/{p.strip()}" loading="lazy"></a></div>' for idx, p in enumerate(fichiers)])
|
||||
btns = f'<button class="nav-btn prev" onclick="moveSlide(this, -1)">❮</button><button class="nav-btn next" onclick="moveSlide(this, 1)">❯</button><div class="slide-counter">1/{nb}</div>' if nb > 1 else ""
|
||||
folium.Marker(location=[ref['Latitude'], ref['Longitude']], popup=folium.Popup(f'<div class="slider-container"><div class="slides">{slides}</div>{btns}</div>', 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'<div style="width:calc(85vw - 40px); max-width:1000px; text-align:center;"><div class="file-header">{v_name.strip()}</div><video style="width:100%; max-height:70vh; border-radius:8px; background:black;" controls playsinline webkit-playsinline autoplay muted><source src="../videos/{v_name.strip()}" type="video/mp4"></video></div>'
|
||||
v_pop = f'<div style="width:calc(85vw - 40px); max-width:1000px; text-align:center;"><div class="file-header">{v_name.strip()}</div><video style="width:100%; max-height:70vh; border-radius:8px; background:black;" controls playsinline webkit-playsinline autoplay muted preload="auto"><source src="../videos/{v_name.strip()}" type="video/mp4"></video></div>'
|
||||
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'<div style="position: fixed; top: 15px; left: 15px; width: 160px; max-height: 85vh; overflow-y: auto; background: rgba(255,255,255,0.9); z-index: 9999; padding: 12px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.2);"><a href="../" target="_top" style="display:block; margin-bottom:15px; padding:12px; background:#444; color:white; text-decoration:none; border-radius:8px; font-weight:bold; text-align:center; border: 2px solid #666; font-family:sans-serif;">⬅ Retour</a><div style="border-top: 1px solid #ccc; margin-bottom: 10px; padding-top: 10px; font-family:sans-serif; font-size:12px; font-weight:bold; color:#666; text-align:center;">PAR JOURS</div>{sidebar_links_html}</div>'
|
||||
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"))
|
||||
48
Scripts/genere_index_general.py
Normal file
48
Scripts/genere_index_general.py
Normal file
@@ -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"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Nos Road-Trips</title>
|
||||
<style>
|
||||
body {{ font-family: 'Segoe UI', sans-serif; background: #f4f4f9; margin: 0; padding: 20px; }}
|
||||
.grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 30px; max-width: 1200px; margin: 0 auto; }}
|
||||
.card {{ background: white; border-radius: 15px; overflow: hidden; box-shadow: 0 10px 20px rgba(0,0,0,0.1); }}
|
||||
.map-container {{ width: 100%; height: 300px; border: none; pointer-events: none; }}
|
||||
.card-content {{ padding: 20px; text-align: center; }}
|
||||
.btn {{ display: inline-block; padding: 10px 25px; background: #3498db; color: white; text-decoration: none; border-radius: 25px; font-weight: bold; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align:center;">🌍 Nos Road-Trips</h1>
|
||||
<div class="grid">
|
||||
"""
|
||||
for trip in road_trips:
|
||||
html_content += f"""
|
||||
<div class="card">
|
||||
<iframe class="map-container" src="{trip}/mini.html"></iframe>
|
||||
<div class="card-content">
|
||||
<h2 style="text-transform: capitalize;">{trip.replace('-', ' ')}</h2>
|
||||
<a href="{trip}/index.html" class="btn">Voir le voyage</a>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
html_content += "</div></body></html>"
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f: f.write(html_content)
|
||||
|
||||
if __name__ == "__main__":
|
||||
generer_index_general()
|
||||
Reference in New Issue
Block a user