diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 8663c0b4c..cc24cad5d 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -50,14 +50,20 @@ namespace MediaBrowser.Providers.TV .OfType() .ToList(); + var seriesGroups = from series in seriesList + let tvdbId = series.GetProviderId(MetadataProviders.Tvdb) + where !string.IsNullOrEmpty(tvdbId) + group series by tvdbId into g + select g; + + await new MissingEpisodeProvider(_logger, _config).Run(seriesGroups, cancellationToken).ConfigureAwait(false); + var numComplete = 0; foreach (var series in seriesList) { cancellationToken.ThrowIfCancellationRequested(); - await new MissingEpisodeProvider(_logger, _config).Run(series, cancellationToken).ConfigureAwait(false); - var episodes = series.RecursiveChildren .OfType() .ToList(); @@ -100,15 +106,17 @@ namespace MediaBrowser.Providers.TV _config = config; } - public async Task Run(Series series, CancellationToken cancellationToken) + public async Task Run(IEnumerable> series, CancellationToken cancellationToken) { - var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); - - // Can't proceed without a tvdb id - if (string.IsNullOrEmpty(tvdbId)) + foreach (var seriesGroup in series) { - return; + await Run(seriesGroup, cancellationToken).ConfigureAwait(false); } + } + + private async Task Run(IGrouping group, CancellationToken cancellationToken) + { + var tvdbId = group.Key; var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, tvdbId); @@ -142,33 +150,36 @@ namespace MediaBrowser.Providers.TV .Where(i => i.Item1 != -1 && i.Item2 != -1) .ToList(); - var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(series, episodeLookup, cancellationToken) + var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup, cancellationToken) .ConfigureAwait(false); - var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(series, episodeLookup, cancellationToken) + var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup, cancellationToken) .ConfigureAwait(false); var hasNewEpisodes = false; var hasNewSeasons = false; - if (series.ContainsEpisodesWithoutSeasonFolders) + foreach (var series in group.Where(s => s.ContainsEpisodesWithoutSeasonFolders)) { hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false); } if (_config.Configuration.EnableInternetProviders) { - hasNewEpisodes = await AddMissingEpisodes(series, seriesDataPath, episodeLookup, cancellationToken) + hasNewEpisodes = await AddMissingEpisodes(group, seriesDataPath, episodeLookup, cancellationToken) .ConfigureAwait(false); } if (hasNewSeasons || hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) { - await series.RefreshMetadata(cancellationToken, true) - .ConfigureAwait(false); + foreach (var series in group) + { + await series.RefreshMetadata(cancellationToken, true) + .ConfigureAwait(false); - await series.ValidateChildren(new Progress(), cancellationToken, true) - .ConfigureAwait(false); + await series.ValidateChildren(new Progress(), cancellationToken, true) + .ConfigureAwait(false); + } } } @@ -214,11 +225,9 @@ namespace MediaBrowser.Providers.TV /// The episode lookup. /// The cancellation token. /// Task. - private async Task AddMissingEpisodes(Series series, string seriesDataPath, IEnumerable> episodeLookup, CancellationToken cancellationToken) + private async Task AddMissingEpisodes(IEnumerable series, string seriesDataPath, IEnumerable> episodeLookup, CancellationToken cancellationToken) { - var existingEpisodes = series.RecursiveChildren - .OfType() - .ToList(); + var existingEpisodes = series.SelectMany(s => s.RecursiveChildren.OfType()).ToList(); var hasChanges = false; @@ -251,21 +260,23 @@ namespace MediaBrowser.Providers.TV } var now = DateTime.UtcNow; + var targetSeries = DetermineAppropriateSeries(series, tuple.Item1); + if (airDate.Value < now) { // tvdb has a lot of nearly blank episodes - _logger.Info("Creating virtual missing episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); + _logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2); - await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); + await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); hasChanges = true; } else if (airDate.Value > now) { // tvdb has a lot of nearly blank episodes - _logger.Info("Creating virtual unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); + _logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2); - await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); + await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); hasChanges = true; } @@ -274,14 +285,19 @@ namespace MediaBrowser.Providers.TV return hasChanges; } + private Series DetermineAppropriateSeries(IEnumerable series, int seasonNumber) + { + return series.FirstOrDefault(s => s.RecursiveChildren.OfType().Any(season => season.IndexNumber == seasonNumber)) ?? + series.FirstOrDefault(s => s.RecursiveChildren.OfType().Any(season => season.IndexNumber == 1)) ?? + series.OrderBy(s => s.RecursiveChildren.OfType().Select(season => season.IndexNumber).Min()).First(); + } + /// /// Removes the virtual entry after a corresponding physical version has been added /// - private async Task RemoveObsoleteOrMissingEpisodes(Series series, IEnumerable> episodeLookup, CancellationToken cancellationToken) + private async Task RemoveObsoleteOrMissingEpisodes(IEnumerable series, IEnumerable> episodeLookup, CancellationToken cancellationToken) { - var existingEpisodes = series.RecursiveChildren - .OfType() - .ToList(); + var existingEpisodes = series.SelectMany(s => s.RecursiveChildren.OfType()).ToList(); var physicalEpisodes = existingEpisodes .Where(i => i.LocationType != LocationType.Virtual) @@ -324,7 +340,7 @@ namespace MediaBrowser.Providers.TV foreach (var episodeToRemove in episodesToRemove) { - _logger.Info("Removing missing/unaired episode {0} {1}x{2}", series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber); + _logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber); await episodeToRemove.Parent.RemoveChild(episodeToRemove, cancellationToken).ConfigureAwait(false); @@ -341,11 +357,9 @@ namespace MediaBrowser.Providers.TV /// The episode lookup. /// The cancellation token. /// Task{System.Boolean}. - private async Task RemoveObsoleteOrMissingSeasons(Series series, IEnumerable> episodeLookup, CancellationToken cancellationToken) + private async Task RemoveObsoleteOrMissingSeasons(IEnumerable series, IEnumerable> episodeLookup, CancellationToken cancellationToken) { - var existingSeasons = series.Children - .OfType() - .ToList(); + var existingSeasons = series.SelectMany(s => s.Children.OfType()).ToList(); var physicalSeasons = existingSeasons .Where(i => i.LocationType != LocationType.Virtual) @@ -385,7 +399,7 @@ namespace MediaBrowser.Providers.TV foreach (var seasonToRemove in seasonsToRemove) { - _logger.Info("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber); + _logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber); await seasonToRemove.Parent.RemoveChild(seasonToRemove, cancellationToken).ConfigureAwait(false);