diff options
| author | Andrew Rabert <ar@nullsum.net> | 2018-12-27 18:27:57 -0500 |
|---|---|---|
| committer | Andrew Rabert <ar@nullsum.net> | 2018-12-27 18:27:57 -0500 |
| commit | a86b71899ec52c44ddc6c3018e8cc5e9d7ff4d62 (patch) | |
| tree | a74f6ea4a8abfa1664a605d31d48bc38245ccf58 /MediaBrowser.Controller/Entities/TV | |
| parent | 9bac3ac616b01f67db98381feb09d34ebe821f9a (diff) | |
Add GPL modules
Diffstat (limited to 'MediaBrowser.Controller/Entities/TV')
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Episode.cs | 374 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Season.cs | 273 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Series.cs | 540 |
3 files changed, 1187 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs new file mode 100644 index 0000000000..201579731e --- /dev/null +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -0,0 +1,374 @@ +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities.TV +{ + /// <summary> + /// Class Episode + /// </summary> + public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries + { + public Episode() + { + RemoteTrailers = EmptyMediaUrlArray; + LocalTrailerIds = new Guid[] {}; + RemoteTrailerIds = new Guid[] {}; + } + + public Guid[] LocalTrailerIds { get; set; } + public Guid[] RemoteTrailerIds { get; set; } + public MediaUrl[] RemoteTrailers { get; set; } + + /// <summary> + /// Gets the season in which it aired. + /// </summary> + /// <value>The aired season.</value> + public int? AirsBeforeSeasonNumber { get; set; } + public int? AirsAfterSeasonNumber { get; set; } + public int? AirsBeforeEpisodeNumber { get; set; } + + /// <summary> + /// This is the ending episode number for double episodes. + /// </summary> + /// <value>The index number.</value> + public int? IndexNumberEnd { get; set; } + + public string FindSeriesSortName() + { + var series = Series; + return series == null ? SeriesName : series.SortName; + } + + [IgnoreDataMember] + protected override bool SupportsOwnedItems + { + get + { + return IsStacked || MediaSourceCount > 1; + } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return true; } + } + + [IgnoreDataMember] + public int? AiredSeasonNumber + { + get + { + return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber; + } + } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return Series; + } + } + + [IgnoreDataMember] + public override Guid DisplayParentId + { + get + { + return SeasonId; + } + } + + [IgnoreDataMember] + protected override bool EnableDefaultVideoUserDataKeys + { + get + { + return false; + } + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + // hack for tv plugins + if (SourceType == SourceType.Channel) + { + return 0; + } + + double value = 16; + value /= 9; + + return value; + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var series = Series; + if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue) + { + var seriesUserDataKeys = series.GetUserDataKeys(); + var take = seriesUserDataKeys.Count; + if (seriesUserDataKeys.Count > 1) + { + take--; + } + list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); + } + + return list; + } + + /// <summary> + /// This Episode's Series Instance + /// </summary> + /// <value>The series.</value> + [IgnoreDataMember] + public Series Series + { + get + { + var seriesId = SeriesId; + if (seriesId.Equals(Guid.Empty)) { + seriesId = FindSeriesId(); + } + return !seriesId.Equals(Guid.Empty) ? (LibraryManager.GetItemById(seriesId) as Series) : null; + } + } + + [IgnoreDataMember] + public Season Season + { + get + { + var seasonId = SeasonId; + if (seasonId.Equals(Guid.Empty)) { + seasonId = FindSeasonId(); + } + return !seasonId.Equals(Guid.Empty) ? (LibraryManager.GetItemById(seasonId) as Season) : null; + } + } + + [IgnoreDataMember] + public bool IsInSeasonFolder + { + get + { + return FindParent<Season>() != null; + } + } + + [IgnoreDataMember] + public string SeriesPresentationUniqueKey { get; set; } + + [IgnoreDataMember] + public string SeriesName { get; set; } + + [IgnoreDataMember] + public string SeasonName { get; set; } + + public string FindSeriesPresentationUniqueKey() + { + var series = Series; + return series == null ? null : series.PresentationUniqueKey; + } + + public string FindSeasonName() + { + var season = Season; + + if (season == null) + { + if (ParentIndexNumber.HasValue) + { + return "Season " + ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture); + } + return "Season Unknown"; + } + + return season.Name; + } + + public string FindSeriesName() + { + var series = Series; + return series == null ? SeriesName : series.Name; + } + + public Guid FindSeasonId() + { + var season = FindParent<Season>(); + + // Episodes directly in series folder + if (season == null) + { + var series = Series; + + if (series != null && ParentIndexNumber.HasValue) + { + var findNumber = ParentIndexNumber.Value; + + season = series.Children + .OfType<Season>() + .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == findNumber); + } + } + + return season == null ? Guid.Empty : season.Id; + } + + /// <summary> + /// Creates the name of the sort. + /// </summary> + /// <returns>System.String.</returns> + protected override string CreateSortName() + { + return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ") : "") + + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; + } + + /// <summary> + /// Determines whether [contains episode number] [the specified number]. + /// </summary> + /// <param name="number">The number.</param> + /// <returns><c>true</c> if [contains episode number] [the specified number]; otherwise, <c>false</c>.</returns> + public bool ContainsEpisodeNumber(int number) + { + if (IndexNumber.HasValue) + { + if (IndexNumberEnd.HasValue) + { + return number >= IndexNumber.Value && number <= IndexNumberEnd.Value; + } + + return IndexNumber.Value == number; + } + + return false; + } + + [IgnoreDataMember] + public override bool SupportsRemoteImageDownloading + { + get + { + if (IsMissingEpisode) + { + return false; + } + + return true; + } + } + + [IgnoreDataMember] + public bool IsMissingEpisode + { + get + { + return LocationType == LocationType.Virtual; + } + } + + [IgnoreDataMember] + public Guid SeasonId { get; set; } + [IgnoreDataMember] + public Guid SeriesId { get; set; } + + public Guid FindSeriesId() + { + var series = FindParent<Series>(); + return series == null ? Guid.Empty : series.Id; + } + + public override IEnumerable<Guid> GetAncestorIds() + { + var list = base.GetAncestorIds().ToList(); + + var seasonId = SeasonId; + + if (!seasonId.Equals(Guid.Empty) && !list.Contains(seasonId)) + { + list.Add(seasonId); + } + + return list; + } + + public override IEnumerable<FileSystemMetadata> GetDeletePaths() + { + return new[] { + new FileSystemMetadata + { + FullName = Path, + IsDirectory = IsFolder + } + }.Concat(GetLocalMetadataFilesToDelete()); + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Series; + } + + public EpisodeInfo GetLookupInfo() + { + var id = GetItemLookupInfo<EpisodeInfo>(); + + var series = Series; + + if (series != null) + { + id.SeriesProviderIds = series.ProviderIds; + id.SeriesDisplayOrder = series.DisplayOrder; + } + + id.IsMissingEpisode = IsMissingEpisode; + id.IndexNumberEnd = IndexNumberEnd; + + return id; + } + + public override bool BeforeMetadataRefresh(bool replaceAllMetdata) + { + var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata); + + if (!IsLocked) + { + if (SourceType == SourceType.Library) + { + try + { + if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetdata)) + { + hasChanges = true; + } + } + catch (Exception ex) + { + Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString()); + } + } + } + + return hasChanges; + } + } +} diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs new file mode 100644 index 0000000000..b5f77df55a --- /dev/null +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -0,0 +1,273 @@ +using System; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Users; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities.TV +{ + /// <summary> + /// Class Season + /// </summary> + public class Season : Folder, IHasSeries, IHasLookupInfo<SeasonInfo> + { + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + public override Guid DisplayParentId + { + get { return SeriesId; } + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + double value = 2; + value /= 3; + + return value; + } + + public string FindSeriesSortName() + { + var series = Series; + return series == null ? SeriesName : series.SortName; + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var series = Series; + if (series != null) + { + list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000"))); + } + + return list; + } + + public override int GetChildCount(User user) + { + var result = GetChildren(user, true).Count; + + return result; + } + + /// <summary> + /// This Episode's Series Instance + /// </summary> + /// <value>The series.</value> + [IgnoreDataMember] + public Series Series + { + get + { + var seriesId = SeriesId; + if (seriesId.Equals(Guid.Empty)) { + seriesId = FindSeriesId(); + } + return !seriesId.Equals(Guid.Empty) ? (LibraryManager.GetItemById(seriesId) as Series) : null; + } + } + + [IgnoreDataMember] + public string SeriesPath + { + get + { + var series = Series; + + if (series != null) + { + return series.Path; + } + + return FileSystem.GetDirectoryName(Path); + } + } + + public override string CreatePresentationUniqueKey() + { + if (IndexNumber.HasValue) + { + var series = Series; + if (series != null) + { + return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000"); + } + } + + return base.CreatePresentationUniqueKey(); + } + + /// <summary> + /// Creates the name of the sort. + /// </summary> + /// <returns>System.String.</returns> + protected override string CreateSortName() + { + return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; + } + + protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query) + { + if (query.User == null) + { + return base.GetItemsInternal(query); + } + + var user = query.User; + + Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + var items = GetEpisodes(user, query.DtoOptions).Where(filter); + + return PostFilterAndSort(items, query, false); + } + + /// <summary> + /// Gets the episodes. + /// </summary> + public List<BaseItem> GetEpisodes(User user, DtoOptions options) + { + return GetEpisodes(Series, user, options); + } + + public List<BaseItem> GetEpisodes(Series series, User user, DtoOptions options) + { + return GetEpisodes(series, user, null, options); + } + + public List<BaseItem> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes, DtoOptions options) + { + return series.GetSeasonEpisodes(this, user, allSeriesEpisodes, options); + } + + public List<BaseItem> GetEpisodes() + { + return Series.GetSeasonEpisodes(this, null, null, new DtoOptions(true)); + } + + public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) + { + return GetEpisodes(user, new DtoOptions(true)); + } + + protected override bool GetBlockUnratedValue(UserPolicy config) + { + // Don't block. Let either the entire series rating or episode rating determine it + return false; + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Series; + } + + [IgnoreDataMember] + public string SeriesPresentationUniqueKey { get; set; } + + [IgnoreDataMember] + public string SeriesName { get; set; } + + [IgnoreDataMember] + public Guid SeriesId { get; set; } + + public string FindSeriesPresentationUniqueKey() + { + var series = Series; + return series == null ? null : series.PresentationUniqueKey; + } + + public string FindSeriesName() + { + var series = Series; + return series == null ? SeriesName : series.Name; + } + + public Guid FindSeriesId() + { + var series = FindParent<Series>(); + return series == null ? Guid.Empty: series.Id; + } + + /// <summary> + /// Gets the lookup information. + /// </summary> + /// <returns>SeasonInfo.</returns> + public SeasonInfo GetLookupInfo() + { + var id = GetItemLookupInfo<SeasonInfo>(); + + var series = Series; + + if (series != null) + { + id.SeriesProviderIds = series.ProviderIds; + } + + return id; + } + + /// <summary> + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// </summary> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + public override bool BeforeMetadataRefresh(bool replaceAllMetdata) + { + var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata); + + if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) + { + IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path); + + // If a change was made record it + if (IndexNumber.HasValue) + { + hasChanges = true; + } + } + + return hasChanges; + } + } +} diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs new file mode 100644 index 0000000000..88fde17608 --- /dev/null +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -0,0 +1,540 @@ +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Users; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities.TV +{ + /// <summary> + /// Class Series + /// </summary> + public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer + { + public Series() + { + RemoteTrailers = EmptyMediaUrlArray; + LocalTrailerIds = new Guid[] {}; + RemoteTrailerIds = new Guid[] {}; + AirDays = new DayOfWeek[] { }; + } + + public DayOfWeek[] AirDays { get; set; } + public string AirTime { get; set; } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return true; } + } + + public Guid[] LocalTrailerIds { get; set; } + public Guid[] RemoteTrailerIds { get; set; } + + public MediaUrl[] RemoteTrailers { get; set; } + + /// <summary> + /// airdate, dvd or absolute + /// </summary> + public string DisplayOrder { get; set; } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public SeriesStatus? Status { get; set; } + + public override double GetDefaultPrimaryImageAspectRatio() + { + double value = 2; + value /= 3; + + return value; + } + + public override string CreatePresentationUniqueKey() + { + if (LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping) + { + var userdatakeys = GetUserDataKeys(); + + if (userdatakeys.Count > 1) + { + return AddLibrariesToPresentationUniqueKey(userdatakeys[0]); + } + } + + return base.CreatePresentationUniqueKey(); + } + + private string AddLibrariesToPresentationUniqueKey(string key) + { + var lang = GetPreferredMetadataLanguage(); + if (!string.IsNullOrEmpty(lang)) + { + key += "-" + lang; + } + + var folders = LibraryManager.GetCollectionFolders(this) + .Select(i => i.Id.ToString("N")) + .ToArray(); + + if (folders.Length == 0) + { + return key; + } + + return key + "-" + string.Join("-", folders); + } + + private static string GetUniqueSeriesKey(BaseItem series) + { + return series.GetPresentationUniqueKey(); + } + + public override int GetChildCount(User user) + { + var seriesKey = GetUniqueSeriesKey(this); + + var result = LibraryManager.GetCount(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = null, + SeriesPresentationUniqueKey = seriesKey, + IncludeItemTypes = new[] { typeof(Season).Name }, + IsVirtualItem = false, + Limit = 0, + DtoOptions = new Dto.DtoOptions(false) + { + EnableImages = false + } + }); + + return result; + } + + public override int GetRecursiveChildCount(User user) + { + var seriesKey = GetUniqueSeriesKey(this); + + var query = new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = null, + SeriesPresentationUniqueKey = seriesKey, + DtoOptions = new Dto.DtoOptions(false) + { + EnableImages = false + } + }; + + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Episode).Name }; + } + query.IsVirtualItem = false; + query.Limit = 0; + var totalRecordCount = LibraryManager.GetCount(query); + + return totalRecordCount; + } + + /// <summary> + /// Gets the user data key. + /// </summary> + /// <returns>System.String.</returns> + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrEmpty(key)) + { + list.Insert(0, key); + } + + key = this.GetProviderId(MetadataProviders.Tvdb); + if (!string.IsNullOrEmpty(key)) + { + list.Insert(0, key); + } + + return list; + } + + public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) + { + return GetSeasons(user, new DtoOptions(true)); + } + + public List<BaseItem> GetSeasons(User user, DtoOptions options) + { + var query = new InternalItemsQuery(user) + { + DtoOptions = options + }; + + SetSeasonQueryOptions(query, user); + + return LibraryManager.GetItemList(query); + } + + private void SetSeasonQueryOptions(InternalItemsQuery query, User user) + { + var seriesKey = GetUniqueSeriesKey(this); + + query.AncestorWithPresentationUniqueKey = null; + query.SeriesPresentationUniqueKey = seriesKey; + query.IncludeItemTypes = new[] { typeof(Season).Name }; + query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(); + + if (user != null) + { + var config = user.Configuration; + + if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + } + } + + protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query) + { + var user = query.User; + + if (query.Recursive) + { + var seriesKey = GetUniqueSeriesKey(this); + + query.AncestorWithPresentationUniqueKey = null; + query.SeriesPresentationUniqueKey = seriesKey; + if (query.OrderBy.Length == 0) + { + query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(); + } + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }; + } + query.IsVirtualItem = false; + return LibraryManager.GetItemsResult(query); + } + + SetSeasonQueryOptions(query, user); + + return LibraryManager.GetItemsResult(query); + } + + public IEnumerable<BaseItem> GetEpisodes(User user, DtoOptions options) + { + var seriesKey = GetUniqueSeriesKey(this); + + var query = new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = null, + SeriesPresentationUniqueKey = seriesKey, + IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }, + OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(), + DtoOptions = options + }; + var config = user.Configuration; + if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + + var allItems = LibraryManager.GetItemList(query); + + var allSeriesEpisodes = allItems.OfType<Episode>().ToList(); + + var allEpisodes = allItems.OfType<Season>() + .SelectMany(i => i.GetEpisodes(this, user, allSeriesEpisodes, options)) + .Reverse(); + + // Specials could appear twice based on above - once in season 0, once in the aired season + // This depends on settings for that series + // When this happens, remove the duplicate from season 0 + + return allEpisodes.DistinctBy(i => i.Id).Reverse(); + } + + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) + { + // Refresh bottom up, children first, then the boxset + // By then hopefully the movies within will have Tmdb collection values + var items = GetRecursiveChildren(); + + var totalItems = items.Count; + var numComplete = 0; + + // Refresh seasons + foreach (var item in items) + { + if (!(item is Season)) + { + continue; + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (refreshOptions.RefreshItem(item)) + { + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); + } + + // Refresh episodes and other children + foreach (var item in items) + { + if ((item is Season)) + { + continue; + } + + cancellationToken.ThrowIfCancellationRequested(); + + var skipItem = false; + + var episode = item as Episode; + + if (episode != null + && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh + && !refreshOptions.ReplaceAllMetadata + && episode.IsMissingEpisode + && episode.LocationType == LocationType.Virtual + && episode.PremiereDate.HasValue + && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30) + { + skipItem = true; + } + + if (!skipItem) + { + if (refreshOptions.RefreshItem(item)) + { + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } + } + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); + } + + refreshOptions = new MetadataRefreshOptions(refreshOptions); + await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); + } + + public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options) + { + var queryFromSeries = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons; + + // add optimization when this setting is not enabled + var seriesKey = queryFromSeries ? + GetUniqueSeriesKey(this) : + GetUniqueSeriesKey(parentSeason); + + var query = new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey, + SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null, + IncludeItemTypes = new[] { typeof(Episode).Name }, + OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(), + DtoOptions = options + }; + if (user != null) + { + var config = user.Configuration; + if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + } + + var allItems = LibraryManager.GetItemList(query); + + return GetSeasonEpisodes(parentSeason, user, allItems, options); + } + + public List<BaseItem> GetSeasonEpisodes(Season parentSeason, User user, IEnumerable<BaseItem> allSeriesEpisodes, DtoOptions options) + { + if (allSeriesEpisodes == null) + { + return GetSeasonEpisodes(parentSeason, user, options); + } + + var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); + + var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; + + return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending).ToList(); + } + + /// <summary> + /// Filters the episodes by season. + /// </summary> + public static IEnumerable<BaseItem> FilterEpisodesBySeason(IEnumerable<BaseItem> episodes, Season parentSeason, bool includeSpecials) + { + var seasonNumber = parentSeason.IndexNumber; + var seasonPresentationKey = GetUniqueSeriesKey(parentSeason); + + var supportSpecialsInSeason = includeSpecials && seasonNumber.HasValue && seasonNumber.Value != 0; + + return episodes.Where(episode => + { + var episodeItem = (Episode)episode; + + var currentSeasonNumber = supportSpecialsInSeason ? episodeItem.AiredSeasonNumber : episode.ParentIndexNumber; + if (currentSeasonNumber.HasValue && seasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber.Value) + { + return true; + } + + if (!currentSeasonNumber.HasValue && !seasonNumber.HasValue && parentSeason.LocationType == LocationType.Virtual) + { + return true; + } + + var season = episodeItem.Season; + return season != null && string.Equals(GetUniqueSeriesKey(season), seasonPresentationKey, StringComparison.OrdinalIgnoreCase); + }); + } + + /// <summary> + /// Filters the episodes by season. + /// </summary> + public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials) + { + if (!includeSpecials || seasonNumber < 1) + { + return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber); + } + + return episodes.Where(i => + { + var episode = i; + + if (episode != null) + { + var currentSeasonNumber = episode.AiredSeasonNumber; + + return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber; + } + + return false; + }); + } + + + protected override bool GetBlockUnratedValue(UserPolicy config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Series); + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Series; + } + + public SeriesInfo GetLookupInfo() + { + var info = GetItemLookupInfo<SeriesInfo>(); + + return info; + } + + public override bool BeforeMetadataRefresh(bool replaceAllMetadata) + { + var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata); + + if (!ProductionYear.HasValue) + { + var info = LibraryManager.ParseName(Name); + + var yearInName = info.Year; + + if (yearInName.HasValue) + { + ProductionYear = yearInName; + hasChanges = true; + } + } + + return hasChanges; + } + + public override List<ExternalUrl> GetRelatedUrls() + { + var list = base.GetRelatedUrls(); + + var imdbId = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrEmpty(imdbId)) + { + list.Add(new ExternalUrl + { + Name = "Trakt", + Url = string.Format("https://trakt.tv/shows/{0}", imdbId) + }); + } + + return list; + } + + [IgnoreDataMember] + public override bool StopRefreshIfLocalMetadataFound + { + get + { + // Need people id's from internet metadata + return false; + } + } + } +} |
