fix(dashcam): range-request video streaming, modal player, async thumbnails

- app.py: replace send_file with manual Range-aware chunked generator;
  returns 206 on partial requests so browsers can seek/buffer properly.
  Thumbnail route returns 202 while ffmpeg is generating.
- app_video.py: thumbnail generation moved to background gevent greenlet
  (_generate_thumbnail_bg). get_thumbnail_path returns immediately with
  RuntimeError('generating') if not cached; ffmpeg only ever runs once
  per video (result cached in dash/thumbnails/).
- dash.html: replaced bottom card player with Bootstrap modal (modal-xl,
  centered, dark bg) — video pops up in place regardless of scroll position.
- local-videos.js: rewritten to use Bootstrap modal; thumbnail loader
  polls with fetch() and retries on 202 every 2s (up to 15x) instead of
  setting img.src directly (which can't detect 202).

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 00:01:49 -04:00
co-authored by Copilot
parent 35ec705fe2
commit 600fd41353
4 changed files with 300 additions and 47 deletions
+13 -10
View File
@@ -99,16 +99,19 @@
</div>
</div>
{# ── Video player (hidden until opened) ─────────────────────────────── #}
<div id="local-video-player-panel" class="card shadow-sm mb-3" hidden>
<div class="card-header py-2 d-flex align-items-center justify-content-between">
<p id="local-video-player-title" class="text-primary m-0 fw-bold small"></p>
<button id="local-video-player-close" class="btn btn-sm btn-outline-secondary" type="button">
<i class="fas fa-times me-1"></i>Close
</button>
</div>
<div class="card-body p-0">
<video id="local-video-player" class="local-video-player" controls preload="metadata"></video>
{# ── Video player modal ──────────────────────────────────────────────── #}
<div class="modal fade" id="video-player-modal" tabindex="-1" aria-labelledby="video-player-modal-title" aria-modal="true" role="dialog">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content bg-dark">
<div class="modal-header border-secondary py-2">
<h6 class="modal-title text-light text-truncate me-2" id="video-player-modal-title"></h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-0">
<video id="local-video-player" class="w-100 d-block" style="max-height:75vh;background:#000;"
controls preload="metadata"></video>
</div>
</div>
</div>
</div>
{% endblock %}