diff --git a/CHANGELOG.md b/CHANGELOG.md index b6c395c..45425ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +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 2026-08-24 19:00 UTC — Fix video resize handle drifting off-screen diff --git a/VERSION b/VERSION index 64f37da..541a536 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026-08-25 00:20 UTC — autoplay, progress rings, duplicate lessons, title-card thumbnails +2026-08-25 00:26 UTC — fix race condition in thumbnail candidate generation diff --git a/offlineu_core.py b/offlineu_core.py index d4b5fa3..9ba7704 100644 --- a/offlineu_core.py +++ b/offlineu_core.py @@ -952,9 +952,14 @@ def _generate_course_thumbnail(course_dir: Path) -> Optional[str]: best_candidate = None best_size = -1 candidate_paths = [] + # Unique per call (not just per offset) so two concurrent requests for + # the same course - e.g. the Precompute background scan racing an + # ordinary page load that also misses the cache - never write or clean + # up each other's candidate files out from under one another. + run_id = uuid.uuid4().hex[:8] try: for i, offset in enumerate(candidate_offsets): - candidate_path = course_dir / f'.offlineu_thumbnail_candidate_{i}.jpg' + candidate_path = course_dir / f'.offlineu_thumbnail_candidate_{run_id}_{i}.jpg' candidate_paths.append(candidate_path) try: result = subprocess.run(