Add duration prewarm button; show video lengths in the loaded-course view

Settings gets a "Precompute Video Lengths" button that walks the whole
library in a background thread, populating every course's persistent
ffprobe duration cache up front instead of paying that cost the first
time each course card is viewed. Progress polls live while it runs and
picks back up correctly if you navigate away mid-scan.

Separately, the loaded-course lesson tree only ever showed a lesson's
duration once you'd actually played it (from progress.json) - a freshly
opened course had no time info anywhere, including the course header's
"~X remaining" line, which existed but was always empty as a result.
apply_progress_to_tree now falls back to the same ffprobe cache for any
lesson without a known duration yet, so per-lesson lengths and the
remaining-time estimate work from the very first visit. Refactored the
duration-cache read/write into a shared helper so the library browser,
the prewarm button, and this tree view all go through one path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 22:20:10 -04:00
co-authored by Claude Sonnet 5
parent 8f7808ab46
commit 24a07dd3e6
3 changed files with 194 additions and 23 deletions
+9
View File
@@ -896,6 +896,12 @@
.lesson-type.quiz { background: #f39c12; color: white; }
.lesson-type.mixed { background: #16a085; color: white; }
.lesson-duration {
font-size: 0.8em;
color: var(--text-muted);
white-space: nowrap;
}
.status-icon {
font-size: 1.2em;
font-weight: bold;
@@ -1081,6 +1087,9 @@
</div>
<div class="lesson-meta">
<span class="lesson-type {{ lesson.lesson_type }}">{{ lesson.lesson_type|title }}</span>
{% if lesson.duration_seconds and lesson.duration_seconds >= 60 %}
<span class="lesson-duration">{{ lesson.duration_seconds|format_duration }}</span>
{% endif %}
{% if lesson.completed %}
<span class="status-icon completed">{{ icons.icon('check', 14) }}</span>
{% elif percent_watched %}