feat(cache): TMDB image proxy, posters, cache admin API and settings cards

GET /img/tmdb/{size}/{file} ported from MP ImageHelper / fetch_image
(sanitized cache path under DATA_DIR/cache/images, GLOBAL_IMAGE_CACHE
read switch, md5 ETag / 304, day-based cleanup job). Cache module adds
cache.sweep / cache.images_clean jobs and /cache/tmdb, /cache/images
list and clear endpoints. Library list/detail and the search palette
show posters with the neutral placeholder as fallback; metadata settings
get TMDB cache and image cache cards. Runbook docs/runbook/cache.md and
porting notes docs/porting/moviepilot-tmdb-cache.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 22:26:29 +08:00
1 parent 946fca5e55
commit 7862573944
16 files changed
+1028 -9

No files matched your search

@@ -150,6 +150,7 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
where (s.media_entity_id = e.id or sr.media_entity_id = e.id) and dt.removed_at is null
and dt.status in ('queued','submitting','downloading','stalled','paused'))::int`.as("active_downloads"),
)
.select(sql<string | null>`e.metadata #>> '{posterPath,value}'`.as("poster_path"))
.select(
sql<Date | null>`(select max(r.finished_at) from organization_record r where r.media_entity_id = e.id and r.status in ('succeeded','partial'))`.as(
"last_org",
@@ -220,6 +221,7 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
slotCounts: { ...emptyCounts(), ...(r.slot_counts ?? {}) },
activeDownloads: r.active_downloads,
lastOrganizedAt: r.last_org ? new Date(r.last_org).toISOString() : null,
posterPath: r.poster_path ?? null,
}));
return { items, nextCursor: p.nextCursor };
},