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

4.0 KiB

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:

{
  "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:

volumes:
  - ./cache/library:/app/cache/library

Ensures cached covers persist across container restarts.

  • 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)