- dispatcher: col step ajoutee (migration). set a chaque phase: scp N/M, ffmpeg N/M, trimming hors-eau, reconstruct demo.py - _refresh_thumbnail() scp la DERNIERE frame extraite toutes les ~15s pendant ffmpeg pour que le preview colle a la progression live - template: cache-bust thumbnail via ?t=mtime, step affiche sous progress bar, thumb revenue a 48x27 - CSS: .badge.busy -> animation spin (rotation infinie) au lieu de juste une couleur, .step-text italique mute
114 lines
5.8 KiB
HTML
114 lines
5.8 KiB
HTML
{% if not acquisitions %}
|
||
<p class="muted">Aucune acquisition. Ingeste un dossier via <code>scripts/ingest.py</code>.</p>
|
||
{% else %}
|
||
<div class="acq-list">
|
||
{% for acq in acquisitions %}
|
||
<section class="acq">
|
||
<h3 class="acq-title">
|
||
{{ acq.name }} <span class="total">{{ acq.total_duration }}</span>
|
||
</h3>
|
||
|
||
<table class="jobs-table">
|
||
<thead>
|
||
<tr>
|
||
<th class="col-status"></th>
|
||
<th class="col-thumb">preview</th>
|
||
<th class="col-id">#</th>
|
||
<th class="col-auv">AUV · GP</th>
|
||
<th class="col-seg">label</th>
|
||
<th class="col-dur">durée</th>
|
||
<th class="col-frames">frames</th>
|
||
<th class="col-trim">hors-eau</th>
|
||
<th class="col-progress">progression</th>
|
||
<th class="col-elapsed">temps</th>
|
||
<th class="col-actions"></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for j in acq.jobs %}
|
||
<tr class="job-row {{ j.status }}">
|
||
<td class="col-status" title="{{ j.status }}">
|
||
{% if j.status == 'done' %}<span class="badge ok">✓</span>
|
||
{% elif j.status in ('running','extracting') %}<span class="badge busy">↻</span>
|
||
{% elif j.status == 'error' %}<span class="badge err">✕</span>
|
||
{% elif j.status == 'skipped' %}<span class="badge muted">⊘</span>
|
||
{% else %}<span class="badge muted">■</span>{% endif %}
|
||
</td>
|
||
<td class="col-thumb">
|
||
{% if j.has_thumbnail %}<img src="jobs/{{ j.id }}/thumbnail?t={{ j.thumb_ts }}" alt="" loading="lazy">{% else %}<span class="thumb-placeholder"></span>{% endif %}
|
||
</td>
|
||
<td class="col-id">#{{ j.id }}</td>
|
||
<td class="col-auv"><strong>{{ j.auv }}</strong> · <span title="{{ j.gopro_serial }}">{{ j.gp_label }}</span></td>
|
||
<td class="col-seg">{{ j.segment_label }}</td>
|
||
<td class="col-dur">{{ j.video_duration_fmt }}</td>
|
||
<td class="col-frames">{% if j.frame_count %}{{ j.frame_count }}{% else %}—{% endif %}</td>
|
||
<td class="col-trim">{% if j.trimmed_total %}−{{ j.trimmed_total }}{% else %}—{% endif %}</td>
|
||
<td class="col-progress">
|
||
{% if j.status in ('extracting','running') %}
|
||
<div class="progress-wrap">
|
||
<span class="prog-bar"><span class="prog-fill" style="width:{{ j.progress }}%"></span></span>
|
||
<span class="prog-text">{{ j.progress }}%</span>
|
||
</div>
|
||
{% if j.step %}<div class="step-text">{{ j.step }}</div>{% endif %}
|
||
{% elif j.status == 'done' and j.viser_url %}
|
||
<a class="btn-viser" href="{{ j.viser_url }}" target="_blank" rel="noopener">viser ↗</a>
|
||
{% elif j.status == 'skipped' %}
|
||
<span class="muted">skipped</span>
|
||
{% elif j.status == 'error' %}
|
||
<span class="muted" title="{{ j.error }}">failed</span>
|
||
{% else %}
|
||
<span class="muted">—</span>
|
||
{% endif %}
|
||
</td>
|
||
<td class="col-elapsed">{{ j._duration }}</td>
|
||
<td class="col-actions">
|
||
{% if j.status in ('queued','extracting','running') %}
|
||
<button class="mini" hx-post="jobs/{{ j.id }}/cancel" hx-target="#jobs-table" title="annuler">×</button>
|
||
{% elif j.status == 'error' %}
|
||
<button class="mini" hx-post="jobs/{{ j.id }}/retry" hx-target="#jobs-table" title="retry">↻</button>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="stitch-section">
|
||
<div class="stitch-title">stitch</div>
|
||
{% if acq.stitches %}
|
||
<ul class="stitch-list">
|
||
{% for s in acq.stitches %}
|
||
<li class="stitch-item {{ s.status }}">
|
||
<span class="badge {% if s.status == 'done' %}ok{% elif s.status == 'running' %}busy{% elif s.status == 'error' %}err{% else %}muted{% endif %}">
|
||
{% if s.status == 'done' %}✓{% elif s.status == 'running' %}↻{% elif s.status == 'error' %}✕{% else %}■{% endif %}
|
||
</span>
|
||
<span class="stitch-label">
|
||
{% if s.level == 'per_auv' %}pair GP1↔GP2 {{ s.auv }}
|
||
{% else %}merge final{% endif %}
|
||
</span>
|
||
{% if s._duration %}<span class="muted"> · {{ s._duration }}</span>{% endif %}
|
||
{% if s.status == 'done' and s.output_ply %}
|
||
<button class="btn-viser" data-view-url="stitches/{{ s.id }}/view">viser ↗</button>
|
||
{% endif %}
|
||
{% if s.status in ('queued','running') %}
|
||
<button class="mini" hx-post="stitches/{{ s.id }}/cancel" hx-target="#jobs-table">×</button>
|
||
{% elif s.status == 'error' %}
|
||
<button class="mini" hx-post="stitches/{{ s.id }}/retry" hx-target="#jobs-table">↻</button>
|
||
{% endif %}
|
||
{% if s.error %}<div class="err-line">{{ s.error[:140] }}</div>{% endif %}
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<ul class="stitch-list">
|
||
<li class="stitch-item pending"><span class="badge muted">■</span> <span class="muted">pair GP1↔GP2 per AUV</span></li>
|
||
<li class="stitch-item pending"><span class="badge muted">■</span> <span class="muted">cross-AUV merge</span></li>
|
||
<li class="stitch-item pending"><span class="badge muted">■</span> <span class="muted">final PLY</span></li>
|
||
</ul>
|
||
{% endif %}
|
||
</div>
|
||
</section>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|