import { z } from "zod"; import { NonNegInt, Timestamp } from "./common.ts"; // C2-A 缓存管理(docs/runbook/cache.md):TMDB 缓存(请求响应 + 识别结果,对照 MP TmdbCache)、图片代理缓存。 export const TmdbCacheKind = z.enum(["request", "recognize"]); export type TmdbCacheKind = z.infer; /** 一条 TMDB 缓存(对照 MP list_items:识别条目带 tmdbId / 标题 / 年份 / 类型 / 海报) */ export const TmdbCacheItem = z.object({ kind: TmdbCacheKind, key: z.string(), expiresAt: Timestamp, updatedAt: Timestamp, /** jsonb 存储字节数 */ bytes: NonNegInt, /** 识别条目:TMDB id,0 = 负缓存(未识别) */ tmdbId: z.number().int().nullable(), title: z.string().nullable(), year: z.string().nullable(), mediaType: z.enum(["movie", "tv", "unknown"]).nullable(), posterPath: z.string().nullable(), }); export type TmdbCacheItem = z.infer; export const TmdbCacheQuery = z.object({ kind: TmdbCacheKind.optional(), limit: z.coerce.number().int().min(1).max(5000).default(500), }); export const TmdbCacheList = z.object({ items: z.array(TmdbCacheItem), counts: z.object({ request: NonNegInt, recognize: NonNegInt }), /** 每类条数上限(cache.tmdbMaxSize) */ maxSize: NonNegInt, ttlHours: NonNegInt, emptyResultTtlSec: NonNegInt, }); export type TmdbCacheList = z.infer; export const CacheClearResult = z.object({ deleted: NonNegInt }); export type CacheClearResult = z.infer; /** 图片代理缓存用量 */ export const ImageCacheStatus = z.object({ /** cache.globalImageCache */ enabled: z.boolean(), /** cache.globalImageCacheDays */ days: NonNegInt, usedBytes: NonNegInt, files: NonNegInt, dir: z.string(), }); export type ImageCacheStatus = z.infer; /** TMDB 图片尺寸白名单(与 TMDB configuration.images 一致) */ export const TMDB_IMAGE_SIZES = [ "w92", "w154", "w185", "w300", "w342", "w500", "w780", "w1280", "h632", "original", ] as const; export type TmdbImageSize = (typeof TMDB_IMAGE_SIZES)[number]; /** 海报 / 背景图路径 → 图片代理 URL(相对 API_PREFIX 之外的完整路径,前端直接用作 img src) */ export function tmdbImageUrl(path: string | null | undefined, size: TmdbImageSize = "w342"): string | null { if (!path) return null; const p = path.startsWith("/") ? path.slice(1) : path; if (!/^[A-Za-z0-9_-]+\.(jpg|jpeg|png|webp)$/.test(p)) return null; return `/api/v1/img/tmdb/${size}/${p}`; }