mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-10 23:36:37 +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:
@@ -404,22 +404,33 @@ func mergeArtworkMetadata(meta *LocalMetadata, mediaPath, showBaseDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// mergeEpisodeMetadata 把单集 sidecar NFO(<episodedetails>)合并进整剧元数据
|
||||
// dst(通常来自 tvshow.nfo)。
|
||||
//
|
||||
// 关键约束:「整剧级」字段(Title/OriginalName/TMDbID/BangumiID/DoubanID/TheTVDBID)
|
||||
// 是合集分组键的依据,必须保证「同一部剧的各集一致」。而单集 NFO 里的
|
||||
// <uniqueid type="tmdb"> 是【单集 episode id】(如 4375419)、<title> 是【单集名】
|
||||
// (如「九龙拉棺」),都是单集级数据 —— 一旦写进整剧字段,同剧每集的 id/原名互不
|
||||
// 相同,会被前端 getSeriesKey / Emby seriesGroupsFromMedia 拆成多张卡(每集一卡)。
|
||||
// 因此:
|
||||
// - 单集外部 id(tmdb/bangumi/douban/thetvdb)一律【不写入】整剧外部 id;
|
||||
// 整剧 id 只认 tvshow.nfo(已在 dst);无则留空,交由路径剧名分组兜底。
|
||||
// - 单集名【不写入】OriginalName(整剧原名);整剧原名只来自 tvshow.nfo。
|
||||
// - 仅 overview/rating/剧照/季集号等【单集级】字段按集回填(不影响分组)。
|
||||
func mergeEpisodeMetadata(dst, episode *LocalMetadata, doc *nfoDocument) {
|
||||
showTitle := cleanXMLText(doc.ShowTitle)
|
||||
episodeTitle := cleanXMLText(doc.Title)
|
||||
// 整剧标题: 优先 <showtitle>(MoviePilot 在单集 NFO 里也会写整剧名);
|
||||
// 其次保留 dst 已有(来自 tvshow.nfo);最后才退而用单集名占位。
|
||||
if showTitle != "" {
|
||||
dst.Title = showTitle
|
||||
if episodeTitle != "" {
|
||||
dst.OriginalName = episodeTitle
|
||||
} else if dst.Title == "" {
|
||||
if episodeTitle := cleanXMLText(doc.Title); episodeTitle != "" {
|
||||
dst.Title = episodeTitle
|
||||
}
|
||||
} else if dst.Title != "" && episodeTitle != "" && episodeTitle != dst.Title {
|
||||
dst.OriginalName = episodeTitle
|
||||
} else if dst.Title == "" && episodeTitle != "" {
|
||||
dst.Title = episodeTitle
|
||||
}
|
||||
if dst.OriginalName == "" && episode.OriginalName != "" {
|
||||
dst.OriginalName = episode.OriginalName
|
||||
}
|
||||
// 注意: 不要把单集名 / 单集 originaltitle 写进 OriginalName(整剧原名,分组键)。
|
||||
|
||||
// 单集级展示字段: 每个媒体行本就对应一集,这些可安全按集回填。
|
||||
if episode.Year > 0 {
|
||||
dst.Year = episode.Year
|
||||
}
|
||||
@@ -435,31 +446,21 @@ func mergeEpisodeMetadata(dst, episode *LocalMetadata, doc *nfoDocument) {
|
||||
if episode.BackdropURL != "" {
|
||||
dst.BackdropURL = episode.BackdropURL
|
||||
}
|
||||
if episode.TMDbID > 0 {
|
||||
dst.TMDbID = episode.TMDbID
|
||||
}
|
||||
if episode.BangumiID > 0 {
|
||||
dst.BangumiID = episode.BangumiID
|
||||
}
|
||||
if episode.DoubanID != "" {
|
||||
dst.DoubanID = episode.DoubanID
|
||||
}
|
||||
if episode.TheTVDBID != "" {
|
||||
dst.TheTVDBID = episode.TheTVDBID
|
||||
}
|
||||
// 整剧外部 id: 单集 NFO 的 id 都是单集级,绝不写入整剧字段(见上方说明)。
|
||||
if episode.SeasonNum > 0 || episode.EpisodeNum > 0 {
|
||||
dst.SeasonNum = episode.SeasonNum
|
||||
}
|
||||
if episode.EpisodeNum > 0 {
|
||||
dst.EpisodeNum = episode.EpisodeNum
|
||||
}
|
||||
if episode.Genres != "" {
|
||||
// 题材/地区/语言为整剧级,单集 NFO 偶尔携带时仅在整剧未提供时回填。
|
||||
if dst.Genres == "" && episode.Genres != "" {
|
||||
dst.Genres = episode.Genres
|
||||
}
|
||||
if episode.Countries != "" {
|
||||
if dst.Countries == "" && episode.Countries != "" {
|
||||
dst.Countries = episode.Countries
|
||||
}
|
||||
if episode.Languages != "" {
|
||||
if dst.Languages == "" && episode.Languages != "" {
|
||||
dst.Languages = episode.Languages
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user