diff --git a/CLAUDE.md b/CLAUDE.md index f745501..1153135 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,12 +57,24 @@ JSON shapes mirror the Tauri Rust bridge exactly — don't change one side alone - Now-playing tiles are `[svg.ti][span.tl]`; change state text with `setTile(btn, label)`, never `textContent`. ## Saved searches + caching notes -- `SearchLibrary` (app.js, localStorage `ytpSearchLibrary`, ≤150 queries × 50 results incl. thumbnail URLs) keeps - every search. The search box's recent list and its "Clear this list" only touch `ytpRecentSearches`; results are - deleted ONLY in Settings → Saved searches (select + delete, or clear all). Thumbnails are warmed into the SW - `ytplayer-thumbs` cache (`THUMB_CACHE_MAX` 2500). Browse view: `{type:'savedSearches'}`. -- `/api/search`: 10 min fresh cache, then stale-while-revalidate up to 6 h (answers instantly, refreshes in the - background); identical concurrent searches share one fetch (`fetchSearch`). `/fonts` and `/icons` are cached 30 days. +- `SearchLibrary` (app.js) keeps every search: metadata index in localStorage `ytpSearchIndex` (sync, ~250 KB), + result lists in IndexedDB `ytpSearchLibrary` (≤1000 queries × 100 results incl. thumbnail URLs; `get()` is async). + A saved copy is trusted 15 days (`FRESH_MS`): a fresh one is shown with NO network call, an older one is shown then + refreshed. The "Update search" button (`#searchMeta`) calls `API.search(q,{refresh:true})` → `/api/search?refresh=1` + which skips every server cache. The search box's recent list and its "Clear this list" only touch + `ytpRecentSearches`; results are deleted ONLY in Settings → Saved searches. Thumbnails are warmed into the SW + `ytplayer-thumbs` cache (`THUMB_CACHE_MAX` 25000, trimmed every 100th put — `cache.keys()` is costly at that size). + Browse view: `{type:'savedSearches'}`. Web Workers were NOT added: IndexedDB/fetch are already async off-thread I/O and + postMessage would clone the same data; DOM rendering can't move to a worker. +- Server `/api/search` (`server/search-cache.js`, tables `search_cache` + `video_meta`): memory (200, 10 min fresh) → + libsql (up to 200 YouTube cards per query via `innertube.searchDeep` continuation paging; ≤250,000 queries AND + `SEARCH_CACHE_MAX_BYTES` 2 GiB, LRU) → YouTube. Stale copies are served for 15 days while refreshing in the background. + Every card seen is also stored in `video_meta` (≤500k); `GET /api/search/local?q=` matches known videos by title/channel + (all words) + uploads in ms, and the app paints them while the real search loads. yt-dlp fallback still returns 25. +- Static files: index.html is stamped with `?v=` on local css/js (`indexHtml()`); a request whose `v` equals the + running build is `immutable, max-age=1y`, anything else `no-cache`. The SW `cacheFirst` matches `?v=` URLs with + `ignoreSearch` against the plain-URL precache. A new build = new URLs, so old ones simply get evicted. + `/fonts` and `/icons` are cached 30 days. ## Server media cache (`server/media-cache.js`) - Every played (`/api/streams`, LOW priority, ≤ `MEDIA_AUTO_MAX_SECONDS`, default 3 h) or saved