Merge pull request #644 from thogil/split_series_missing_episodes_2

Missing episodes supports split series
This commit is contained in:
Luke 2013-12-30 07:41:20 -08:00
commit 83f1fec902

View File

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