diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index f2869856..99e09885 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -4728,6 +4728,10 @@ export default { success: 'Success', failed: 'Failed', }, + category: { + subtitle: 'Subtitle', + audio: 'Audio', + }, deleteStep: { source: 'Source file', destination: 'Media library file', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index ca75264c..f2be3653 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -4632,6 +4632,10 @@ export default { success: '成功', failed: '失败', }, + category: { + subtitle: '字幕', + audio: '音频', + }, deleteStep: { source: '源文件', destination: '媒体库文件', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index ad51ed6c..f3fd7c5a 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -4573,6 +4573,10 @@ export default { success: '成功', failed: '失敗', }, + category: { + subtitle: '字幕', + audio: '音訊', + }, deleteStep: { source: '來源檔案', destination: '媒體庫檔案', diff --git a/src/views/reorganize/TransferHistoryView.vue b/src/views/reorganize/TransferHistoryView.vue index 7e65b56d..a1ba70e0 100644 --- a/src/views/reorganize/TransferHistoryView.vue +++ b/src/views/reorganize/TransferHistoryView.vue @@ -1388,9 +1388,68 @@ function getHistoryDisplayTitle(item: TransferHistory) { // 获取移动端卡片副标题,优先展示二级分类和年份。 function getHistorySubtitle(item: TransferHistory) { - return [item.category, item.year, item.type === '音乐' ? formatMusicAudioSpecs(item) : ''].filter(Boolean).join(' / ') + return [getHistoryCategory(item), item.year, item.type === '音乐' ? formatMusicAudioSpecs(item) : ''] + .filter(Boolean) + .join(' / ') } +// 附加文件不属于媒体分类,按源文件扩展名显示独立类别,避免与电影/剧集分类混淆。 +function getHistoryCategory(item: TransferHistory) { + const rawExtension = item.src_fileitem?.extension || getFileExtension(item.src) + const extension = rawExtension ? `.${rawExtension.replace(/^\./, '').toLowerCase()}` : '' + if (subtitleExtensions.has(extension)) return t('transferHistory.category.subtitle') + if (audioExtensions.has(extension) && item.type !== '音乐') return t('transferHistory.category.audio') + return item.category +} + +function getFileExtension(path?: string) { + const filename = path?.split(/[\\/]/).at(-1) || '' + const dot = filename.lastIndexOf('.') + return dot >= 0 ? filename.slice(dot) : '' +} + +const subtitleExtensions = new Set(['.srt', '.ass', '.ssa', '.sup']) +const audioExtensions = new Set([ + '.aac', + '.ac3', + '.amr', + '.caf', + '.cda', + '.dsf', + '.dff', + '.kar', + '.m4a', + '.mp1', + '.mp2', + '.mp3', + '.mid', + '.mod', + '.mka', + '.mpc', + '.nsf', + '.ogg', + '.pcm', + '.rmi', + '.s3m', + '.snd', + '.spx', + '.tak', + '.tta', + '.vqf', + '.wav', + '.wma', + '.aifc', + '.aiff', + '.alac', + '.adif', + '.adts', + '.ape', + '.flac', + '.midi', + '.opus', + '.sfalc', +]) + // 获取存储展示名称,配置缺失时回退到原始存储标识。 function getHistoryStorageName(storage?: string) { if (!storage) return t('common.unknown') @@ -1849,7 +1908,7 @@ onUnmounted(() => { {{ item?.seasons }}{{ item?.episodes }} - {{ item?.category }} + {{ getHistoryCategory(item) }} @@ -1955,7 +2014,7 @@ onUnmounted(() => { {{ item?.title }} - {{ item?.category }} + {{ getHistoryCategory(item) }} diff --git a/src/views/reorganize/__tests__/TransferHistoryView.spec.ts b/src/views/reorganize/__tests__/TransferHistoryView.spec.ts index c7d742a4..1b54ffe1 100644 --- a/src/views/reorganize/__tests__/TransferHistoryView.spec.ts +++ b/src/views/reorganize/__tests__/TransferHistoryView.spec.ts @@ -963,6 +963,41 @@ describe('TransferHistoryView', () => { expect(await screen.findByText('华语流行 / 2003 / FLAC · 24-bit · 96 kHz · 2,304 kbps')).toBeInTheDocument() }) + it('shows subtitle and attached audio as distinct history categories', async () => { + mocks.desktop = false + const histories = [ + createHistory(1, '字幕文件', { + category: '电影', + image: '/subtitle-poster.jpg', + src: '/downloads/电影.zh.srt', + src_fileitem: { extension: 'srt', size: 100 } as TransferHistory['src_fileitem'], + }), + createHistory(2, '音频文件', { + category: '电影', + image: '/audio-poster.jpg', + src: '/downloads/电影.commentary.mka', + src_fileitem: { extension: 'mka', size: 200 } as TransferHistory['src_fileitem'], + }), + createHistory(3, '正常媒体', { + category: '动作', + image: '/movie-poster.jpg', + src: '/downloads/电影.mkv', + src_fileitem: { extension: 'mkv', size: 300 } as TransferHistory['src_fileitem'], + }), + ] + mocks.apiGet.mockImplementation((path: string) => { + if (path === 'storage/options') return Promise.resolve(storageResponse()) + return Promise.resolve(historyResponse(histories)) + }) + + await renderHistory() + await fireEvent.click(screen.getByRole('button', { name: '加载下一页' })) + + expect(await screen.findByText('字幕')).toBeInTheDocument() + expect(screen.getByText('音频')).toBeInTheDocument() + expect(screen.getByText('动作')).toBeInTheDocument() + }) + it('prevents a mobile request invalidated by a route reset from appending stale records', async () => { mocks.desktop = false const oldRequest = createDeferred>()