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