Add progress/recently-added sort to Library browser

The sort dropdown only had Name (A-Z/Z-A) - no way to surface "what
did I just download" or "what am I closest to finishing" without
scrolling. Adds Progress and Recently Added, backed by each course's
completion_percentage and folder mtime (already/newly attached in
list_library_directory and search_library_courses). Folders have
neither, so they naturally sort to the end under these two modes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 10:58:01 -04:00
co-authored by Claude Sonnet 5
parent bf0a02d008
commit 47e7214bd7
5 changed files with 27 additions and 5 deletions
+1
View File
@@ -1,3 +1,4 @@
2026-08-26 14:57 UTC — Add progress/recently-added sort to Library browser
2026-08-26 14:45 UTC — Redesign expandable lists to a leaner, flat style 2026-08-26 14:45 UTC — Redesign expandable lists to a leaner, flat style
2026-08-26 14:27 UTC — Add expand all/collapse all to the course tree 2026-08-26 14:27 UTC — Add expand all/collapse all to the course tree
2026-08-26 14:13 UTC — Header/footer polish, fix dead link, add focus rings 2026-08-26 14:13 UTC — Header/footer polish, fix dead link, add focus rings
+2 -1
View File
@@ -96,7 +96,8 @@ silently stay blank instead of erroring.
**Library browsing** **Library browsing**
- Lazy-loading folder browser (grid or list view) with search, including an - Lazy-loading folder browser (grid or list view) with search, including an
opt-in transcript search across subtitle files opt-in transcript search across subtitle files. Sortable by name,
progress, or how recently a course showed up in the library
- Course cards show media file count, total video/audio runtime, and - Course cards show media file count, total video/audio runtime, and
completion % at a glance completion % at a glance
- Cover art: uses a manually-placed `cover`/`folder`/`thumbnail`/`thumb`/ - Cover art: uses a manually-placed `cover`/`folder`/`thumbnail`/`thumb`/
+1 -1
View File
@@ -1 +1 @@
2026-08-26 14:45 UTC — redesign expandable lists to a leaner, flat style 2026-08-26 14:57 UTC — add progress/recently-added sort to Library browser
+8
View File
@@ -1288,6 +1288,10 @@ def list_library_directory(dir_path: str, skip_hidden: bool = True) -> Dict[str,
item['hidden'] = is_hidden item['hidden'] = is_hidden
item['favorited'] = os.path.abspath(entry_path_str) in favorite_set item['favorited'] = os.path.abspath(entry_path_str) in favorite_set
item['completion_percentage'] = _course_completion_percentage(entry, item['media_files']) item['completion_percentage'] = _course_completion_percentage(entry, item['media_files'])
try:
item['mtime'] = entry.stat().st_mtime
except OSError:
item['mtime'] = 0
items.append(item) items.append(item)
else: else:
if skip_hidden: if skip_hidden:
@@ -2078,6 +2082,10 @@ def search_library_courses(query: str) -> List[Dict[str, Any]]:
item = _course_summary(course) item = _course_summary(course)
item['favorited'] = os.path.abspath(str(course)) in favorite_set item['favorited'] = os.path.abspath(str(course)) in favorite_set
item['completion_percentage'] = _course_completion_percentage(course, item['media_files']) item['completion_percentage'] = _course_completion_percentage(course, item['media_files'])
try:
item['mtime'] = course.stat().st_mtime
except OSError:
item['mtime'] = 0
results.append(item) results.append(item)
return results return results
+15 -3
View File
@@ -1243,6 +1243,8 @@
<select id="library-sort" class="library-sort-select" onchange="reapplySort()"> <select id="library-sort" class="library-sort-select" onchange="reapplySort()">
<option value="name-asc">Name (A→Z)</option> <option value="name-asc">Name (A→Z)</option>
<option value="name-desc">Name (Z→A)</option> <option value="name-desc">Name (Z→A)</option>
<option value="progress">Progress</option>
<option value="recently-added">Recently Added</option>
</select> </select>
<div class="library-view-toggle"> <div class="library-view-toggle">
<button type="button" class="view-toggle-btn" id="library-view-list-btn" <button type="button" class="view-toggle-btn" id="library-view-list-btn"
@@ -1889,9 +1891,19 @@
function sortLibraryItems(items) { function sortLibraryItems(items) {
const order = document.getElementById('library-sort').value; const order = document.getElementById('library-sort').value;
const sorted = items.slice(); const sorted = items.slice();
sorted.sort((a, b) => order === 'name-desc' // Directories have neither a completion_percentage nor an
? b.name.localeCompare(a.name) // mtime (only courses do), so they naturally fall to the end
: a.name.localeCompare(b.name)); // under these two modes rather than needing separate grouping
// logic - falling back to name order among themselves/ties.
if (order === 'progress') {
sorted.sort((a, b) => (b.completion_percentage || 0) - (a.completion_percentage || 0) || a.name.localeCompare(b.name));
} else if (order === 'recently-added') {
sorted.sort((a, b) => (b.mtime || 0) - (a.mtime || 0) || a.name.localeCompare(b.name));
} else {
sorted.sort((a, b) => order === 'name-desc'
? b.name.localeCompare(a.name)
: a.name.localeCompare(b.name));
}
return sorted; return sorted;
} }