Compare commits

...

6 Commits

Author SHA1 Message Date
jxxghp
cc8d5cf931 style: lighten setting card accents 2026-05-21 22:05:46 +08:00
jxxghp
6083887675 fix: load registered passkeys on dialog open 2026-05-21 07:06:51 +08:00
jxxghp
beb0506b0c feat: show plugin system version compatibility 2026-05-20 19:56:21 +08:00
InfinityPacer
0f906f791a fix(filter-rule): keep custom rule chip titles in sync with props (#474) 2026-05-20 17:29:46 +08:00
jxxghp
7614696e61 fix: 修复智能推荐按钮初始不可用 2026-05-20 12:36:41 +08:00
jxxghp
4235d3687c feat: add tmdb api key setting 2026-05-20 11:28:29 +08:00
15 changed files with 140 additions and 41 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "moviepilot", "name": "moviepilot",
"version": "2.12.2", "version": "2.12.4",
"private": true, "private": true,
"type": "module", "type": "module",
"bin": "dist/service.js", "bin": "dist/service.js",

View File

@@ -646,6 +646,12 @@ export interface Plugin {
has_page?: boolean has_page?: boolean
// 是否有新版本 // 是否有新版本
has_update?: boolean has_update?: boolean
// 主系统版本是否兼容
system_version_compatible?: boolean
// 主系统版本兼容提示
system_version_message?: string
// 主系统版本限定范围
system_version?: string
// 是否本地插件 // 是否本地插件
is_local?: boolean is_local?: boolean
// 插件仓库地址 // 插件仓库地址

View File

@@ -28,19 +28,18 @@ function filtersChanged(value: string[]) {
} }
// 过滤规则下拉框 // 过滤规则下拉框
const selectFilterOptions = ref<{ [key: string]: string }[]>([]) // 同时包含内置规则与用户自定义规则;使用 computed 而非 onMounted 一次性赋值,
// 是为了在父组件异步加载完 custom_rules 或后续新增/删除规则时,
onMounted(() => { // 选项与已选 chip 的显示名title能跟随刷新避免回退到原始 ID如 "zhong")。
selectFilterOptions.value = cloneDeep(innerFilterRules) const selectFilterOptions = computed<{ [key: string]: string }[]>(() => {
if (props.custom_rules) { const options = cloneDeep(innerFilterRules)
console.log(props.custom_rules) props.custom_rules?.forEach(rule => {
props.custom_rules.map(rule => { options.push({
selectFilterOptions.value.push({ title: rule.name,
title: rule.name, value: rule.id,
value: rule.id,
})
}) })
} })
return options
}) })
</script> </script>

View File

@@ -226,6 +226,11 @@ async function resetPlugin() {
// 更新插件 // 更新插件
async function updatePlugin() { async function updatePlugin() {
if (props.plugin?.system_version_compatible === false) {
$toast.error(props.plugin?.system_version_message || t('plugin.incompatibleSystemVersion'))
return
}
try { try {
// 显示等待提示框 // 显示等待提示框
showPluginProgress(t('plugin.updating', { name: props.plugin?.plugin_name })) showPluginProgress(t('plugin.updating', { name: props.plugin?.plugin_name }))

View File

@@ -206,6 +206,7 @@ watch(
passkeyList.value = [] passkeyList.value = []
} }
}, },
{ immediate: true },
) )
</script> </script>

View File

@@ -98,6 +98,11 @@ function visitPluginPage() {
/** 安装插件并通知父级刷新市场列表。 */ /** 安装插件并通知父级刷新市场列表。 */
async function installPlugin() { async function installPlugin() {
if (props.plugin?.system_version_compatible === false) {
$toast.error(props.plugin?.system_version_message || t('plugin.incompatibleSystemVersion'))
return
}
try { try {
showInstallProgress( showInstallProgress(
t('plugin.installing', { t('plugin.installing', {
@@ -176,9 +181,28 @@ onUnmounted(() => {
</span> </span>
</VListItemTitle> </VListItemTitle>
</VListItem> </VListItem>
<VListItem v-if="props.plugin?.system_version" class="ps-0">
<VListItemTitle class="text-center text-md-left">
<span class="font-weight-medium">{{ t('plugin.systemVersion') }}</span>
<span class="text-body-1">{{ props.plugin?.system_version }}</span>
</VListItemTitle>
</VListItem>
</VList> </VList>
<VAlert
v-if="props.plugin?.system_version_compatible === false"
type="warning"
variant="tonal"
density="compact"
class="mb-3"
:text="props.plugin?.system_version_message || t('plugin.incompatibleSystemVersion')"
/>
<div class="text-center text-md-left"> <div class="text-center text-md-left">
<VBtn color="primary" @click="installPlugin" prepend-icon="mdi-download"> <VBtn
color="primary"
@click="installPlugin"
prepend-icon="mdi-download"
:disabled="props.plugin?.system_version_compatible === false"
>
{{ t('plugin.installToLocal') }} {{ t('plugin.installToLocal') }}
</VBtn> </VBtn>
<div class="text-xs mt-2" v-if="props.count"> <div class="text-xs mt-2" v-if="props.count">

View File

@@ -49,7 +49,15 @@ function handleUpdate() {
<template v-if="props.showUpdateAction"> <template v-if="props.showUpdateAction">
<VDivider /> <VDivider />
<VCardItem> <VCardItem>
<VBtn @click="handleUpdate" block> <VAlert
v-if="props.plugin?.system_version_compatible === false"
type="warning"
variant="tonal"
density="compact"
class="mb-3"
:text="props.plugin?.system_version_message || t('plugin.incompatibleSystemVersion')"
/>
<VBtn @click="handleUpdate" block :disabled="props.plugin?.system_version_compatible === false">
<template #prepend> <template #prepend>
<VIcon icon="mdi-arrow-up-circle-outline" /> <VIcon icon="mdi-arrow-up-circle-outline" />
</template> </template>

View File

@@ -1559,15 +1559,19 @@ export default {
'Can improve read/write concurrency performance, but may increase the risk of data loss in exceptional cases, requires restart to take effect', 'Can improve read/write concurrency performance, but may increase the risk of data loss in exceptional cases, requires restart to take effect',
tmdbApiDomain: 'TMDB API Service Address', tmdbApiDomain: 'TMDB API Service Address',
tmdbApiDomainPlaceholder: 'api.themoviedb.org', tmdbApiDomainPlaceholder: 'api.themoviedb.org',
tmdbApiDomainHint: 'Customize themoviedb API domain or proxy address', tmdbApiDomainHint: 'Customize TheMovieDb API domain or proxy address',
tmdbApiDomainRequired: 'Please enter TMDB API domain', tmdbApiDomainRequired: 'Please enter TMDB API domain',
tmdbApiKey: 'TMDB API Key',
tmdbApiKeyPlaceholder: 'Please enter TMDB API Key',
tmdbApiKeyHint: 'Set TheMovieDb API Key',
tmdbApiKeyRequired: 'Please enter TMDB API Key',
tmdbImageDomain: 'TMDB Image Service Address', tmdbImageDomain: 'TMDB Image Service Address',
tmdbImageDomainPlaceholder: 'image.tmdb.org', tmdbImageDomainPlaceholder: 'image.tmdb.org',
tmdbImageDomainHint: 'Customize themoviedb image service domain or proxy address', tmdbImageDomainHint: 'Customize TheMovieDb image service domain or proxy address',
tmdbImageDomainRequired: 'Please enter image service domain', tmdbImageDomainRequired: 'Please enter image service domain',
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: 'Media Metadata Cache Expiration Time',
metaCacheExpireHint: 'Recognition metadata local cache time, use built-in default value when set to 0', metaCacheExpireHint: 'Recognition metadata local cache time, use built-in default value when set to 0',
metaCacheExpireRequired: 'Please enter metadata cache time', metaCacheExpireRequired: 'Please enter metadata cache time',
@@ -1576,7 +1580,7 @@ export default {
scrapFollowTmdbHint: scrapFollowTmdbHint:
'When turned off, organization history will be used (if available) to avoid TMDB data changes during subscription', 'When turned off, organization history will be used (if available) to avoid TMDB data changes during subscription',
scrapOriginalImage: 'Scrap TheMovieDb Original Language Image', scrapOriginalImage: 'Scrap TheMovieDb Original Language Image',
scrapOriginalImageHint: 'Scrap original language image from themoviedb, otherwise scrap metadata language image', scrapOriginalImageHint: 'Scrap original language image from TheMovieDb, otherwise scrap metadata language image',
fanartEnable: 'Fanart Image Data Source', fanartEnable: 'Fanart Image Data Source',
fanartEnableHint: 'Use image data from fanart.tv', fanartEnableHint: 'Use image data from fanart.tv',
fanartLang: 'Fanart Language', fanartLang: 'Fanart Language',
@@ -2838,6 +2842,8 @@ export default {
projectHome: 'Project Home', projectHome: 'Project Home',
updateHistory: 'Update History', updateHistory: 'Update History',
local: 'Local', local: 'Local',
systemVersion: 'System Version',
incompatibleSystemVersion: 'The current MoviePilot version does not meet this plugin requirement.',
installToLocal: 'Install to Local', installToLocal: 'Install to Local',
totalDownloads: 'Total {count} downloads', totalDownloads: 'Total {count} downloads',
viewData: 'View Data', viewData: 'View Data',

View File

@@ -1542,15 +1542,19 @@ export default {
dbWalEnableHint: '可提升读写并发性能,但可能在异常情况下增加数据丢失风险,更改后需重启生效', dbWalEnableHint: '可提升读写并发性能,但可能在异常情况下增加数据丢失风险,更改后需重启生效',
tmdbApiDomain: 'TMDB API服务地址', tmdbApiDomain: 'TMDB API服务地址',
tmdbApiDomainPlaceholder: 'api.themoviedb.org', tmdbApiDomainPlaceholder: 'api.themoviedb.org',
tmdbApiDomainHint: '自定义themoviedb API域名或代理地址', tmdbApiDomainHint: '自定义 TheMovieDb API域名或代理地址',
tmdbApiDomainRequired: '请输入TMDB API域名', tmdbApiDomainRequired: '请输入TMDB API域名',
tmdbApiKey: 'TMDB API Key',
tmdbApiKeyPlaceholder: '请输入 TMDB API Key',
tmdbApiKeyHint: '设置 TheMovieDb API Key',
tmdbApiKeyRequired: '请输入TMDB API Key',
tmdbImageDomain: 'TMDB 图片服务地址', tmdbImageDomain: 'TMDB 图片服务地址',
tmdbImageDomainPlaceholder: 'image.tmdb.org', tmdbImageDomainPlaceholder: 'image.tmdb.org',
tmdbImageDomainHint: '自定义themoviedb图片服务域名或代理地址', tmdbImageDomainHint: '自定义 TheMovieDb 图片服务域名或代理地址',
tmdbImageDomainRequired: '请输入图片服务域名', tmdbImageDomainRequired: '请输入图片服务域名',
tmdbLocale: 'TMDB 元数据语言', tmdbLocale: 'TMDB 元数据语言',
tmdbLocalePlaceholder: 'zh', tmdbLocalePlaceholder: 'zh',
tmdbLocaleHint: '自定义themoviedb元数据语言', tmdbLocaleHint: '自定义 TheMovieDb 元数据语言',
metaCacheExpire: '媒体元数据缓存过期时间', metaCacheExpire: '媒体元数据缓存过期时间',
metaCacheExpireHint: '识别元数据本地缓存时间,为 0 时使用内置默认值', metaCacheExpireHint: '识别元数据本地缓存时间,为 0 时使用内置默认值',
metaCacheExpireRequired: '请输入元数据缓存时间', metaCacheExpireRequired: '请输入元数据缓存时间',
@@ -2791,6 +2795,8 @@ export default {
projectHome: '项目主页', projectHome: '项目主页',
updateHistory: '更新说明', updateHistory: '更新说明',
local: '本地', local: '本地',
systemVersion: '系统版本',
incompatibleSystemVersion: '当前 MoviePilot 版本不满足插件要求,无法安装',
installToLocal: '安装到本地', installToLocal: '安装到本地',
totalDownloads: '共 {count} 次下载', totalDownloads: '共 {count} 次下载',
viewData: '查看数据', viewData: '查看数据',

View File

@@ -1543,15 +1543,19 @@ export default {
dbWalEnableHint: '可提升讀寫併發性能,但可能在異常情況下增加數據丟失風險,更改後需重啟生效', dbWalEnableHint: '可提升讀寫併發性能,但可能在異常情況下增加數據丟失風險,更改後需重啟生效',
tmdbApiDomain: 'TMDB API服務地址', tmdbApiDomain: 'TMDB API服務地址',
tmdbApiDomainPlaceholder: 'api.themoviedb.org', tmdbApiDomainPlaceholder: 'api.themoviedb.org',
tmdbApiDomainHint: '自定義themoviedb API域名或代理地址', tmdbApiDomainHint: '自定義 TheMovieDb API域名或代理地址',
tmdbApiDomainRequired: '請輸入TMDB API域名', tmdbApiDomainRequired: '請輸入TMDB API域名',
tmdbApiKey: 'TMDB API Key',
tmdbApiKeyPlaceholder: '請輸入 TMDB API Key',
tmdbApiKeyHint: '設定 TheMovieDb API Key',
tmdbApiKeyRequired: '請輸入TMDB API Key',
tmdbImageDomain: 'TMDB 圖片服務地址', tmdbImageDomain: 'TMDB 圖片服務地址',
tmdbImageDomainPlaceholder: 'image.tmdb.org', tmdbImageDomainPlaceholder: 'image.tmdb.org',
tmdbImageDomainHint: '自定義themoviedb圖片服務域名或代理地址', tmdbImageDomainHint: '自定義 TheMovieDb 圖片服務域名或代理地址',
tmdbImageDomainRequired: '請輸入圖片服務域名', tmdbImageDomainRequired: '請輸入圖片服務域名',
tmdbLocale: 'TMDB 元數據語言', tmdbLocale: 'TMDB 元數據語言',
tmdbLocalePlaceholder: 'zh', tmdbLocalePlaceholder: 'zh',
tmdbLocaleHint: '自定義themoviedb元數據語言', tmdbLocaleHint: '自定義 TheMovieDb 元數據語言',
metaCacheExpire: '媒體元數據緩存過期時間', metaCacheExpire: '媒體元數據緩存過期時間',
metaCacheExpireHint: '識別元數據本地緩存時間,為 0 時使用內置默認值', metaCacheExpireHint: '識別元數據本地緩存時間,為 0 時使用內置默認值',
metaCacheExpireRequired: '請輸入元數據緩存時間', metaCacheExpireRequired: '請輸入元數據緩存時間',

View File

@@ -294,6 +294,7 @@ const streamPreviewLimit = 24
const streamUiFlushDelay = 1000 const streamUiFlushDelay = 1000
const streamPreviewBufferLimit = streamPreviewLimit * 4 const streamPreviewBufferLimit = streamPreviewLimit * 4
const searchStreamIdleTimeout = 90_000 const searchStreamIdleTimeout = 90_000
const searchStreamDoneCloseDelay = 1500
const streamTotalCount = ref(0) const streamTotalCount = ref(0)
const streamPreviewDataList = ref<Array<Context>>([]) const streamPreviewDataList = ref<Array<Context>>([])
@@ -528,6 +529,11 @@ function resetSearchResults() {
applyFilter() applyFilter()
} }
// 判断当前页面是否已经完成过一次带关键词的空结果搜索,避免 keep-alive 返回时自动重搜。
function hasLoadedEmptySearchResult() {
return isRefreshed.value && !progressActive.value && rawDataList.value.length === 0 && hasSearchKeyword(activeSearchParams.value)
}
// 更新搜索进度 // 更新搜索进度
function updateSearchProgress(eventData: { [key: string]: any }, flushNow: boolean = false) { function updateSearchProgress(eventData: { [key: string]: any }, flushNow: boolean = false) {
if (eventData.text) { if (eventData.text) {
@@ -658,6 +664,7 @@ function searchByStream(params: SearchParams, requestToken?: string) {
closeSearchEventSource() closeSearchEventSource()
let settled = false let settled = false
let receivedDone = false
const source = new EventSource(buildSearchStreamUrl(params, requestToken)) const source = new EventSource(buildSearchStreamUrl(params, requestToken))
searchEventSource = source searchEventSource = source
@@ -692,7 +699,12 @@ function searchByStream(params: SearchParams, requestToken?: string) {
} }
if (eventData.type === 'done') { if (eventData.type === 'done') {
settleSearchStream(resolve) // 收到 done 后给后端留出收尾时间,避免过早关闭连接中断搜索结果缓存写入
receivedDone = true
clearSearchStreamIdleTimer()
searchStreamIdleTimer = setTimeout(() => {
settleSearchStream(resolve)
}, searchStreamDoneCloseDelay)
} }
} catch (error) { } catch (error) {
settleSearchStream(() => reject(error)) settleSearchStream(() => reject(error))
@@ -702,6 +714,11 @@ function searchByStream(params: SearchParams, requestToken?: string) {
source.onerror = () => { source.onerror = () => {
if (source !== searchEventSource || settled) return if (source !== searchEventSource || settled) return
if (receivedDone) {
settleSearchStream(resolve)
return
}
settleSearchStream(() => reject(new Error(t('resource.noResourceFound')))) settleSearchStream(() => reject(new Error(t('resource.noResourceFound'))))
} }
}) })
@@ -1009,8 +1026,8 @@ async function checkAiRecommendStatus() {
const { success, data } = result const { success, data } = result
const status = data?.status const status = data?.status
// 只要有数据且状态不是disabled就标记已检查允许重试 // 状态检查只是初始化已有推荐结果,非禁用状态下即使后端暂无历史状态也不应锁住按钮
if (data && status !== 'disabled') { if (status !== 'disabled') {
aiStatusChecked.value = true aiStatusChecked.value = true
} }
@@ -1030,6 +1047,8 @@ async function checkAiRecommendStatus() {
} }
} catch (error) { } catch (error) {
console.error('检查AI状态失败:', error) console.error('检查AI状态失败:', error)
// 检查失败不影响用户手动发起智能推荐,避免按钮永久不可用
aiStatusChecked.value = true
} }
} }
@@ -1081,6 +1100,7 @@ onMounted(async () => {
useKeepAliveRefresh(async () => { useKeepAliveRefresh(async () => {
if (progressActive.value || isRefreshing.value || isRecommending.value || showingAiResults.value) return if (progressActive.value || isRefreshing.value || isRecommending.value || showingAiResults.value) return
if (hasLoadedEmptySearchResult()) return
const refreshParams = await resolveRefreshSearchParams() const refreshParams = await resolveRefreshSearchParams()
if (!refreshParams) return if (!refreshParams) return

View File

@@ -74,7 +74,7 @@ html {
block-size: 100%; block-size: 100%;
} }
// 设置项强调卡片:复用通知模板入口的强调条、轻渐变与悬浮反馈。 // 设置项强调卡片:复用通知模板入口的强调条、轻渐变与悬浮反馈。
.app-card-colorful { .app-card-colorful {
overflow: hidden; overflow: hidden;
border: 1px solid rgba(var(--app-card-accent-rgb), var(--app-card-border-opacity)) !important; border: 1px solid rgba(var(--app-card-accent-rgb), var(--app-card-border-opacity)) !important;
@@ -91,11 +91,11 @@ html {
color: rgb(var(--v-theme-on-surface)); color: rgb(var(--v-theme-on-surface));
--app-card-accent-rgb: var(--v-theme-primary); --app-card-accent-rgb: var(--v-theme-primary);
--app-card-accent-end-rgb: var(--app-card-accent-rgb); --app-card-accent-end-rgb: var(--app-card-accent-rgb);
--app-card-accent-start-opacity: 0.09; --app-card-accent-start-opacity: 0.025;
--app-card-accent-end-opacity: 0.06; --app-card-accent-end-opacity: 0.012;
--app-card-border-opacity: 0.2; --app-card-border-opacity: 0.08;
--app-card-hover-border-opacity: 0.34; --app-card-hover-border-opacity: 0.16;
--app-card-stripe-opacity: 0.78; --app-card-stripe-opacity: 0.22;
--app-card-surface-opacity: 0.92; --app-card-surface-opacity: 0.92;
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease; transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
} }
@@ -105,7 +105,7 @@ html {
background: rgb(var(--app-card-accent-rgb)); background: rgb(var(--app-card-accent-rgb));
block-size: 100%; block-size: 100%;
content: ""; content: "";
inline-size: 0.25rem; inline-size: 0.125rem;
inset-block: 0; inset-block: 0;
inset-inline-start: 0; inset-inline-start: 0;
opacity: var(--app-card-stripe-opacity); opacity: var(--app-card-stripe-opacity);
@@ -136,11 +136,11 @@ html[data-theme="transparent"] .app-card-colorful,
.v-theme--transparent .app-card-colorful { .v-theme--transparent .app-card-colorful {
backdrop-filter: blur(var(--transparent-blur, 10px)); backdrop-filter: blur(var(--transparent-blur, 10px));
border: 0 !important; border: 0 !important;
--app-card-accent-start-opacity: 0.04; --app-card-accent-start-opacity: 0.018;
--app-card-accent-end-opacity: 0.03; --app-card-accent-end-opacity: 0.01;
--app-card-border-opacity: 0; --app-card-border-opacity: 0;
--app-card-hover-border-opacity: 0; --app-card-hover-border-opacity: 0;
--app-card-stripe-opacity: 0.42; --app-card-stripe-opacity: 0.16;
--app-card-surface-opacity: var(--transparent-opacity-light, 0.2); --app-card-surface-opacity: var(--transparent-opacity-light, 0.2);
} }

View File

@@ -37,13 +37,13 @@ html[data-theme="transparent"] {
} }
} }
// 设置页彩色卡片保留透明主题的玻璃质感,只叠加非常轻的图标主色。 // 设置页彩色卡片保留透明主题的玻璃质感,只叠加轻的图标主色。
.app-card-colorful { .app-card-colorful {
background: background:
linear-gradient( linear-gradient(
135deg, 135deg,
rgba(var(--app-card-accent-rgb), var(--app-card-accent-start-opacity, 0.04)), rgba(var(--app-card-accent-rgb), var(--app-card-accent-start-opacity, 0.018)),
rgba(var(--app-card-accent-end-rgb, var(--app-card-accent-rgb)), var(--app-card-accent-end-opacity, 0.03)) 46%, rgba(var(--app-card-accent-end-rgb, var(--app-card-accent-rgb)), var(--app-card-accent-end-opacity, 0.01)) 46%,
rgba(var(--v-theme-surface), 0) 76% rgba(var(--v-theme-surface), 0) 76%
), ),
rgba(var(--v-theme-surface), var(--transparent-opacity-light)) !important; rgba(var(--v-theme-surface), var(--transparent-opacity-light)) !important;

View File

@@ -665,6 +665,11 @@ function closePluginProgressDialog() {
// 安装插件 // 安装插件
async function installPlugin(item: Plugin) { async function installPlugin(item: Plugin) {
if (item?.system_version_compatible === false) {
$toast.error(item.system_version_message || t('plugin.incompatibleSystemVersion'))
return
}
try { try {
// 显示等待提示框 // 显示等待提示框
progressText.value = t('plugin.installing', { name: item?.plugin_name, version: item?.plugin_version }) progressText.value = t('plugin.installing', { name: item?.plugin_name, version: item?.plugin_version })
@@ -775,6 +780,9 @@ async function fetchUninstalledPlugins(force: boolean = false, context: KeepAliv
data.has_update = true data.has_update = true
data.repo_url = uninstalled.repo_url data.repo_url = uninstalled.repo_url
data.history = uninstalled.history data.history = uninstalled.history
data.system_version = uninstalled.system_version
data.system_version_compatible = uninstalled.system_version_compatible
data.system_version_message = uninstalled.system_version_message
} }
} }
} }

View File

@@ -96,6 +96,7 @@ const SystemSettings = ref<any>({
RECOGNIZE_PLUGIN_FIRST: false, RECOGNIZE_PLUGIN_FIRST: false,
MEDIA_RECOGNIZE_SHARE: true, MEDIA_RECOGNIZE_SHARE: true,
TMDB_API_DOMAIN: null, TMDB_API_DOMAIN: null,
TMDB_API_KEY: null,
TMDB_IMAGE_DOMAIN: null, TMDB_IMAGE_DOMAIN: null,
TMDB_LOCALE: null, TMDB_LOCALE: null,
META_CACHE_EXPIRE: 0, META_CACHE_EXPIRE: 0,
@@ -1798,6 +1799,17 @@ watch(currentLlmSnapshotKey, (snapshotKey, previousSnapshotKey) => {
prepend-inner-icon="mdi-api" prepend-inner-icon="mdi-api"
/> />
</VCol> </VCol>
<VCol cols="12" md="6">
<VTextField
v-model="SystemSettings.Advanced.TMDB_API_KEY"
:label="t('setting.system.tmdbApiKey')"
:hint="t('setting.system.tmdbApiKeyHint')"
persistent-hint
:placeholder="t('setting.system.tmdbApiKeyPlaceholder')"
:rules="[(v: string) => !!v || t('setting.system.tmdbApiKeyRequired')]"
prepend-inner-icon="mdi-key-variant"
/>
</VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VCombobox <VCombobox
v-model="SystemSettings.Advanced.TMDB_IMAGE_DOMAIN" v-model="SystemSettings.Advanced.TMDB_IMAGE_DOMAIN"