mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-10 15:26:38 +08:00
fix(series): 彻底修复剧集被按单集显示且无法修复
根因(用真实数据确诊, 两个互相独立): - Web 端拆集: episode NFO 的 <uniqueid type=tmdb> 是【单集 episode id】、 <title> 是【单集名】, mergeEpisodeMetadata 却无条件覆盖了整剧 tm_db_id / original_name → 同剧每集 id/原名各不相同, 被 getSeriesKey 按 tmdb_id 拆成 N 张单集卡(实测「遮天」90 集 = 89 个不同 tmdb_id)。 - Emby 散装单集: 电影库混入按 Season/SxxE 整理的剧集结构内容, 被判成 Episode 却因 movie 库不聚 Series 而漏成散装单集。 - 无法修复: EnrichLibrary 只处理 pending/no_match, 跳过 matched 脏行。 修复: - local_metadata.mergeEpisodeMetadata: 单集 NFO 的外部 id 不再写入整剧字段, 单集名不再污染 original_name; 整剧 id/原名只认 tvshow.nfo。 - groupSeries.ts: 剧集分组改「库+路径剧名」优先(整剧目录对全剧一致), 外部 id 仅作回退, 对存量脏数据也能合并。 - emby_compat: 电影库常规浏览新增 movieLibraryItems, 把剧集结构内容聚成 Series 卡片与真电影并列(方案 B), 不再漏散装单集。 - cloud_path_repair: 「修复+重刮」先把脏的 matched 剧集行重置为 pending 再重刮, 使其能被重新刮削; 结果新增 reset 计数。 - 新增 NormalizePollutedEpisodeMetadata 启动迁移(幂等): 按整剧目录聚合, 清洗 tmdb/原名散乱的剧组并重置 pending。 测试: local_metadata/emby_compat/cloud_path_repair/episode_metadata_cleanup 新增或更新断言; 修正 scanner_cloud_test 中依赖旧错误行为的用例。
This commit is contained in:
@@ -61,7 +61,9 @@ func TestReadLocalEpisodeMetadataMergesShowAndEpisode(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(showDir, "tvshow.nfo"), []byte(`<tvshow><title>正确剧名</title><year>2024</year><tmdbid>123</tmdbid></tvshow>`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(nfoPath(mediaPath), []byte(`<episodedetails><title>第三集</title><season>2</season><episode>3</episode><plot>本集简介</plot></episodedetails>`), 0o644); err != nil {
|
||||
// 单集 NFO 携带【单集级】tmdb id(4375419)与单集名(第三集):二者都不得
|
||||
// 覆盖整剧字段,否则同剧各集 id/原名互不相同会被拆成多张卡。
|
||||
if err := os.WriteFile(nfoPath(mediaPath), []byte(`<episodedetails><title>第三集</title><season>2</season><episode>3</episode><plot>本集简介</plot><uniqueid type="tmdb">4375419</uniqueid></episodedetails>`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -69,9 +71,14 @@ func TestReadLocalEpisodeMetadataMergesShowAndEpisode(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got == nil || got.Title != "正确剧名" || got.OriginalName != "第三集" || got.SeasonNum != 2 || got.EpisodeNum != 3 {
|
||||
if got == nil || got.Title != "正确剧名" || got.SeasonNum != 2 || got.EpisodeNum != 3 {
|
||||
t.Fatalf("unexpected metadata: %+v", got)
|
||||
}
|
||||
// 单集名不得写入整剧原名(分组键)。
|
||||
if got.OriginalName != "" {
|
||||
t.Fatalf("episode title must not pollute OriginalName, got %q", got.OriginalName)
|
||||
}
|
||||
// 单集级简介按集回填;整剧 tmdb 仍取 tvshow.nfo 的 123,单集 id 不得覆盖。
|
||||
if got.Overview != "本集简介" || got.TMDbID != 123 {
|
||||
t.Fatalf("episode/show merge failed: %+v", got)
|
||||
}
|
||||
@@ -464,7 +471,9 @@ func TestScanLibraryUsesLocalMetadata(t *testing.T) {
|
||||
if err := db.First(&media, "path = ?", mediaPath).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if media.Title != "本地剧名" || media.OriginalName != "本地第三集" || media.SeasonNum != 2 || media.EpisodeNum != 3 || media.ScrapeStatus != "matched" {
|
||||
// 单集名(本地第三集)不应写入 OriginalName(整剧原名,合集分组键)。
|
||||
// tvshow.nfo 未提供 originaltitle, 故 OriginalName 应为空。
|
||||
if media.Title != "本地剧名" || media.OriginalName != "" || media.SeasonNum != 2 || media.EpisodeNum != 3 || media.ScrapeStatus != "matched" {
|
||||
t.Fatalf("unexpected scanned media: %+v", media)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user