Files
ReadMeABook/documentation/features/library-thumbnail-cache.md
T
kikootwo ac2ad8aac2 Add BookDate card stack animations and thumbnail caching
Implements pure CSS card stack animations for BookDate recommendations, including smooth exit and advance transitions. Adds local caching of library cover thumbnails during scans, updates database schema and API to serve cached covers, and enhances BookDate to support 'favorites' scope with a book picker modal. Updates admin settings validation logic for Prowlarr, improves indexer state management, and documents new features and backend changes.
2026-01-28 11:41:59 -05:00

122 lines
4.0 KiB
Markdown

# Library Thumbnail Caching
**Status:** ✅ Implemented | Cache library covers during scans, serve in BookDate
## Overview
Caches book covers from Plex/Audiobookshelf during library scans. Stores cached files in `/app/cache/library/` with SHA-256 hashed filenames. Dramatically improves BookDate user experience by showing real covers instead of placeholders.
## Key Details
### Caching Strategy
- **When:** During full scans (scan-plex.processor.ts) and recently-added scans (plex-recently-added.processor.ts)
- **Where:** `/app/cache/library/` directory
- **Filename:** SHA-256 hash (first 16 chars) of plexGuid + extension (e.g., `a3f5e9d2c1b4.jpg`)
- **Smart caching:** Checks if file exists before downloading (subsequent scans are fast)
### Database Schema
- **Field:** `PlexLibrary.cachedLibraryCoverPath` (nullable TEXT)
- **Stores:** Full path like `/app/cache/library/{hash}.jpg`
- **Migration:** `20260120000000_add_cached_library_cover_path`
### URL Construction (Backend-Specific)
- **Plex:** `{serverUrl}{thumbUrl}?X-Plex-Token={token}`
- **Audiobookshelf:** `{serverUrl}{coverPath}` with `Authorization: Bearer {token}` header
### Cover Priority (BookDate Library Picker)
1. **Library cached cover** (`cachedLibraryCoverPath`) → `/api/cache/library/{filename}`
2. **Audible cache** (if book has ASIN) → from `AudibleCache.coverArtUrl`
3. **Null** (show placeholder 📚)
## API Endpoints
### GET /api/cache/library/[filename]
Serves cached library covers (24-hour browser cache).
**Path validation:** Prevents directory traversal (rejects `..` and `/`).
**Content types:** jpg, jpeg, png, gif, webp → image/*, else application/octet-stream
### GET /api/bookdate/library
Returns library books with cover URLs.
**Response:**
```json
{
"books": [
{
"id": "uuid",
"title": "Book Title",
"author": "Author Name",
"coverUrl": "/api/cache/library/a3f5e9d2c1b4.jpg" // or Audible URL, or null
}
]
}
```
## Service Layer
### ThumbnailCacheService
Located: `src/lib/services/thumbnail-cache.service.ts`
**Methods:**
- `cacheLibraryThumbnail(plexGuid, coverUrl, backendBaseUrl, authToken, backendMode)` → Returns cached path or null
- `cleanupLibraryThumbnails(plexGuidToHashMap)` → Returns deleted count
**Safeguards:**
- 10s timeout per download
- 5MB max file size
- Content-type validation (must be image/*)
- Graceful degradation (logs warning, returns null on failure)
### Library Services
Located: `src/lib/services/library/`
**Both PlexLibraryService and AudiobookshelfLibraryService provide:**
- `getCoverCachingParams()` → Returns `{ backendBaseUrl, authToken, backendMode }`
## Performance
### First Full Scan (1000 books)
- Database: ~30 seconds
- Downloads: ~1-5 minutes (network-dependent)
- **Total: ~1.5-5.5 minutes** (one-time cost)
### Subsequent Scans (1000 books)
- Database: ~30 seconds
- Downloads: **~0 seconds** (skipped, files exist)
- **Total: ~30 seconds** (same as before caching)
### BookDate Library Load
- **Before:** Mostly placeholder covers
- **After:** Real covers for all books with valid thumbUrl
- **Performance:** No change (local file serving is fast)
## Error Handling
- Download fails → log warning, store null, continue scan
- Invalid content-type → reject, store null
- File system errors → log, store null
- Missing backend config → throw (scan fails early with clear error)
## Cleanup (Future Enhancement)
**Manual or Scheduled:**
- Builds hash-to-plexGuid reverse map from database
- Deletes cached files for plexGuids no longer in library
- Returns count of deleted files
**Trigger:** Admin endpoint or weekly scheduled job
## Docker Configuration
**Volume mount required:**
```yaml
volumes:
- ./cache/library:/app/cache/library
```
Ensures cached covers persist across container restarts.
## Related
- documentation/backend/database.md (PlexLibrary schema)
- documentation/features/bookdate.md (cover loading logic)
- documentation/integrations/audible.md (Audible thumbnail caching pattern)
- documentation/backend/services/jobs.md (scan processors)