diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 218c9fa5b6..0d9e5ec6d1 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -149,16 +149,34 @@ namespace MediaBrowser.Controller.Entities.TV return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; } + private IEnumerable GetEpisodes() + { + var series = Series; + + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) + { + var seasonNumber = IndexNumber; + + if (seasonNumber.HasValue) + { + return series.RecursiveChildren.OfType() + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value); + } + } + + return Children.OfType(); + } + [IgnoreDataMember] public bool IsMissingSeason { - get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsMissingEpisode); } + get { return LocationType == Model.Entities.LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); } } [IgnoreDataMember] public bool IsUnaired { - get { return Children.OfType().All(i => i.IsUnaired); } + get { return GetEpisodes().All(i => i.IsUnaired); } } [IgnoreDataMember] @@ -170,7 +188,7 @@ namespace MediaBrowser.Controller.Entities.TV [IgnoreDataMember] public bool IsMissingOrVirtualUnaired { - get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } + get { return LocationType == Model.Entities.LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } } [IgnoreDataMember] diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 7ad564d0fe..055a6c101c 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; @@ -150,6 +151,12 @@ namespace MediaBrowser.Providers.TV .ConfigureAwait(false); var hasNewEpisodes = false; + var hasNewSeasons = false; + + if (series.ContainsEpisodesWithoutSeasonFolders) + { + hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false); + } if (_config.Configuration.EnableInternetProviders) { @@ -157,7 +164,7 @@ namespace MediaBrowser.Providers.TV .ConfigureAwait(false); } - if (hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) + if (hasNewSeasons || hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) { await series.RefreshMetadata(cancellationToken, true) .ConfigureAwait(false); @@ -167,6 +174,40 @@ namespace MediaBrowser.Providers.TV } } + /// + /// For series with episodes directly under the series folder, this adds dummy seasons to enable regular browsing and metadata + /// + /// + /// + /// + private async Task AddDummySeasonFolders(Series series, CancellationToken cancellationToken) + { + var existingEpisodes = series.RecursiveChildren + .OfType() + .ToList(); + + var hasChanges = false; + + // Loop through the unique season numbers + foreach (var seasonNumber in existingEpisodes.Select(i => i.ParentIndexNumber ?? -1) + .Where(i => i >= 0) + .Distinct() + .ToList()) + { + var hasSeason = series.Children.OfType() + .Any(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber); + + if (!hasSeason) + { + await AddSeason(series, seasonNumber, cancellationToken).ConfigureAwait(false); + + hasChanges = true; + } + } + + return hasChanges; + } + /// /// Adds the missing episodes. /// @@ -355,7 +396,7 @@ namespace MediaBrowser.Providers.TV return hasChanges; } - + /// /// Adds the episode. ///