mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-20 20:11:39 +08:00
feat: 添加剧集组功能,支持自动查询和手动输入剧集组编号
This commit is contained in:
@@ -150,6 +150,15 @@ let previewFileBodyResizeObserver: ResizeObserver | undefined
|
||||
// 所有存储
|
||||
const storages = ref<StorageConf[]>([])
|
||||
|
||||
// 所有剧集组
|
||||
const episodeGroups = ref<{ [key: string]: any }[]>([])
|
||||
|
||||
// 剧集组加载状态
|
||||
const episodeGroupLoading = ref(false)
|
||||
|
||||
// 剧集组查询防抖句柄
|
||||
let episodeGroupQueryTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
// 查询存储
|
||||
async function loadStorages() {
|
||||
try {
|
||||
@@ -169,6 +178,57 @@ const storageOptions = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
// 剧集组选项属性
|
||||
function episodeGroupItemProps(item: { title: string; subtitle?: string }) {
|
||||
return {
|
||||
title: item.title,
|
||||
subtitle: item.subtitle,
|
||||
}
|
||||
}
|
||||
|
||||
// 剧集组选项,保留空值作为不指定剧集组。
|
||||
const episodeGroupOptions = computed(() => {
|
||||
const options = (
|
||||
episodeGroups.value as { id: string; name: string; group_count: number; episode_count: number }[]
|
||||
).map(item => {
|
||||
return {
|
||||
title: item.name,
|
||||
subtitle: `${t('dialog.reorganize.seasonCount', { count: item.group_count })} • ${t(
|
||||
'dialog.reorganize.episodeCount',
|
||||
{ count: item.episode_count },
|
||||
)}`,
|
||||
value: item.id,
|
||||
}
|
||||
})
|
||||
|
||||
options.unshift({
|
||||
title: t('dialog.reorganize.defaultEpisodeGroup'),
|
||||
subtitle: t('dialog.reorganize.defaultEpisodeGroupHint'),
|
||||
value: '',
|
||||
})
|
||||
|
||||
return options
|
||||
})
|
||||
|
||||
// 查询指定 TMDB 剧集的所有剧集组。
|
||||
async function getEpisodeGroups(tmdbid?: number | string) {
|
||||
const normalizedTmdbId = Number(tmdbid)
|
||||
if (!Number.isInteger(normalizedTmdbId) || normalizedTmdbId <= 0) {
|
||||
episodeGroups.value = []
|
||||
return
|
||||
}
|
||||
|
||||
episodeGroupLoading.value = true
|
||||
try {
|
||||
episodeGroups.value = await api.get(`media/groups/${normalizedTmdbId}`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
episodeGroups.value = []
|
||||
} finally {
|
||||
episodeGroupLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 标题
|
||||
const dialogTitle = computed(() => {
|
||||
return t('dialog.reorganize.manualTitle')
|
||||
@@ -252,6 +312,31 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
// 监听 TMDB 编号变化,自动加载可用剧集组并清空旧选择。
|
||||
watch(
|
||||
() => transferForm.tmdbid,
|
||||
tmdbid => {
|
||||
transferForm.episode_group = ''
|
||||
episodeGroups.value = []
|
||||
if (episodeGroupQueryTimer) clearTimeout(episodeGroupQueryTimer)
|
||||
if (transferForm.type_name !== '电视剧' || mediaSource.value !== 'themoviedb') return
|
||||
episodeGroupQueryTimer = setTimeout(() => getEpisodeGroups(tmdbid), 400)
|
||||
},
|
||||
)
|
||||
|
||||
// 切换媒体类型或识别源时,非 TMDB 电视剧不保留剧集组选择。
|
||||
watch(
|
||||
[() => transferForm.type_name, () => mediaSource.value],
|
||||
([typeName, source]) => {
|
||||
if (typeName === '电视剧' && source === 'themoviedb' && transferForm.tmdbid) {
|
||||
getEpisodeGroups(transferForm.tmdbid)
|
||||
return
|
||||
}
|
||||
transferForm.episode_group = ''
|
||||
episodeGroups.value = []
|
||||
},
|
||||
)
|
||||
|
||||
// 过滤后的预览数据
|
||||
const filteredPreviewItems = computed(() => {
|
||||
return previewData.value?.items ?? []
|
||||
@@ -528,6 +613,7 @@ function createTransferPayload(options: { item?: FileItem; items?: FileItem[]; l
|
||||
...transferForm,
|
||||
fileitem: sourceItem,
|
||||
logid: options.logid ?? 0,
|
||||
episode_group: transferForm.episode_group?.trim() || undefined,
|
||||
}
|
||||
|
||||
if (options.items?.length) {
|
||||
@@ -1014,6 +1100,7 @@ onMounted(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
stopLoadingProgress()
|
||||
if (episodeGroupQueryTimer) clearTimeout(episodeGroupQueryTimer)
|
||||
previewFileBodyResizeObserver?.disconnect()
|
||||
})
|
||||
</script>
|
||||
@@ -1123,9 +1210,14 @@ onUnmounted(() => {
|
||||
</VCol>
|
||||
</VRow>
|
||||
<VRow v-show="transferForm.type_name === '电视剧'">
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
<VCol v-if="mediaSource === 'themoviedb'" cols="12" md="6">
|
||||
<VCombobox
|
||||
v-model="transferForm.episode_group"
|
||||
:items="episodeGroupOptions"
|
||||
:item-props="episodeGroupItemProps"
|
||||
:loading="episodeGroupLoading"
|
||||
:disabled="!transferForm.tmdbid"
|
||||
clearable
|
||||
:label="t('dialog.reorganize.episodeGroup')"
|
||||
:placeholder="t('dialog.reorganize.episodeGroupPlaceholder')"
|
||||
:hint="t('dialog.reorganize.episodeGroupHint')"
|
||||
|
||||
@@ -1850,7 +1850,7 @@ export default {
|
||||
'Word to replace => Replacement\n' +
|
||||
'Front word <> Back word >> Episode offset (EP)\n' +
|
||||
'Word to replace => Replacement && Front word <> Back word >> Episode offset (EP)\n' +
|
||||
'Replacement format supports: {[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} to directly specify TMDBID/Douban ID, where s and e are season and episode numbers (optional)',
|
||||
'Replacement format supports: {[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} to directly specify TMDBID/Douban ID, where g is the episode group ID and s/e are season and episode numbers (all optional)',
|
||||
identifierSaveSuccess: 'Custom identifiers saved successfully',
|
||||
identifierSaveFailed: 'Failed to save custom identifiers!',
|
||||
|
||||
@@ -2544,9 +2544,13 @@ export default {
|
||||
doubanId: 'Douban ID',
|
||||
mediaIdHint: 'Query media ID by name, leave empty for auto recognition',
|
||||
mediaIdPlaceholder: 'Leave empty for auto recognition',
|
||||
episodeGroup: 'Episode Group ID',
|
||||
episodeGroupHint: 'Specify episode group',
|
||||
episodeGroupPlaceholder: 'Manually query episode group',
|
||||
episodeGroup: 'Episode Group',
|
||||
episodeGroupHint: 'After entering a TMDB ID, episode groups are queried automatically; group IDs can still be entered manually',
|
||||
episodeGroupPlaceholder: 'Enter TMDB ID first',
|
||||
defaultEpisodeGroup: 'No episode group',
|
||||
defaultEpisodeGroupHint: 'Use TMDB default season and episode order',
|
||||
seasonCount: '{count} seasons',
|
||||
episodeCount: '{count} episodes',
|
||||
season: 'Season',
|
||||
seasonHint: 'Which season',
|
||||
episodeDetail: 'Episode',
|
||||
@@ -2660,7 +2664,7 @@ export default {
|
||||
customWords: 'Custom Recognition Words',
|
||||
customWordsHint: 'Recognition words only used for this subscription',
|
||||
customWordsPlaceholder:
|
||||
'Block word\nReplaced word => Replacement word\nPrefix <> Suffix >> Episode offset (EP)\nReplaced word => Replacement word && Prefix <> Suffix >> Episode offset (EP)\nReplacement word supports format: { tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx } to directly specify TMDBID/Douban ID recognition, where s, e are season and episode numbers (optional)',
|
||||
'Block word\nReplaced word => Replacement word\nPrefix <> Suffix >> Episode offset (EP)\nReplaced word => Replacement word && Prefix <> Suffix >> Episode offset (EP)\nReplacement word supports format: {[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} to directly specify TMDBID/Douban ID recognition, where g is the episode group ID and s/e are season and episode numbers (all optional)',
|
||||
cancelSubscribe: 'Cancel Subscription',
|
||||
save: 'Save',
|
||||
cancelSubscribeConfirm: 'Are you sure you want to cancel the subscription?',
|
||||
|
||||
@@ -1819,7 +1819,7 @@ export default {
|
||||
'被替换词 => 替换词\n' +
|
||||
'前定位词 <> 后定位词 >> 集偏移量(EP)\n' +
|
||||
'被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量(EP)\n' +
|
||||
'其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别,其中s、e为季数和集数(可选)',
|
||||
'其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别,其中g为剧集组编号,s、e为季数和集数(均可选)',
|
||||
identifierSaveSuccess: '自定义识别词保存成功',
|
||||
identifierSaveFailed: '自定义识别词保存失败!',
|
||||
|
||||
@@ -2498,9 +2498,13 @@ export default {
|
||||
doubanId: '豆瓣编号',
|
||||
mediaIdHint: '按名称查询媒体编号,留空自动识别',
|
||||
mediaIdPlaceholder: '留空自动识别',
|
||||
episodeGroup: '剧集组编号',
|
||||
episodeGroupHint: '指定剧集组',
|
||||
episodeGroupPlaceholder: '手动查询剧集组',
|
||||
episodeGroup: '剧集组',
|
||||
episodeGroupHint: '输入 TMDB 编号后自动查询剧集组,也可手动填写剧集组编号',
|
||||
episodeGroupPlaceholder: '先输入 TMDB 编号',
|
||||
defaultEpisodeGroup: '不指定剧集组',
|
||||
defaultEpisodeGroupHint: '使用 TMDB 默认季集排序',
|
||||
seasonCount: '{count} 季',
|
||||
episodeCount: '{count} 集',
|
||||
season: '季',
|
||||
seasonHint: '第几季',
|
||||
episodeDetail: '集',
|
||||
@@ -2613,7 +2617,7 @@ export default {
|
||||
customWords: '自定义识别词',
|
||||
customWordsHint: '只对该订阅使用的识别词',
|
||||
customWordsPlaceholder:
|
||||
'屏蔽词\n被替换词 => 替换词\n前定位词 <> 后定位词 >> 集偏移量(EP)\n被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量(EP)\n其中替换词支持格式:{ tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx } 直接指定TMDBID/豆瓣ID识别,其中s、e为季数和集数(可选)',
|
||||
'屏蔽词\n被替换词 => 替换词\n前定位词 <> 后定位词 >> 集偏移量(EP)\n被替换词 => 替换词 && 前定位词 <> 后定位词 >> 集偏移量(EP)\n其中替换词支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID识别,其中g为剧集组编号,s、e为季数和集数(均可选)',
|
||||
cancelSubscribe: '取消订阅',
|
||||
save: '保存',
|
||||
cancelSubscribeConfirm: '是否确认取消订阅?',
|
||||
|
||||
@@ -1820,7 +1820,7 @@ export default {
|
||||
'被替換詞 => 替換詞\n' +
|
||||
'前定位詞 <> 後定位詞 >> 集偏移量(EP)\n' +
|
||||
'被替換詞 => 替換詞 && 前定位詞 <> 後定位詞 >> 集偏移量(EP)\n' +
|
||||
'其中替換詞支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID識別,其中s、e為季數和集數(可選)',
|
||||
'其中替換詞支持格式:{[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID識別,其中g為劇集組編號,s、e為季數和集數(均可選)',
|
||||
identifierSaveSuccess: '自定義識別詞保存成功',
|
||||
identifierSaveFailed: '自定義識別詞保存失敗!',
|
||||
|
||||
@@ -2499,9 +2499,13 @@ export default {
|
||||
doubanId: '豆瓣編號',
|
||||
mediaIdHint: '按名稱查詢媒體編號,留空自動識別',
|
||||
mediaIdPlaceholder: '留空自動識別',
|
||||
episodeGroup: '劇集組編號',
|
||||
episodeGroupHint: '指定劇集組',
|
||||
episodeGroupPlaceholder: '手動查詢劇集組',
|
||||
episodeGroup: '劇集組',
|
||||
episodeGroupHint: '輸入 TMDB 編號後自動查詢劇集組,也可手動填寫劇集組編號',
|
||||
episodeGroupPlaceholder: '先輸入 TMDB 編號',
|
||||
defaultEpisodeGroup: '不指定劇集組',
|
||||
defaultEpisodeGroupHint: '使用 TMDB 預設季集排序',
|
||||
seasonCount: '{count} 季',
|
||||
episodeCount: '{count} 集',
|
||||
season: '季',
|
||||
seasonHint: '第幾季',
|
||||
episodeDetail: '集',
|
||||
@@ -2614,7 +2618,7 @@ export default {
|
||||
customWords: '自定義識別詞',
|
||||
customWordsHint: '只對該訂閱使用的識別詞',
|
||||
customWordsPlaceholder:
|
||||
'屏蔽詞\n被替換詞 => 替換詞\n前定位詞 <> 後定位詞 >> 集偏移量(EP)\n被替換詞 => 替換詞 && 前定位詞 <> 後定位詞 >> 集偏移量(EP)\n其中替換詞支援格式:{ tmdbid/doubanid=xxx;type=movie/tv;s=xxx;e=xxx } 直接指定TMDBID/豆瓣ID識別,其中s、e為季數和集數(可選)',
|
||||
'屏蔽詞\n被替換詞 => 替換詞\n前定位詞 <> 後定位詞 >> 集偏移量(EP)\n被替換詞 => 替換詞 && 前定位詞 <> 後定位詞 >> 集偏移量(EP)\n其中替換詞支援格式:{[tmdbid/doubanid=xxx;type=movie/tv;g=xxx;s=xxx;e=xxx]} 直接指定TMDBID/豆瓣ID識別,其中g為劇集組編號,s、e為季數和集數(均可選)',
|
||||
cancelSubscribe: '取消訂閱',
|
||||
save: '儲存',
|
||||
cancelSubscribeConfirm: '是否確認取消訂閱?',
|
||||
|
||||
Reference in New Issue
Block a user