fix: guard series metadata from episode identity pollution

This commit is contained in:
ShukeBta
2026-06-25 07:22:00 +08:00
parent 3419077b67
commit 087a00b5d0
8 changed files with 338 additions and 13 deletions
+85 -1
View File
@@ -93,6 +93,86 @@ func (s *ScannerService) cloudDirectoryMetadata(ctx context.Context, typ, displa
return meta
}
func cloudFileJSONIsEpisodeMetadata(seriesLike bool, parsedSeason, parsedEpisode int, meta *LocalMetadata) bool {
if !seriesLike || meta == nil {
return false
}
return parsedSeason > 0 ||
parsedEpisode > 0 ||
meta.SeasonNum > 0 ||
meta.EpisodeNum > 0 ||
strings.TrimSpace(meta.EpisodeTitle) != ""
}
func mergeCloudEpisodeMetadata(dst, episode *LocalMetadata) *LocalMetadata {
if episode == nil {
return dst
}
if dst == nil {
dst = &LocalMetadata{}
}
mergeCloudEpisodeIdentity(dst, episode)
mergeCloudEpisodeDisplay(dst, episode)
mergeCloudEpisodeNumbersAndTaxonomy(dst, episode)
dst.NSFW = dst.NSFW || episode.NSFW
dst.HasNFO = dst.HasNFO || episode.HasNFO
dst.HasArtwork = dst.HasArtwork || episode.HasArtwork
return dst
}
func mergeCloudEpisodeIdentity(dst, episode *LocalMetadata) {
showTitle := ""
if episode.EpisodeTitle != "" && episode.Title != "" && !strings.EqualFold(episode.Title, episode.EpisodeTitle) {
showTitle = episode.Title
}
if showTitle != "" {
dst.Title = showTitle
}
episodeTitle := strings.TrimSpace(episode.EpisodeTitle)
if episodeTitle == "" && (episode.SeasonNum > 0 || episode.EpisodeNum > 0) {
episodeTitle = strings.TrimSpace(episode.Title)
}
if episodeTitle != "" && !strings.EqualFold(episodeTitle, strings.TrimSpace(dst.Title)) {
dst.EpisodeTitle = episodeTitle
}
}
func mergeCloudEpisodeDisplay(dst, episode *LocalMetadata) {
if dst.Year == 0 && episode.Year > 0 {
dst.Year = episode.Year
}
if episode.Overview != "" {
dst.Overview = episode.Overview
}
if episode.Rating > 0 {
dst.Rating = episode.Rating
}
if episode.PosterURL != "" {
dst.PosterURL = episode.PosterURL
}
if episode.BackdropURL != "" {
dst.BackdropURL = episode.BackdropURL
}
}
func mergeCloudEpisodeNumbersAndTaxonomy(dst, episode *LocalMetadata) {
if episode.SeasonNum > 0 {
dst.SeasonNum = episode.SeasonNum
}
if episode.EpisodeNum > 0 {
dst.EpisodeNum = episode.EpisodeNum
}
if dst.Genres == "" && episode.Genres != "" {
dst.Genres = episode.Genres
}
if dst.Countries == "" && episode.Countries != "" {
dst.Countries = episode.Countries
}
if dst.Languages == "" && episode.Languages != "" {
dst.Languages = episode.Languages
}
}
func (s *ScannerService) cloudFileMetadata(ctx context.Context, typ, displayPath, fileName string, sidecars cloudSidecarSet, inherited *LocalMetadata, seriesLike bool) *LocalMetadata {
season, episode := ParseEpisode(displayPath)
seriesLike = seriesLike || season > 0 || episode > 0
@@ -121,7 +201,11 @@ func (s *ScannerService) cloudFileMetadata(ctx context.Context, typ, displayPath
continue
}
if local, err := s.readCloudJSONMetadata(ctx, typ, ref, sidecars); err == nil && local != nil {
meta = mergeCloudMetadata(meta, local)
if cloudFileJSONIsEpisodeMetadata(seriesLike, season, episode, local) {
meta = mergeCloudEpisodeMetadata(meta, local)
} else {
meta = mergeCloudMetadata(meta, local)
}
break
}
}