mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
ac2ad8aac2
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.
122 lines
4.0 KiB
Markdown
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)
|