Interface locale : rtg interface (voyages, sources, journées, récit, génération)

Serveur web local sans dépendance nouvelle (http.server, Jinja2, htmx et
Leaflet fournis). Écrans : Mes voyages, nouveau voyage, un voyage (journées
avec carte, rattachement des traces sans heures à un jour avec suggestion
d'après les photos, sources avec choix de dossier, infos et récit,
génération avec avancement et arrêt), réglages du site et vérification
des outils.

Chaîne : jours imposés ([dates_traces] dans voyage.toml), arrêt propre de
la compression, suivi des étapes, état structuré (site.tableau_etat),
titre du voyage repris de voyage.toml sur l'accueil.

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 14:07:47 +00:00
1 parent 608334b9d8
commit 06153a227e
37 files changed
+2618 -57

No files matched your search

+20 -8
View File
@@ -16,7 +16,7 @@ import time
from concurrent.futures import ThreadPoolExecutor
from rtg.config import parametres_video
from rtg.outils import coeurs_performance, dire, exiger, lisible, taille_lisible
from rtg.outils import ARRET, coeurs_performance, dire, exiger, lisible, taille_lisible
def _encodeurs_disponibles():
@@ -126,8 +126,9 @@ def deja_faite(voyage, src, rapide=False):
def compresser(voyage, cfg, analyse, force=False, progression=None):
"""Compresse les rushs du voyage. `progression(nom, etat, pourcentage)` est
appelée à chaque changement (pour l'interface). Renvoie True si tout a réussi."""
"""Compresse les rushs du voyage. `progression(nom, etat, pourcentage, rang)`
est appelée à chaque changement (pour l'interface) ; rang = (i, total).
Renvoie True si tout a réussi."""
videos = analyse.videos
os.makedirs(os.path.join(voyage, "videos"), exist_ok=True)
p = parametres_video(cfg)
@@ -148,9 +149,9 @@ def compresser(voyage, cfg, analyse, force=False, progression=None):
verrou = threading.Lock()
bilan = {"faits": 0, "sautes": 0, "echecs": 0, "octets_src": 0, "octets_web": 0}
def signaler(nom, etat, detail=None):
def signaler(nom, etat, detail=None, rang=None):
if progression:
progression(nom, etat, detail)
progression(nom, etat, detail, rang)
total = len(videos)
en_direct = jobs == 1 and sys.stdout.isatty()
@@ -160,15 +161,17 @@ def compresser(voyage, cfg, analyse, force=False, progression=None):
nom = os.path.basename(src)
rang = f"[{i:3d}/{total}]"
hd, sd = sorties(voyage, src)
if ARRET.is_set():
return False
if not force and lisible(hd) and lisible(sd):
with verrou:
bilan["sautes"] += 1
dire(f"⏭️ {rang} {nom} — déjà faite")
signaler(nom, "deja_faite")
signaler(nom, "deja_faite", rang=(i, total))
return True
h, duree = _hauteur(src), _duree(src)
dire(f"🎬 {rang} {nom} ({h}p, {int(duree) // 60} min {int(duree) % 60:02d} s) — encodage…")
signaler(nom, "en_cours", 0)
signaler(nom, "en_cours", 0, rang=(i, total))
tmp_hd = os.path.join(os.path.dirname(hd), ".tmp-" + os.path.basename(hd))
tmp_sd = os.path.join(os.path.dirname(sd), ".tmp-" + os.path.basename(sd))
avancement = {"hd": 0.0, "sd": 0.0}
@@ -183,11 +186,14 @@ def compresser(voyage, cfg, analyse, force=False, progression=None):
dernier = -1
while any(pr.poll() is None for pr in procs):
time.sleep(0.5)
if ARRET.is_set():
for pr in procs:
pr.terminate()
moyenne = int((avancement["hd"] + avancement["sd"]) / 2)
if en_direct:
print(f"\r 1080p {avancement['hd']:3.0f} % · 720p {avancement['sd']:3.0f} % ",
end="", flush=True)
elif moyenne // 25 > dernier // 25 and moyenne < 100:
elif 0 < moyenne < 100 and moyenne // 25 > max(dernier, 0) // 25:
dire(f" {nom} : {moyenne} %")
if moyenne != dernier:
signaler(nom, "en_cours", moyenne)
@@ -211,6 +217,10 @@ def compresser(voyage, cfg, analyse, force=False, progression=None):
for t in (tmp_hd, tmp_sd):
if os.path.exists(t):
os.remove(t)
if ARRET.is_set():
dire(f" ⏹ {nom} : arrêtée")
signaler(nom, "arretee")
return False
with verrou:
bilan["echecs"] += 1
dire(f" ❌ Échec sur {nom}")
@@ -235,4 +245,6 @@ def compresser(voyage, cfg, analyse, force=False, progression=None):
dire(f"ℹ️ Aucun nouvel encodage ({bilan['sautes']} déjà faite(s))")
if bilan["echecs"]:
dire(f"⚠️ {bilan['echecs']} échec(s)")
if ARRET.is_set():
dire("⏹ Compression arrêtée (les vidéos finies sont gardées).")
return all(resultats)