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>
This commit is contained in:
@@ -432,6 +432,11 @@
|
||||
<button class="btn" onclick="saveLibraryPath()">Save</button>
|
||||
</div>
|
||||
<span id="library-path-status" style="font-size: 0.85em; min-height: 1.2em;"></span>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<button class="btn btn-secondary btn-sm" onclick="refreshLibraryCache()">🔄 Refresh Library</button>
|
||||
<span id="library-refresh-status" style="font-size: 0.85em; color: var(--text-muted);"></span>
|
||||
</div>
|
||||
<span class="setting-desc">Courses are cached briefly for speed - use this after adding or removing files directly on disk if you don't want to wait a few minutes for it to notice.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -951,6 +956,23 @@
|
||||
});
|
||||
}
|
||||
|
||||
function refreshLibraryCache() {
|
||||
const status = document.getElementById('library-refresh-status');
|
||||
status.style.color = 'var(--text-muted)';
|
||||
status.textContent = 'Refreshing…';
|
||||
fetch('/api/library/refresh', { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
status.style.color = data.success ? 'var(--success)' : 'var(--error)';
|
||||
status.textContent = data.success ? '✓ Refreshed' : 'Could not refresh';
|
||||
if (data.success) setTimeout(() => { status.textContent = ''; }, 3000);
|
||||
})
|
||||
.catch(() => {
|
||||
status.style.color = 'var(--error)';
|
||||
status.textContent = 'Could not reach the server';
|
||||
});
|
||||
}
|
||||
|
||||
function loadCourseFromPath() {
|
||||
const input = document.getElementById('manual-course-path');
|
||||
const status = document.getElementById('manual-course-status');
|
||||
|
||||
Reference in New Issue
Block a user