From eac491fbd3951fa9533566f6d5b359e88f20010b Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 3 Feb 2025 16:56:03 -0500 Subject: [PATCH] Backport pull request #13218 from jellyfin/release-10.10.z Fix missing episode removal Original-merge: 4e28f4fe03467f35285a021d7fbab27c83c0cc41 Merged-by: crobibero Backported-by: Bond_009 --- .../TV/SeriesMetadataService.cs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index f4aede463e..284415dce6 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -140,38 +140,39 @@ namespace MediaBrowser.Providers.TV private void RemoveObsoleteEpisodes(Series series) { - var episodes = series.GetEpisodes(null, new DtoOptions(), true).OfType().ToList(); - var numberOfEpisodes = episodes.Count; - // TODO: O(n^2), but can it be done faster without overcomplicating it? - for (var i = 0; i < numberOfEpisodes; i++) + var episodesBySeason = series.GetEpisodes(null, new DtoOptions(), true) + .OfType() + .GroupBy(e => e.ParentIndexNumber) + .ToList(); + + foreach (var seasonEpisodes in episodesBySeason) { - var currentEpisode = episodes[i]; - // The outer loop only examines virtual episodes - if (!currentEpisode.IsVirtualItem) + List nonPhysicalEpisodes = []; + List physicalEpisodes = []; + foreach (var episode in seasonEpisodes) { - continue; - } + if (episode.IsVirtualItem || episode.IsMissingEpisode) + { + nonPhysicalEpisodes.Add(episode); + continue; + } - // Virtual episodes without an episode number are practically orphaned and should be deleted - if (!currentEpisode.IndexNumber.HasValue) - { - DeleteEpisode(currentEpisode); - continue; + physicalEpisodes.Add(episode); } - for (var j = i + 1; j < numberOfEpisodes; j++) + // Only consider non-physical episodes + foreach (var episode in nonPhysicalEpisodes) { - var comparisonEpisode = episodes[j]; - // The inner loop is only for "physical" episodes - if (comparisonEpisode.IsVirtualItem - || currentEpisode.ParentIndexNumber != comparisonEpisode.ParentIndexNumber - || !comparisonEpisode.ContainsEpisodeNumber(currentEpisode.IndexNumber.Value)) + // Episodes without an episode number are practically orphaned and should be deleted + // Episodes with a physical equivalent should be deleted (they are no longer missing) + var shouldKeep = episode.IndexNumber.HasValue && !physicalEpisodes.Any(e => e.ContainsEpisodeNumber(episode.IndexNumber.Value)); + + if (shouldKeep) { continue; } - DeleteEpisode(currentEpisode); - break; + DeleteEpisode(episode); } } }