fix(media): format named season labels correctly

This commit is contained in:
hectorqin committed 2026-09-29 10:16:12 +08:00
1 parent 2d13f11811
commit ee6a6fb755
2 files changed
+10 -2

No files matched your search

+3 -2
View File
@@ -7,7 +7,7 @@ import {MediaSelect} from './select.tsx';
import type {MediaApi} from './api.ts';
type Mode='auto'|'movie'|'series'|'season'|'ignore';
interface Rule {path:string;mode:Mode;title?:string;season?:number|string;year?:number;stripLeadingNumber?:boolean}
interface Proposal {assetId:string;ref:string;before:{id:string;title:string;kind:string}|null;status:'ready'|'review'|'protected'|'ignored';reason:string;after:{kind:string;confidence:string;metadata:{title:string;show?:string;season?:number;episode?:number}}}
interface Proposal {assetId:string;ref:string;before:{id:string;title:string;kind:string}|null;status:'ready'|'review'|'protected'|'ignored';reason:string;after:{kind:string;confidence:string;metadata:{title:string;show?:string;season?:number|string;episode?:number}}}
interface Preview {id:string;items:Proposal[]}
const labels:Record<Mode,string>={auto:'自动识别',movie:'电影目录',series:'剧集目录',season:'指定季目录',ignore:'忽略目录'};
@@ -35,7 +35,8 @@ export function FolderRecognition({api,libraryId,path,disabled,onBusy,onApplied}
const result=await api.request<{revision:string}>(base+'/recognition-rule','PUT',{path,rule,revision},signal);
if(!signal.aborted){setRevision(result.revision);setNotice('目录规则已保存。已有作品尚未更改。');}
}
const description=(row:Proposal)=>row.after.kind==='ignore'?'跳过扫描':row.after.kind==='episode'?`${row.after.metadata.show} · 第 ${row.after.metadata.season} 季第 ${row.after.metadata.episode} 集`:`电影 · ${row.after.metadata.title}`;
const seasonLabel=(season:number|string|undefined)=>typeof season==='number'&&Number.isFinite(season)?`第 ${season} 季`:String(season??'正片');
const description=(row:Proposal)=>row.after.kind==='ignore'?'跳过扫描':row.after.kind==='episode'?`${row.after.metadata.show} · ${seasonLabel(row.after.metadata.season)}第 ${row.after.metadata.episode} 集`:`电影 · ${row.after.metadata.title}`;
const selectable=(row:Proposal)=>['ready','review'].includes(row.status);
const visible=preview?.items.slice(page*30,page*30+30)??[];
const selectRows=(rows:Proposal[])=>setSelected(current=>[...new Set([...current,...rows.filter(selectable).map(row=>row.assetId)])]);
@@ -40,3 +40,10 @@ it('batch selection spans pages, excludes protected resources, and can be cleare
expect(root.querySelectorAll('.media-recognition-row input:disabled:checked')).toHaveLength(0);
await click('清空选择');expect(root.textContent).toContain('已选 0 / 33 项');
});
it('does not decorate named season groups with 第 and 季',async()=>{
const named={...preview.items[0]!,after:{...preview.items[0]!.after,metadata:{...preview.items[0]!.after.metadata,season:'4K版'}}};
const request=vi.fn().mockResolvedValueOnce({...rule,rule:{...rule.rule,season:'4K版'}}).mockResolvedValueOnce({revision:'next'}).mockResolvedValueOnce({id:'named',items:[named]});
act(()=>render(<FolderRecognition api={{request} as unknown as MediaApi} libraryId="lib" path="Show" disabled={false} onBusy={vi.fn()} onApplied={vi.fn()}/>,root));
await click('识别规则与预览');await vi.waitFor(()=>expect(root.querySelector<HTMLButtonElement>('.media-primary')?.disabled).toBe(false));await click('保存并预览');
await vi.waitFor(()=>expect(root.textContent).toContain('4K版第 0 集'));expect(root.textContent).not.toContain('第 4K版 季');
});