Commit Graph
23 Commits
Author SHA1 Message Date
rmsitzandClaude Sonnet 5 9e7dc85558 Add Resume button, Surprise Me random pick, and bulk select
Three quality-of-life additions to the Library browser and course
pages:

- A course page shows a "Resume: <lesson>" button when there's a
  last-visited lesson, using Course.last_accessed_path (already
  populated by apply_progress_to_tree, just not surfaced before) -
  no more scrolling the tree to find where you left off.

- "Surprise Me" picks a random not-yet-fully-watched course and loads
  it, for when there's too much library to decide what to watch.
  Reuses the already-cached get_all_course_dirs() and the same
  per-course completed/total shape _scan_library_activity() already
  computes for Stale Courses - no new scanning.

- A select-mode toggle in the Library browser adds checkboxes to every
  row/card (list and grid) with a bulk-action bar to hide or queue
  several courses/folders at once, via two new endpoints
  (/api/hidden-paths/bulk, /api/next-up/bulk) that reuse the existing
  single-item set_path_hidden()/set_path_queued() in a loop. Selection
  is scoped to the current folder view and clears on navigation.

Also fixes runTranscriptSearch() going silently blank on zero results
instead of showing a "no matches" message, and updates the Help page
to cover all of this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 15:06:29 -04:00
rmsitzandClaude Sonnet 5 2941910c54 Cache library filesystem scans to fix ~90s NAS page loads
There was no caching anywhere in the app, so every request re-walked
the SMB-mounted library from scratch - the dashboard alone triggered
2 duplicate full-library directory walks, 100+ individual
.offlineu_progress.json opens, and ~10-15 full per-course recursive
rglob() scans, none of it shared between requests. Measured ~90s per
dashboard load against the real mounted NAS, with a second immediate
reload taking just as long - proof nothing was being reused.

Add a minimal in-process TTL cache (5 min) and apply it at the actual
hot spots: the shared course-directory listing (get_all_course_dirs,
replacing 6 independent iter_all_courses() walks), the per-course
media-count/thumbnail summary (_course_summary, also now reused by
list_library_directory instead of a third duplicate implementation),
the per-course tree scan (get_course_tree - safe to cache since
progress is always re-applied fresh on top, never baked into the
cached structure), and the transcript-search subtitle index (the
worst offender - previously re-read and lowercased every subtitle
file in the library on every search).

Invalidates immediately on hide/show, rename, and library-path
changes; a "Refresh Library" button on Settings covers files added
directly on the NAS outside the app.

Measured after the fix, same real NAS mount: dashboard ~90s -> 1.3s
warm, Notes Hub ~1.15s warm, Library browser ~3.6s warm.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 14:27:24 -04:00
rmsitzandClaude Sonnet 5 bc6725ee4c Add section lesson list, section progress, and library grid view
Three comparison-driven upgrades against typical course/media platforms:

- Lesson page gets a collapsible "Lessons in this section" list so you
  can jump between siblings without leaving the page, instead of only
  global Prev/Next buttons. find_lesson_in_tree() now also returns the
  DirectoryNode that owns the lesson (its .lessons list is the sibling
  set) since DirectoryNode has no parent pointer; a new
  get_section_lessons() resolves each sibling's progress the same way
  view_lesson() already does for the current lesson.

- Course tree section headers show "X/Y watched" instead of a flat item
  count, via DynamicCourseParser._calculate_completion_stats (already
  computed recursive completion for any node, just never called
  per-section) exposed as a Jinja global.

- Library browser gets a list/grid toggle for browsing large category
  folders by poster-style thumbnail instead of a 32px-icon list.
  Client-side only, persisted via localStorage, defaults to the
  existing list view; both folder browsing and search results already
  funnel through the same render function so grid mode covers both.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 14:04:14 -04:00
rmsitzandClaude Sonnet 5 3fedf849d3 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>
2026-08-22 22:00:35 -04:00
rmsitzandClaude Sonnet 5 68639a2b5b Fix chapter-name regex missing underscore-joined folder names
_SECTION_NAME_RE used \b after keywords like "chapter", but \b doesn't
fire between "Chapter" and an immediately-following "_" since
underscore counts as a word character too - so a course whose chapter
folders were named "Chapter_1-Introduction" (no space) failed to match
and got misclassified as a plain folder instead of a course.

Swap \b for a (?![a-z]) negative lookahead, which correctly rejects
only a following letter (e.g. "Chapterhouse") while accepting a digit,
underscore, hyphen, space, or end of string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 20:41:11 -04:00
rmsitzandClaude Sonnet 5 21a468fb7f Fix course-detection heuristic swallowing category folders as one course
_looks_like_course() treated any folder with 2+ media-holding subfolders
as a single multi-section course, with no check that those subfolders
actually looked like sections. A category folder holding many unrelated
courses (e.g. Claude/, General/) could accidentally satisfy that if
several of its courses happened to keep episodes directly at their own
top level rather than nested under a chapter folder, collapsing the
whole category into one bogus course.

Now every media-holding subfolder must read as a chapter/section label
(Section 1, Module 2, or a numbered chapter like 01-Introduction) for
the parent to count as one course - verified against the real library
mount that ChatGPT/Claude/Copilot/General all correctly show as
folders again, while individually-structured courses still detect
correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 20:33:02 -04:00
rmsitzandClaude Sonnet 5 8b365c4463 Add timestamped notes: multi-note support, quick-capture hotkey, live saved indicator
Replace the single free-text note per lesson with a list of timestamped
note entries, migrated transparently from the old format. Adds a
floating notes panel on the lesson page with an always-reachable
quick-capture bar (press N to pause and jump in), inline edit/delete,
click-to-seek timestamps, and a live "Saved Xs ago" indicator. Notes
Hub, the study guide export, and the Outline push all updated to
render multiple timestamped notes per lesson.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 20:17:20 -04:00
rmsitzandClaude Sonnet 5 0454834e62 Add Notes Hub, transcript search, stale nudges, study guide export, Next Up queue, activity heatmap
Six more dashboard features, all built on the per-course progress-file
scanning infrastructure from earlier sessions:

- Notes Hub (/notes): every lesson note across the library in one place,
  newest first, reusing the existing /recent/open cross-course jump route.
- Transcript search: opt-in checkbox on the Library search box, searching
  inside .srt/.vtt files. Kept cheap by doing a raw substring match as the
  filter step and only extracting a snippet for files that actually match.
- Stale course nudges ("Pick Back Up"): courses with progress that haven't
  been touched in 14+ days.
- Study guide export: compiles a course's notes into one downloadable
  markdown file, section/lesson structure preserved.
- Next Up queue: an ordered, persisted "what to tackle next" list with
  up/down reordering and a queue button on every course row.
- Activity heatmap: a 90-day contribution-style calendar in the Library
  Stats card.

_scan_library_activity() now does one walk over every course's progress
file per dashboard load; library stats, stale courses, and the heatmap all
derive from that single scan instead of three independent ones.

Also refactored the duplicated lesson-progress-key lookup (used in three
places now) into _resolve_lesson_progress_key(), and flagged a pre-existing
bug found along the way (not fixed here, kept out of scope): subtitle files
never actually attach to a Lesson object, so the video player's caption
track has never had anything to render.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 19:04:01 -04:00
rmsitzandClaude Sonnet 5 145f91a647 Fix note-field space bar hijacking video, rework Outline topics as documents
- Lesson page's global keyboard shortcuts (space/arrows for play-pause/
  seek/volume) fired regardless of focus, so typing a space in the notes
  textarea toggled the video instead of typing. Now skipped entirely
  whenever a text field/select has focus.
- Outline topics no longer spawn their own top-level collection each time.
  A topic is now a document inside one fixed, user-configured collection
  (Settings -> Outline Integration -> Default collection), and a lesson's
  note becomes a child document nested under its topic - matching how
  notes actually get organized in a real Outline instance instead of
  cluttering the collections list with one per topic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 12:27:00 -04:00
rmsitzandClaude Sonnet 5 5fe3fafc7a Add Outline integration: push lesson notes to a topic collection
Pick a topic on a lesson's note and it pushes to that topic's collection
in a self-hosted Outline instance when you leave the page; notes without a
topic stay local-only. Settings gets a new Outline Integration card
(URL/token, Save, Test Connection).

Credential handling: the API token lives in its own outline_config.json
in DATA_DIR, deliberately kept out of the general settings flow (GET
/api/settings is fetched on every page load by theme.js - no place for a
secret to ride along). GET /api/outline/config only ever returns whether
it's configured, never the token itself.

The topic chooser reflects Outline's live collection list rather than a
locally cached copy, and resolves a newly-typed topic name to a real
collection immediately (find-by-name-or-create) rather than waiting until
the note is pushed. That's not just an optimization: the push itself fires
via navigator.sendBeacon() on pagehide, which can't read a response, so a
page that fires pagehide more than once for the same load (a back/forward-
cache restore, for instance) would otherwise re-send the same "create a
new topic" intent every time and spawn duplicate collections. Verified
live against a local mock Outline server that firing pagehide repeatedly
for the same lesson creates the collection/document once and updates
thereafter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 12:12:50 -04:00
rmsitzandClaude Sonnet 5 a6c49d3762 Add Recently Added, bulk rename, time remaining, library stats, backup export
Five more usability features on top of the search/thumbnails batch:

- Recently Added: dashboard card of courses by folder mtime, separate from
  Recently Viewed (watched vs. just showed up on disk).
- Bulk find/replace rename across every course/folder name at once, with a
  mandatory preview step before anything touches disk. Refactored the
  single-item rename route to share the same validate/apply logic.
- Estimated time remaining on the loaded course's stats card, computed only
  from lessons that have actually reported a duration.
- Library-wide stats overview: total courses, lessons tracked, time
  watched, daily streak - read from each course's small progress file
  rather than re-scanning course contents.
- One-click backup/export zip of settings, hidden-paths, recently-viewed,
  and every course's progress/notes.

Also fixes a real performance bug found along the way: search was routing
through list_library_directory, which computes a full recursive file count
and thumbnail lookup for every course at every level regardless of match -
turning a search into an O(every file in the library) scan. Gave search its
own lightweight directory-only walk (iter_all_courses), now shared by
Recently Added and the stats overview too, so the expensive per-course work
only runs for courses that actually match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 11:36:05 -04:00
rmsitzandClaude Sonnet 5 dbf9528fea Speed up library search on large libraries
search_library_courses was walking via list_library_directory, which
computes a full recursive file count (rglob) and thumbnail lookup for
every course at every level - turning a search into an O(every file in
every course) scan regardless of how few results actually match. Give
search its own lightweight directory-only walk and defer the expensive
per-course lookups until after a name match is confirmed, so cost now
scales with matches found rather than total library size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 09:22:54 -04:00
rmsitzandClaude Sonnet 5 38963b1d55 Add search, thumbnails, continue watching, playback speed, PWA install, sort, notes, and mark-watched
Eight usability additions on top of the mobile redesign: course cover-image
thumbnails, a debounced library search, a Continue Watching section split
from Recently Viewed, per-lesson playback speed control, an installable
PWA manifest/icons, name sort in the library browser, per-lesson notes, and
a bulk mark-course-as-watched action. Also fixes two real bugs found along
the way: lesson_view.html was missing the viewport meta tag (so lesson
pages weren't actually mobile-responsive), and update_lesson_progress
always overwrote the whole progress entry, which would have silently
deleted a saved note on the next routine autosave.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 09:06:44 -04:00
rmsitz c92268e04c New mobile layout, new regular screen, added the ability to edit directory names from settings 2026-08-22 08:37:25 -04:00
rmsitz fa00a0b0c4 Fixed themes, empty folder showing and single section course detection 2026-08-21 09:49:44 -04:00
rmsitz 0e9fff65b0 Adding ability to hide / show courses 2026-08-20 20:47:38 -04:00
rmsitz b06b8cf94c Added additional themes 2026-08-20 20:09:58 -04:00
rmsitz 699ca7d6df Added some functionality so we can modify the size of the playing video 2026-08-20 19:50:20 -04:00
rmsitz 5b8bef1451 Added a back to main clicking on the link, added help page, added a last 5 viewed 2026-08-20 18:57:27 -04:00
rmsitz 66dff7e69f New main page way of browsing the courses 2026-08-20 18:48:17 -04:00
rmsitz a604569a45 Adding a new settings page and other tweaks for how it looks 2026-08-20 17:04:21 -04:00
rmsitz fc62d77131 PDF inline fix 1 2026-08-20 16:41:20 -04:00
rmsitz 6f0fc6cb60 Add library browser feature 2026-08-20 13:47:51 -04:00