Administration native sur le Mac, réencodage sélectif et aide intégrée

- admin.sh : RTG lancé directement sur le Mac (plus de conteneur), nginx
  joint l'interface par host.docker.internal:8765 ; variante conteneur
  conservée (admin-docker.sh, docker-compose-conteneur.yml)
- Compression : réglages lus dans les fichiers (CRF, codec), refaire
  seulement le 1080p ou le 720p, seulement si les réglages ont changé ;
  réglages vidéo propres à chaque voyage ; « Compression actuelle » affichée
- Plus d'arrêt automatique après 2 h d'inactivité (--arret-inactif, 0 par défaut) ;
  arrêt propre qui attend la fin des encodages en cours
- Aide contextuelle dans l'interface (rtg/interface/aide) ; README centré
  sur l'installation et la mise en place
- Cœurs attribués au conteneur détectés (cgroup) et signalé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-19 05:32:05 +00:00
1 parent b54ae69b19
commit ada3d76f50
38 files changed
+1067 -264

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
<div class="aide-texte">{{ contenu|safe }}</div>
<p class="aide-pied"><a href="{{ base }}/aide/{{ sujet }}" target="_blank">Ouvrir dans une page ↗</a> · <a href="#" hx-get="{{ base }}/aide/index?panneau=1" hx-target="#aide-contenu">Sommaire</a></p>
+55
View File
@@ -0,0 +1,55 @@
<div id="compression" class="encart compression" hx-get="{{ base }}/voyage/compression?v={{ v.chemin|url }}"
hx-trigger="tache-finie from:body" hx-swap="outerHTML">
<h3>Compression des vidéos</h3>
<table>
<thead><tr><th></th><th>Vidéos actuelles</th><th>Prochains encodages</th></tr></thead>
<tbody>
{% for version in ("1080p", "720p") %}
{% set b = bilan[version] %}{% set c = cibles[version] %}
<tr>
<td><strong>{{ version }}</strong></td>
<td>
{% if not b.total %}<span class="discret">aucune</span>
{% else %}
{% for reg, n in b.reglages.most_common() %}
<div>{{ reg[0] }}{% if reg[1] is not none %} CRF {{ reg[1]|crf }}{% else %} <span class="discret">(réglages non inscrits : encodeur matériel ?)</span>{% endif %}
<span class="discret">× {{ n }}</span>
{% if reg == c %}<span class="pastille ok">conforme</span>{% else %}<span class="pastille attention">différent</span>{% endif %}
</div>
{% endfor %}
<div class="discret">{{ b.total }} vidéo{{ 's' if b.total > 1 }}{% if b.lues < b.total %} · échantillon de {{ b.lues }}{% endif %}</div>
{% endif %}
</td>
<td>{{ c[0] }}{% if c[1] is not none %} CRF {{ c[1]|crf }}{% else %} (encodeur matériel){% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="aide">Qualité {{ noms_qualites.get(cfg.video.qualite, cfg.video.qualite) }}, vitesse {{ cfg.video.preset }},
encodeur {{ "matériel" if p.encodeur == "vt" else "processeur" }} :
{{ "réglages propres à ce voyage" if propre else "réglages du site" }}.
Plus le CRF est élevé, plus les fichiers sont légers, et moins la qualité est bonne.</p>
<details {{ 'open' if propre }}>
<summary>Réglages vidéo propres à ce voyage</summary>
<form class="formulaire" hx-post="{{ base }}/voyage/video" hx-target="#compression" hx-swap="outerHTML">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label>Qualité
<select name="qualite">
<option value="">Comme le site ({{ noms_qualites.get(site.video.qualite, site.video.qualite) }})</option>
{% for q in qualites %}<option value="{{ q }}" {{ 'selected' if propre.qualite == q }}>{{ noms_qualites[q]|capitalize }} (CRF {{ qualites[q].crf_hevc }} / {{ qualites[q].crf_h264 }})</option>{% endfor %}
</select></label>
<label>Vitesse de l'encodeur
<select name="preset">
<option value="">Comme le site ({{ site.video.preset }})</option>
{% for pr in presets %}<option {{ 'selected' if propre.preset == pr }}>{{ pr }}</option>{% endfor %}
</select>
<span class="aide">Plus lent = un peu plus léger à qualité égale.</span></label>
<label>CRF 1080p (HEVC), pour un essai
<input type="number" name="crf_hevc" min="0" max="51" value="{{ propre.crf_hevc if propre.crf_hevc is defined }}" placeholder="selon la qualité ({{ qualites.get(cfg.video.qualite, qualites.equilibree).crf_hevc }})"></label>
<label>CRF 720p (H.264), pour un essai
<input type="number" name="crf_h264" min="0" max="51" value="{{ propre.crf_h264 if propre.crf_h264 is defined }}" placeholder="selon la qualité ({{ qualites.get(cfg.video.qualite, qualites.equilibree).crf_h264 }})"></label>
<p><button class="principal">Enregistrer</button> <span class="ok">{{ message }}</span></p>
</form>
</details>
</div>
+15
View File
@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block titre %}Aide{% endblock %}
{% block contenu %}
<div class="page-aide">
<nav class="aide-sommaire">
<h2>Aide</h2>
<ul>
{% for s, titre in sujets.items() %}
<li><a href="{{ base }}/aide/{{ s }}" class="{{ 'actif' if s == sujet }}">{{ titre }}</a></li>
{% endfor %}
</ul>
</nav>
<article class="aide-texte">{{ contenu|safe }}</article>
</div>
{% endblock %}
+5
View File
@@ -15,6 +15,7 @@
<a href="{{ base }}/">Mes voyages</a>
<a href="{{ base }}/reglages">Réglages</a>
<a href="{{ base }}/tache">Traitement</a>
<a href="{{ base }}/aide">Aide</a>
<a href="{{ lien_site }}" target="_blank">Voir le site ↗</a>
</nav>
<div id="bandeau" hx-get="{{ base }}/tache/bandeau" hx-trigger="load, every 2s"></div>
@@ -25,6 +26,10 @@
</main>
<div id="messages" aria-live="polite"></div>
<div id="fenetre"></div>
<aside id="aide" class="aide-panneau" hidden aria-label="Aide">
<button class="petit aide-fermer" data-fermer-aide title="Fermer (Échap)">✕</button>
<div id="aide-contenu"></div>
</aside>
<footer>RoadTripsGenerator {{ version }} · <code>{{ racine }}</code></footer>
<script src="{{ base }}/static/rtg.js"></script>
{% block fin %}{% endblock %}
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block titre %}Nouveau voyage{% endblock %}
{% block contenu %}
<h1>Nouveau voyage</h1>
<h1>Nouveau voyage <button class="bouton-aide" hx-get="{{ base }}/aide/voyages?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">?</button></h1>
<form id="formulaire" class="formulaire" hx-post="{{ base }}/nouveau" hx-target="#formulaire" hx-select="#formulaire" hx-swap="outerHTML">
{% if erreur %}<div class="erreur">{{ erreur }}</div>{% endif %}
<label>Titre
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block titre %}Réglages{% endblock %}
{% block contenu %}
<h1>Réglages du site</h1>
<h1>Réglages du site <button class="bouton-aide" hx-get="{{ base }}/aide/reglages?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">?</button></h1>
<p class="aide">Enregistrés dans <code>site.toml</code>. Ils valent pour tous les voyages.</p>
<form class="formulaire" hx-post="{{ base }}/reglages" hx-target="#retour-reglages">
<fieldset>
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block titre %}Traitement{% endblock %}
{% block contenu %}
<h1>Traitement</h1>
<h1>Traitement <button class="bouton-aide" hx-get="{{ base }}/aide/generer?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">?</button></h1>
{% if tache %}
{% if tache.voyage %}<p><a href="{{ base }}/voyage?v={{ tache.voyage|url }}">← {{ tache.voyage }}</a></p>{% endif %}
{% with complet = True %}{% include "_tache.html" %}{% endwith %}
+18 -2
View File
@@ -23,12 +23,14 @@
</nav>
<section class="onglet" id="onglet-journees">
<div class="onglet-aide"><button class="bouton-aide" hx-get="{{ base }}/aide/journees?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">? Aide</button></div>
<div id="analyse" hx-get="{{ base }}/voyage/analyse?v={{ v.chemin|url }}" hx-trigger="load, sources-modifiees from:body, tache-finie from:body">
<p class="chargement">Lecture des traces…</p>
</div>
</section>
<section class="onglet" id="onglet-carte">
<div class="onglet-aide"><button class="bouton-aide" hx-get="{{ base }}/aide/carte?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">? Aide</button></div>
<div id="editeur" class="editeur" data-voyage="{{ v.chemin }}">
<aside class="editeur-panneau">
<label>Jour
@@ -64,6 +66,7 @@
</section>
<section class="onglet" id="onglet-sources">
<div class="onglet-aide"><button class="bouton-aide" hx-get="{{ base }}/aide/sources?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">? Aide</button></div>
<p class="aide">Indiquez où se trouvent les fichiers d'origine : un ou plusieurs dossiers (ou fichiers) par type, sur n'importe quel disque.
RTG ne les modifie jamais : les traces sont recopiées par journée dans <code>routes/</code>, les photos dans <code>photos/</code> (HEIC converties en JPEG),
et les vidéos sont lues directement pour l'encodage.</p>
@@ -71,6 +74,7 @@
</section>
<section class="onglet" id="onglet-infos">
<div class="onglet-aide"><button class="bouton-aide" hx-get="{{ base }}/aide/infos?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">? Aide</button></div>
<form class="formulaire" hx-post="{{ base }}/voyage/infos" hx-target="find .retour">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label>Titre <input name="titre" value="{{ cfg.titre }}"></label>
@@ -90,11 +94,23 @@
</section>
<section class="onglet" id="onglet-generer">
<div class="onglet-aide"><button class="bouton-aide" hx-get="{{ base }}/aide/generer?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">? Aide</button></div>
<div hx-get="{{ base }}/voyage/infos-utiles?v={{ v.chemin|url }}" hx-trigger="load, sources-modifiees from:body, tache-finie from:body"><p class="chargement">Calcul des tailles…</p></div>
<div id="compression" hx-get="{{ base }}/voyage/compression?v={{ v.chemin|url }}" hx-trigger="load, tache-finie from:body" hx-swap="outerHTML"><p class="chargement">Lecture de la compression des vidéos…</p></div>
<form class="formulaire" hx-post="{{ base }}/tache/lancer" hx-target="#tache-zone">
<input type="hidden" name="v" value="{{ v.chemin }}">
<label class="choix"><input type="checkbox" name="sans_compression"> Sans encoder les vidéos <span class="aide">(photos, positions et pages seulement)</span></label>
<label class="choix"><input type="checkbox" name="force"> Tout refaire, même ce qui est déjà à jour</label>
<fieldset>
<legend>Vidéos</legend>
<label class="choix"><input type="radio" name="refaire" value="manquantes" checked> Encoder seulement celles qui manquent</label>
<label class="choix"><input type="radio" name="refaire" value="differentes"> Réencoder aussi celles faites avec d'autres réglages <span class="aide">(lus dans chaque fichier)</span></label>
<label class="choix"><input type="radio" name="refaire" value="toutes"> Tout réencoder</label>
<p class="aide">Versions concernées :
<label class="choix" style="display:inline"><input type="radio" name="versions" value="deux" checked> les deux</label>
<label class="choix" style="display:inline"><input type="radio" name="versions" value="1080p"> 1080p seulement</label>
<label class="choix" style="display:inline"><input type="radio" name="versions" value="720p"> 720p seulement</label></p>
<label class="choix"><input type="checkbox" name="sans_compression"> Ne pas toucher aux vidéos <span class="aide">(photos, positions et pages seulement)</span></label>
</fieldset>
<label class="choix"><input type="checkbox" name="force"> Refaire aussi les photos web et les pages, même à jour</label>
<p>
<button class="principal" name="quoi" value="generer">Générer le voyage</button>
<button name="quoi" value="compresser">Encoder les vidéos seulement</button>
+1 -1
View File
@@ -10,7 +10,7 @@
{% endif %}
<div class="entete-page">
<h1>Mes voyages <span class="compte">{{ nb }}</span></h1>
<h1>Mes voyages <span class="compte">{{ nb }}</span> <button class="bouton-aide" hx-get="{{ base }}/aide/voyages?panneau=1" hx-target="#aide-contenu" data-ouvrir-aide title="Aide">?</button></h1>
<div class="actions">
<a class="bouton principal" href="{{ base }}/nouveau">+ Nouveau voyage</a>
<form hx-post="{{ base }}/tache/lancer" hx-target="#tache-zone">