From 8220bb542a92c77ac1d7b1f47a57ac3878a38902 Mon Sep 17 00:00:00 2001 From: rmsitz Date: Mon, 24 Aug 2026 20:33:37 -0400 Subject: [PATCH] Widen thumbnail sampling window past platform bumpers Many course platforms (Pluralsight, Packt, ...) open with a generic branded bumper animation running 10-20s before the actual course title card. The 8s candidate window landed squarely inside that bumper and grabbed the platform logo instead of anything course-specific. Candidates now spread across the first ~25s. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 1 + VERSION | 2 +- offlineu_core.py | 28 +++++++++++++++++----------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45425ff..6cf63ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +2026-08-25 00:33 UTC — Widen thumbnail sampling window past platform bumpers 2026-08-25 00:26 UTC — Fix race condition in thumbnail candidate generation 2026-08-25 00:20 UTC — Add autoplay, progress rings, duplicate-lesson detection, title-card thumbnails 2026-08-24 23:22 UTC — Add Favorites, storage drill-down, shortcuts, and What's New diff --git a/VERSION b/VERSION index 541a536..17c78b1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026-08-25 00:26 UTC — fix race condition in thumbnail candidate generation +2026-08-25 00:33 UTC — widen thumbnail sampling window past platform bumpers diff --git a/offlineu_core.py b/offlineu_core.py index 9ba7704..e610e3f 100644 --- a/offlineu_core.py +++ b/offlineu_core.py @@ -926,15 +926,16 @@ def _generate_course_thumbnail(course_dir: Path) -> Optional[str]: added later is still picked up on the next request past the short in-memory cache above. - Course intros typically show a title card (course/lesson name, maybe - a logo) for the first several seconds before cutting to the - presenter - a single frame at a fixed offset can easily land past - that cut and grab someone mid-sentence instead. Rather than guess one - offset, this samples a handful of candidates in the first ~8 seconds - and keeps the one with the largest resulting JPEG: a title card (text, - graphics, a logo) compresses to a noticeably bigger file than a blank - fade-in or a plain talking-head frame, which is a cheap enough proxy - for "has more going on" without any real image analysis. + Course intros typically run a branded bumper animation (10-20s on + some platforms) followed by a title card before cutting to the + presenter - a single frame at a fixed offset can easily land inside + the bumper or past the cut into someone mid-sentence instead. Rather + than guess one offset, this samples candidates spread across the + first ~25 seconds and keeps the one with the largest resulting JPEG: + a title card or logo (text, graphics) compresses to a noticeably + bigger file than a blank fade-in or a plain talking-head frame, which + is a cheap enough proxy for "has more going on" without any real + image analysis. """ video_files = sorted( f for f in course_dir.rglob('*') @@ -945,8 +946,13 @@ def _generate_course_thumbnail(course_dir: Path) -> Optional[str]: source = video_files[0] duration = _probe_media_duration_seconds(source) or 0 - max_offset = min(8.0, duration * 0.5) if duration else 6.0 - candidate_offsets = sorted({o for o in (1.0, 2.5, 4.0, 6.0) if o <= max_offset}) or [1.0] + # Many course platforms (Pluralsight, Packt, ...) open with a + # generic branded bumper animation running 10-20s before the actual + # course title card - an 8s window landed squarely inside that + # bumper and grabbed the platform logo instead. Spread candidates + # further in to reach past it. + max_offset = min(25.0, duration * 0.5) if duration else 15.0 + candidate_offsets = sorted({o for o in (2.0, 5.0, 8.0, 12.0, 16.0, 20.0, 25.0) if o <= max_offset}) or [2.0] output_path = course_dir / AUTO_THUMBNAIL_FILENAME best_candidate = None