diff --git a/packages/obsidian/src/watchlist/build.ts b/packages/obsidian/src/watchlist/build.ts index e399f7b..bc78de9 100644 --- a/packages/obsidian/src/watchlist/build.ts +++ b/packages/obsidian/src/watchlist/build.ts @@ -18,6 +18,13 @@ function langName(details: TmdbDetail): string { return LANG_FALLBACK[code] ?? code; } +function prevCrewList(prev: Record, key: string): string[] { + return stripQuotes(prev[key]) + .split(/,\s*/) + .map(s => s.trim()) + .filter(Boolean); +} + function crewNames(crew: any[], jobs: Set): string[] { const seen = new Set(); const out: string[] = []; @@ -94,9 +101,14 @@ export function buildRecord(details: TmdbDetail, isMovie: boolean, prev: Record< const agg = details.aggregate_credits ?? {}; cast = (agg.cast ?? []).slice(0, 12).map((c: any) => c.name); const createdBy: string[] = (details.created_by ?? []).map((c: any) => c.name); - director = createdBy; // series: creators (latest-episode director needs extra call — Phase 3) - writer = createdBy; - producer = []; + // series: creators (latest-episode director needs extra call — Phase 3) + // prev user-set crew wins over created_by fallback until per-episode enrichment lands + const prevDirector = prevCrewList(prev, 'director'); + const prevWriter = prevCrewList(prev, 'writer'); + const prevProducer = prevCrewList(prev, 'producer'); + director = prevDirector.length ? prevDirector : createdBy; + writer = prevWriter.length ? prevWriter : createdBy; + producer = prevProducer.length ? prevProducer : []; title = details.name ?? ''; originalTitle = details.original_name ?? ''; mediaType = 'TV Series'; diff --git a/tests/watchlist-build.test.ts b/tests/watchlist-build.test.ts index 44d3780..3414565 100644 --- a/tests/watchlist-build.test.ts +++ b/tests/watchlist-build.test.ts @@ -111,6 +111,31 @@ describe('watch-status rule (TV)', () => { }); }); +describe('TV crew preservation', () => { + test('TV + prev crew present → prev wins over created_by', () => { + const prev = { + director: '"Aaron Moorhead, Justin Benson"', + writer: 'Eric Martin', + producer: '"Rachel Alter, Tommy Turtle"', + }; + const r = buildRecord(tvDetail, false, prev); + expect(r.director).toEqual(['Aaron Moorhead', 'Justin Benson']); + expect(r.writer).toEqual(['Eric Martin']); + expect(r.producer).toEqual(['Rachel Alter', 'Tommy Turtle']); + }); + test('TV + empty prev crew → falls back to created_by', () => { + const r = buildRecord(tvDetail, false, EMPTY_PREV); + expect(r.director).toEqual(['Michael Waldron']); + expect(r.writer).toEqual(['Michael Waldron']); + expect(r.producer).toEqual([]); + }); + test('Movie + prev crew present → prev ignored, TMDB credits win', () => { + const prev = { director: 'Someone Else' }; + const r = buildRecord(movieDetail, true, prev); + expect(r.director).toEqual(['Denis Villeneuve']); + }); +}); + describe('eng_name derivation', () => { test('non-Latin original → engName = localized title', () => { const jp = { ...movieDetail, title: 'A Silent Voice: The Movie', original_title: '映画 聲の形' };