Le viser de demo.py était tué dès que le PLY était écrit (pour libérer la VRAM),
donc les liens dans le dashboard menaient vers ERR_CONNECTION_REFUSED.
Ajout d'un viewer standalone indépendant :
- scripts/viser_ply.py : charge un PLY via open3d + sert via viser (sans GPU),
subsample random à 2M pts max pour rester fluide
- app/main.py : routes POST /jobs/{id}/view et /stitches/{id}/view qui scp
le script sur le worker et lancent un viser détaché (nohup+setsid+disown via
wrapper shell déposé sur le worker)
- templates : remplace <a href> par <button class=viewer-btn> qui POST puis
window.open de l'URL retournée
- Dockerfile : copie scripts/ dans l'image (nécessaire pour scp-er viser_ply.py)
83 lines
3.6 KiB
HTML
83 lines
3.6 KiB
HTML
{% if not acquisitions %}
|
||
<p class="muted">Aucune acquisition. Ingeste un dossier via <code>scripts/ingest.py</code>.</p>
|
||
{% else %}
|
||
<div class="acq-grid">
|
||
{% for acq in acquisitions %}
|
||
<div class="acq-col">
|
||
<h3 class="acq-title">
|
||
{{ acq.name }} <span class="total">{{ acq.total_duration }}</span>
|
||
</h3>
|
||
|
||
<ul class="job-list">
|
||
{% for j in acq.jobs %}
|
||
<li class="job-item {{ j.status }}">
|
||
<span class="icon">
|
||
{% if j.status == 'done' %}<span class="check">✓</span>
|
||
{% elif j.status in ('running','extracting') %}<span class="spin">↻</span>
|
||
{% elif j.status == 'error' %}<span class="err">✕</span>
|
||
{% else %}<span class="sq">■</span>{% endif %}
|
||
</span>
|
||
<span class="label">
|
||
{{ j.auv }}/{{ j.gopro_serial }}/{{ j.segment_label }}
|
||
{% if j.status == 'done' and j.ply_path %}
|
||
<button class="ext viewer-btn" data-view-url="jobs/{{ j.id }}/view">viser</button>
|
||
{% endif %}
|
||
</span>
|
||
<span class="dur">{{ j._duration }}</span>
|
||
{% if j.status in ('queued','extracting','running') %}
|
||
<button class="mini" hx-post="jobs/{{ j.id }}/cancel" hx-target="#jobs-table">×</button>
|
||
{% elif j.status == 'error' %}
|
||
<button class="mini" hx-post="jobs/{{ j.id }}/retry" hx-target="#jobs-table">↻</button>
|
||
{% else %}
|
||
<span></span>
|
||
{% endif %}
|
||
</li>
|
||
{% if j.error %}<li class="err-line">{{ j.error }}</li>{% endif %}
|
||
{% endfor %}
|
||
</ul>
|
||
|
||
<div class="stitch-section">
|
||
<div class="stitch-title">
|
||
<span class="icon"><span class="sq">■</span></span>
|
||
<span>stitch</span>
|
||
</div>
|
||
{% if acq.stitches %}
|
||
<ul class="stitch-children">
|
||
{% for s in acq.stitches %}
|
||
<li class="sub {{ s.status }}">
|
||
<span class="icon stitch-icon">
|
||
{% if s.status == 'done' %}<span class="check ok">✓</span>
|
||
{% elif s.status == 'running' %}<span class="spin">↻</span>
|
||
{% elif s.status == 'error' %}<span class="err">✕</span>
|
||
{% else %}<span class="sq">■</span>{% endif %}
|
||
</span>
|
||
<span>
|
||
{% if s.level == 'per_auv' %}pair GP1↔GP2 {{ s.auv }}
|
||
{% else %}merge final{% endif %}
|
||
{% if s._duration %}<span class="dur muted"> — {{ s._duration }}</span>{% endif %}
|
||
{% if s.status == 'done' and s.output_ply %}
|
||
<button class="ext viewer-btn" data-view-url="stitches/{{ s.id }}/view" title="{{ s.output_ply }}">viser</button>
|
||
{% endif %}
|
||
</span>
|
||
{% 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 %}
|
||
</li>
|
||
{% if s.error %}<li class="err-line" style="padding-left:42px">{{ s.error[:120] }}</li>{% endif %}
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<ul class="stitch-children">
|
||
<li class="sub pending"><span class="sq">■</span> pair GP1↔GP2 per AUV</li>
|
||
<li class="sub pending"><span class="sq">■</span> cross-AUV merge</li>
|
||
<li class="sub pending"><span class="sq">■</span> final PLY</li>
|
||
</ul>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|