Add auto-thumbnails, library-wide remaining time, backup restore

Auto-generated course thumbnails: find_course_thumbnail() falls back to
grabbing a frame from a course's first video via ffmpeg when there's no
manual cover image, cached to .offlineu_thumbnail.jpg so it only ever
runs once. Folded into the "Precompute" prewarm button, now "Precompute
Lengths & Cover Art".

Library-wide "Remaining" stat: sums every course's already-cached
duration (no ffprobe, just a JSON read) into the dashboard's Library
Stats card, alongside the existing "Watched" figure.

Backup restore: new /api/backup/restore endpoint and a "Choose Backup
File..." control in Settings, with zip-slip and missing-course guards.
Also fixed a gap in the export itself - next_up.json and
outline_config.json weren't being backed up before, so restore wouldn't
have been a true round trip.

Move Surprise Me from a full-width button above the course list to a
dice-icon button in the dashboard header, swapping with the course-name
badge depending on whether a course is loaded.

Update README to cover all of the above.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 22:50:48 -04:00
co-authored by Claude Sonnet 5
parent 01a965c0f1
commit acabb89981
5 changed files with 261 additions and 49 deletions
+1 -1
View File
@@ -163,7 +163,7 @@
<li><strong>Browse your library</strong> from the main page, or enter a path manually in Settings</li>
<li><strong>Click a course</strong> to load it - folders drill down, courses open directly</li>
<li><strong>Start learning!</strong> Progress, notes, and completion are all saved automatically</li>
<li>Not sure what to watch? <strong>{{ icons.icon('dice', 14) }} Surprise Me</strong> at the top of the dashboard picks a random course you haven't finished</li>
<li>Not sure what to watch? The dice icon {{ icons.icon('dice', 14) }} in the top-right of the dashboard header picks a random course you haven't finished</li>
</ul>
</div>