Stop resetting lesson progress on view, and add Notes Hub search
view_lesson() called update_lesson_progress() with no arguments on every page load, which defaults completed=False and progress_seconds=0 and silently overwrote whatever was already saved - just opening an already-watched lesson reset its progress/completed state unless the client happened to report real values within the next 15 seconds. Replaced with touch_lesson_accessed(), which only bumps last_accessed. Also fixes the "Resume from Xs" feature this fed into: it read a value that was never populated on this route, and even when given a real one, checked activeMedia.duration synchronously before metadata had loaded, so it silently never applied. Now waits for loadedmetadata (or resolves immediately if already available) and defers to an explicit note-timestamp jump (?t=) when both are present. Notes Hub gets a live search box filtering by note text, lesson title, or course name (client-side, no reload), with a "no notes match" state - useful now that a single category folder can hold dozens of courses' worth of timestamped notes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+24
-5
@@ -1446,6 +1446,23 @@ class ProgressTracker:
|
||||
|
||||
ProgressTracker.save_progress(course, progress)
|
||||
|
||||
@staticmethod
|
||||
def touch_lesson_accessed(course: Course, lesson_path: str):
|
||||
"""
|
||||
Record that a lesson was opened, without touching its saved
|
||||
completed/progress_seconds/duration_seconds - update_lesson_progress
|
||||
is for the client reporting real playback progress, and calling it
|
||||
(with its completed=False, progress_seconds=0 defaults) just for
|
||||
viewing a page would silently reset an already-watched lesson back
|
||||
to 0% every time it's opened, before the client gets a chance to
|
||||
report anything real.
|
||||
"""
|
||||
progress = ProgressTracker.load_progress(course)
|
||||
entry = progress.setdefault(lesson_path, {})
|
||||
entry['last_accessed'] = datetime.now().isoformat()
|
||||
progress['last_accessed_path'] = lesson_path
|
||||
ProgressTracker.save_progress(course, progress)
|
||||
|
||||
@staticmethod
|
||||
def _notes_from_entry(entry: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
@@ -2340,15 +2357,16 @@ def view_lesson(lesson_path: str):
|
||||
if current_index < len(all_lessons) - 1:
|
||||
next_lesson = all_lessons[current_index + 1][0]
|
||||
|
||||
# Update last accessed
|
||||
ProgressTracker.update_lesson_progress(current_course, lesson_path)
|
||||
# Update last accessed without touching saved progress/completed state
|
||||
ProgressTracker.touch_lesson_accessed(current_course, lesson_path)
|
||||
|
||||
# Record for the cross-course "Recently Viewed" list on the dashboard
|
||||
record_recent_view(current_course.name, current_course.path, lesson_path, lesson.title)
|
||||
|
||||
# Read notes directly from the progress file rather than the Lesson
|
||||
# object - apply_progress_to_tree (which populates Lesson fields) isn't
|
||||
# called on this code path, only on the dashboard's tree render.
|
||||
# Read notes and progress directly from the progress file rather than
|
||||
# the Lesson object - apply_progress_to_tree (which populates Lesson
|
||||
# fields) isn't called on this code path, only on the dashboard's tree
|
||||
# render.
|
||||
lesson_progress = ProgressTracker.load_progress(current_course).get(lesson_path, {})
|
||||
|
||||
seek_seconds = request.args.get('t', type=int)
|
||||
@@ -2358,6 +2376,7 @@ def view_lesson(lesson_path: str):
|
||||
lesson=lesson,
|
||||
lesson_path=lesson_path,
|
||||
lesson_notes=ProgressTracker._notes_from_entry(lesson_progress),
|
||||
lesson_progress_seconds=lesson_progress.get('progress_seconds', 0),
|
||||
initial_seek_seconds=seek_seconds,
|
||||
outline_topic_id=lesson_progress.get('outline_topic_id', ''),
|
||||
outline_topic_name=lesson_progress.get('outline_topic_name', ''),
|
||||
|
||||
Reference in New Issue
Block a user