refactor(cache): manage TMDB recognition cache only

This commit is contained in:
jxxghp
2026-08-06 07:48:43 +08:00
parent 0045ecf5d9
commit 6fd259427e
7 changed files with 38 additions and 41 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "moviepilot",
"version": "2.15.4",
"version": "2.15.5",
"private": true,
"type": "module",
"bin": "dist/service.js",
+1 -3
View File
@@ -1889,9 +1889,7 @@ export interface RecognitionCacheItem {
key: string
// TMDB ID0 表示未识别
tmdb_id?: number
// 豆瓣 ID0 表示未识别
douban_id?: string | number
// 当前识别数据源对应的统一 ID
// 识别缓存对应的字符串 ID
recognition_id?: string
// 识别后的标题
title: string
+11 -29
View File
@@ -11,9 +11,9 @@ import { getDisplayImageUrl } from '@/utils/imageUtils'
type RecognitionStatusFilter = 'all' | 'recognized' | 'unrecognized'
type InfiniteScrollStatus = 'ok' | 'empty' | 'loading' | 'error'
type RecognitionCacheSource = 'tmdb' | 'douban'
const MOBILE_CACHE_PAGE_SIZE = 20
const RECOGNITION_CACHE_ENDPOINT = 'tmdb/cache'
const { t } = useI18n()
const display = useDisplay()
@@ -22,18 +22,8 @@ const $toast = useToast()
const globalSettingsStore = useGlobalSettingsStore()
const isMobile = computed(() => display.smAndDown.value)
const recognitionSource = computed<RecognitionCacheSource>(() =>
globalSettingsStore.globalSettings.RECOGNIZE_SOURCE === 'douban' ? 'douban' : 'tmdb',
)
const recognitionSourceName = computed(() =>
recognitionSource.value === 'douban'
? t('setting.cache.recognitionSource.douban')
: t('setting.cache.recognitionSource.themoviedb'),
)
const recognitionIdLabel = computed(() =>
recognitionSource.value === 'douban' ? t('setting.cache.doubanId') : t('setting.cache.tmdbId'),
)
const recognitionCacheEndpoint = computed(() => `${recognitionSource.value}/cache`)
const recognitionSourceName = computed(() => t('setting.cache.recognitionSource.themoviedb'))
const recognitionIdLabel = computed(() => t('setting.cache.tmdbId'))
const recognitionFilterPlaceholder = computed(() =>
t('setting.cache.filterRecognitionCache', { source: recognitionSourceName.value }),
)
@@ -106,12 +96,12 @@ function loadMoreMobileCache({ done }: { done: (status: InfiniteScrollStatus) =>
done(mobileHasMore.value ? 'ok' : 'empty')
}
/** 加载当前识别数据源对应的缓存列表。 */
/** 加载 TMDB 主识别缓存列表。 */
async function loadCacheData(showSuccess = false) {
const requestId = ++cacheLoadRequestId
try {
loading.value = true
const response = (await api.get(recognitionCacheEndpoint.value)) as unknown as ApiResponse<RecognitionCacheData>
const response = (await api.get(RECOGNITION_CACHE_ENDPOINT)) as unknown as ApiResponse<RecognitionCacheData>
if (requestId !== cacheLoadRequestId) return
const responseData = response.data ?? {
count: 0,
@@ -139,7 +129,7 @@ async function loadCacheData(showSuccess = false) {
}
}
/** 清空当前识别数据源的全部识别缓存。 */
/** 清空全部 TMDB 主识别缓存。 */
async function clearAllCache() {
const confirmed = await createConfirm({
type: 'warn',
@@ -150,7 +140,7 @@ async function clearAllCache() {
try {
loading.value = true
const response = (await api.delete(recognitionCacheEndpoint.value)) as unknown as ApiResponse
const response = (await api.delete(RECOGNITION_CACHE_ENDPOINT)) as unknown as ApiResponse
if (!response.success) throw new Error(response.message)
$toast.success(response.message || t('setting.cache.clearSuccess'))
await loadCacheData()
@@ -163,10 +153,10 @@ async function clearAllCache() {
}
}
/** 请求当前识别数据源接口删除指定识别缓存。 */
/** 请求 TMDB 接口删除指定识别缓存。 */
async function deleteCacheItem(key: string) {
const response = (await api.delete(
`${recognitionCacheEndpoint.value}/${encodeURIComponent(key)}`,
`${RECOGNITION_CACHE_ENDPOINT}/${encodeURIComponent(key)}`,
)) as unknown as ApiResponse
if (!response.success) throw new Error(response.message)
}
@@ -217,10 +207,9 @@ function getPosterUrl(item: RecognitionCacheItem): string {
return getDisplayImageUrl(sourceUrl, globalSettingsStore.globalSettings.GLOBAL_IMAGE_CACHE)
}
/** 获取当前识别数据源对应的媒体 ID。 */
/** 获取识别缓存对应的 TMDB ID。 */
function getRecognitionId(item: RecognitionCacheItem): string {
const recognitionId = recognitionSource.value === 'douban' ? item.douban_id : item.tmdb_id
return recognitionId ? String(recognitionId) : ''
return item.tmdb_id ? String(item.tmdb_id) : ''
}
/** 判断识别缓存条目是否包含有效媒体 ID。 */
@@ -260,13 +249,6 @@ onMounted(() => {
watch([searchFilter, statusFilter], () => {
resetMobilePagination()
})
watch(recognitionSource, () => {
searchFilter.value = ''
statusFilter.value = 'all'
selectedItems.value = []
void loadCacheData()
})
</script>
<template>
@@ -22,13 +22,13 @@ vi.mock('vue-toastification', () => ({
}),
}))
async function renderRecognitionCachePanel() {
async function renderRecognitionCachePanel(recognitionSource = 'themoviedb') {
return renderWithProviders(RecognitionCachePanel, {
initialState: {
globalSettings: {
data: {
GLOBAL_IMAGE_CACHE: false,
RECOGNIZE_SOURCE: 'themoviedb',
RECOGNIZE_SOURCE: recognitionSource,
TMDB_IMAGE_DOMAIN: 'image.tmdb.org',
},
},
@@ -79,4 +79,21 @@ describe('RecognitionCachePanel shared recognition statistics', () => {
expect(screen.queryByText('共享识别')).not.toBeInTheDocument()
})
it('loads TMDB cache even when Douban is selected as the recognition source', async () => {
mocks.apiGet.mockResolvedValue({
data: {
count: 0,
recognized: 0,
unrecognized: 0,
shared_recognized: 0,
shared_recognize_enabled: false,
data: [],
},
})
await renderRecognitionCachePanel('douban')
await waitFor(() => expect(mocks.apiGet).toHaveBeenCalledWith('tmdb/cache'))
})
})
+2 -2
View File
@@ -2033,8 +2033,8 @@ export default {
tmdbLocale: 'TMDB Metadata Language',
tmdbLocalePlaceholder: 'en',
tmdbLocaleHint: 'Customize TheMovieDb metadata language',
metaCacheExpire: 'Media Metadata Cache Expiration Time',
metaCacheExpireHint: 'Recognition metadata local cache time, use built-in default value when set to 0',
metaCacheExpire: 'Per-item Media Metadata Cache TTL',
metaCacheExpireHint: 'Independent TTL for each recognition metadata item; use the built-in default when set to 0',
metaCacheExpireRequired: 'Please enter metadata cache time',
metaCacheExpireMin: 'Metadata cache time must be greater than or equal to 0',
scrapFollowTmdb: 'Follow TMDB Recognition',
+2 -2
View File
@@ -2008,8 +2008,8 @@ export default {
tmdbLocale: 'TMDB 元数据语言',
tmdbLocalePlaceholder: 'zh',
tmdbLocaleHint: '自定义 TheMovieDb 元数据语言',
metaCacheExpire: '媒体元数据缓存过期时间',
metaCacheExpireHint: '识别元数据本地缓存时间,为 0 时使用内置默认值',
metaCacheExpire: '单条媒体元数据缓存有效期',
metaCacheExpireHint: '每条识别元数据的独立有效期,为 0 时使用内置默认值',
metaCacheExpireRequired: '请输入元数据缓存时间',
metaCacheExpireMin: '元数据缓存时间必须大于等于0',
scrapFollowTmdb: '跟随TMDB识别整理',
+2 -2
View File
@@ -2007,8 +2007,8 @@ export default {
tmdbLocale: 'TMDB 元數據語言',
tmdbLocalePlaceholder: 'zh',
tmdbLocaleHint: '自定義 TheMovieDb 元數據語言',
metaCacheExpire: '媒體元數據緩存過期時間',
metaCacheExpireHint: '識別元數據本地緩存時間,為 0 時使用內置默認值',
metaCacheExpire: '單筆媒體元數據緩存有效期',
metaCacheExpireHint: '每筆識別元數據的獨立有效期,為 0 時使用內置默認值',
metaCacheExpireRequired: '請輸入元數據緩存時間',
metaCacheExpireMin: '元數據緩存時間必須大於等於0',
scrapFollowTmdb: '跟隨TMDB識別整理',