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>()