diff options
Diffstat (limited to 'MediaBrowser.Controller')
48 files changed, 965 insertions, 712 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 929308ba0..c34a884ff 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Entities.Audio IArchivable { public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - + public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } @@ -40,12 +40,6 @@ namespace MediaBrowser.Controller.Entities.Audio public List<string> AlbumArtists { get; set; } - /// <summary> - /// Gets or sets the album. - /// </summary> - /// <value>The album.</value> - public string Album { get; set; } - [IgnoreDataMember] public bool IsThemeMedia { @@ -150,12 +144,10 @@ namespace MediaBrowser.Controller.Entities.Audio + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { + var list = base.GetUserDataKeys(); + if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys) { var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty; @@ -165,7 +157,7 @@ namespace MediaBrowser.Controller.Entities.Audio { songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; } - songKey+= Name; + songKey += Name; if (!string.IsNullOrWhiteSpace(Album)) { @@ -178,25 +170,25 @@ namespace MediaBrowser.Controller.Entities.Audio songKey = albumArtist + "-" + songKey; } - return songKey; + list.Insert(0, songKey); } - - var parent = AlbumEntity; - - if (parent != null) + else { - var parentKey = parent.GetUserDataKey(); + var parent = AlbumEntity; - if (IndexNumber.HasValue) + if (parent != null && IndexNumber.HasValue) { - var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "") - + IndexNumber.Value.ToString("0000 - "); + list.InsertRange(0, parent.GetUserDataKeys().Select(i => + { + var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "") + + IndexNumber.Value.ToString("0000 - "); - return parentKey + songKey; + return i + songKey; + })); } } - return base.CreateUserDataKey(); + return list; } public override UnratedItem GetBlockUnratedType() diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index e6178c183..615276e83 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -49,6 +49,15 @@ namespace MediaBrowser.Controller.Entities.Audio } [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] public List<string> AllArtists { get @@ -96,36 +105,34 @@ namespace MediaBrowser.Controller.Entities.Audio public List<string> Artists { get; set; } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + var list = base.GetUserDataKeys(); - if (!string.IsNullOrWhiteSpace(id)) + if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys) { - return "MusicAlbum-MusicBrainzReleaseGroup-" + id; + var albumArtist = AlbumArtist; + if (!string.IsNullOrWhiteSpace(albumArtist)) + { + list.Insert(0, albumArtist + "-" + Name); + } } - id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum); + var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum); if (!string.IsNullOrWhiteSpace(id)) { - return "MusicAlbum-Musicbrainz-" + id; + list.Insert(0, "MusicAlbum-Musicbrainz-" + id); } - if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys) + id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (!string.IsNullOrWhiteSpace(id)) { - var albumArtist = AlbumArtist; - if (!string.IsNullOrWhiteSpace(albumArtist)) - { - return albumArtist + "-" + Name; - } + list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id); } - return base.CreateUserDataKey(); + return list; } protected override bool GetBlockUnratedValue(UserPolicy config) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 02bcceada..fb8a24061 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -17,7 +17,12 @@ namespace MediaBrowser.Controller.Entities.Audio /// </summary> public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo<ArtistInfo> { - public bool IsAccessedByName { get; set; } + [IgnoreDataMember] + public bool IsAccessedByName + { + get { return ParentId == Guid.Empty; } + } + public List<string> ProductionLocations { get; set; } [IgnoreDataMember] @@ -30,6 +35,15 @@ namespace MediaBrowser.Controller.Entities.Audio } [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] public override bool SupportsAddingToPlaylist { get { return true; } @@ -40,6 +54,40 @@ namespace MediaBrowser.Controller.Entities.Audio return !IsAccessedByName; } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + var itemByNameFilter = GetItemFilter(); + + if (query.User != null) + { + return query.User.RootFolder + .GetRecursiveChildren(query.User, i => + { + if (query.IsFolder.HasValue) + { + if (query.IsFolder.Value != i.IsFolder) + { + return false; + } + } + return itemByNameFilter(i); + }); + } + + return LibraryManager.RootFolder + .GetRecursiveChildren(i => + { + if (query.IsFolder.HasValue) + { + if (query.IsFolder.Value != i.IsFolder) + { + return false; + } + } + return itemByNameFilter(i); + }); + } + protected override IEnumerable<BaseItem> ActualChildren { get @@ -80,13 +128,12 @@ namespace MediaBrowser.Controller.Entities.Audio ProductionLocations = new List<string>(); } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return GetUserDataKey(this); + var list = base.GetUserDataKeys(); + + list.InsertRange(0, GetUserDataKeys(this)); + return list; } /// <summary> @@ -121,16 +168,18 @@ namespace MediaBrowser.Controller.Entities.Audio /// </summary> /// <param name="item">The item.</param> /// <returns>System.String.</returns> - private static string GetUserDataKey(MusicArtist item) + private static List<string> GetUserDataKeys(MusicArtist item) { + var list = new List<string>(); var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist); if (!string.IsNullOrEmpty(id)) { - return "Artist-Musicbrainz-" + id; + list.Add("Artist-Musicbrainz-" + id); } - return "Artist-" + item.Name; + list.Add("Artist-" + item.Name); + return list; } protected override bool GetBlockUnratedValue(UserPolicy config) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 45304d47e..77cf0cc49 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities.Audio /// </summary> public class MusicGenre : BaseItem, IItemByName { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "MusicGenre-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "MusicGenre-" + Name); + return list; } [IgnoreDataMember] @@ -80,5 +79,13 @@ namespace MediaBrowser.Controller.Entities.Audio return false; } } + + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.Genres = new[] { Name }; + query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name }; + + return LibraryManager.GetItemList(query); + } } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 475e2ace8..61060766f 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -44,6 +44,9 @@ namespace MediaBrowser.Controller.Entities ImageInfos = new List<ItemImageInfo>(); } + public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; + public static char SlugChar = '-'; + /// <summary> /// The supported image extensions /// </summary> @@ -67,6 +70,12 @@ namespace MediaBrowser.Controller.Entities public List<ItemImageInfo> ImageInfos { get; set; } /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + public string Album { get; set; } + + /// <summary> /// Gets or sets the channel identifier. /// </summary> /// <value>The channel identifier.</value> @@ -125,6 +134,29 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public string SlugName + { + get + { + var name = Name; + if (string.IsNullOrWhiteSpace(name)) + { + return string.Empty; + } + + return SlugReplaceChars.Aggregate(name, (current, c) => current.Replace(c, SlugChar)); + } + } + + [IgnoreDataMember] + public bool IsUnaired + { + get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; } + } + + public string OriginalTitle { get; set; } + /// <summary> /// Gets or sets the id. /// </summary> @@ -726,12 +758,14 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the critic rating. /// </summary> /// <value>The critic rating.</value> + [IgnoreDataMember] public float? CriticRating { get; set; } /// <summary> /// Gets or sets the critic rating summary. /// </summary> /// <value>The critic rating summary.</value> + [IgnoreDataMember] public string CriticRatingSummary { get; set; } /// <summary> @@ -1147,33 +1181,31 @@ namespace MediaBrowser.Controller.Entities get { return null; } } - private string _userDataKey; - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - public string GetUserDataKey() + [IgnoreDataMember] + public virtual string PresentationUniqueKey { - if (string.IsNullOrWhiteSpace(_userDataKey)) - { - var key = CreateUserDataKey(); - _userDataKey = key; - return key; - } + get { return Id.ToString("N"); } + } - return _userDataKey; + public virtual bool RequiresRefresh() + { + return false; } - protected virtual string CreateUserDataKey() + public virtual List<string> GetUserDataKeys() { + var list = new List<string>(); + if (SourceType == SourceType.Channel) { if (!string.IsNullOrWhiteSpace(ExternalId)) { - return ExternalId; + list.Add(ExternalId); } } - return Id.ToString(); + + list.Add(Id.ToString()); + return list; } internal virtual bool IsValidFromResolver(BaseItem newItem) @@ -1186,7 +1218,6 @@ namespace MediaBrowser.Controller.Entities public void AfterMetadataRefresh() { _sortName = null; - _userDataKey = null; } /// <summary> @@ -1540,11 +1571,11 @@ namespace MediaBrowser.Controller.Entities { if (!string.IsNullOrEmpty(info.Path)) { - var itemByPath = LibraryManager.FindByPath(info.Path); + var itemByPath = LibraryManager.FindByPath(info.Path, null); if (itemByPath == null) { - Logger.Warn("Unable to find linked item at path {0}", info.Path); + //Logger.Warn("Unable to find linked item at path {0}", info.Path); } return itemByPath; @@ -1553,6 +1584,15 @@ namespace MediaBrowser.Controller.Entities return null; } + [IgnoreDataMember] + public virtual bool EnableRememberingTrackSelections + { + get + { + return true; + } + } + /// <summary> /// Adds a studio to the item /// </summary> @@ -1606,9 +1646,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException(); } - var key = GetUserDataKey(); - - var data = UserDataManager.GetUserData(user.Id, key); + var data = UserDataManager.GetUserData(user, this); if (datePlayed.HasValue) { @@ -1643,9 +1681,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException(); } - var key = GetUserDataKey(); - - var data = UserDataManager.GetUserData(user.Id, key); + var data = UserDataManager.GetUserData(user, this); //I think it is okay to do this here. // if this is only called when a user is manually forcing something to un-played @@ -1976,14 +2012,14 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsPlayed(User user) { - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata != null && userdata.Played; } public bool IsFavoriteOrLiked(User user) { - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false)); } @@ -1995,7 +2031,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException("user"); } - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata == null || !userdata.Played; } @@ -2026,7 +2062,6 @@ namespace MediaBrowser.Controller.Entities /// </summary> public virtual bool BeforeMetadataRefresh() { - _userDataKey = null; _sortName = null; var hasChanges = false; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 429700327..5e0cf6e88 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -8,6 +8,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MoreLinq; namespace MediaBrowser.Controller.Entities { @@ -97,7 +98,6 @@ namespace MediaBrowser.Controller.Entities } } - return base.IsValidFromResolver(newItem); } @@ -200,9 +200,30 @@ namespace MediaBrowser.Controller.Entities public IEnumerable<Folder> GetPhysicalParents() { - return LibraryManager.RootFolder.Children + var rootChildren = LibraryManager.RootFolder.Children .OfType<Folder>() - .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)); + .ToList(); + + return PhysicalLocations.Where(i => !string.Equals(i, Path, StringComparison.OrdinalIgnoreCase)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id); + } + + private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren) + { + var result = rootChildren + .Where(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (result.Count == 0) + { + var folder = LibraryManager.FindByPath(path, true) as Folder; + + if (folder != null) + { + result.Add(folder); + } + } + + return result; } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index f4cdc8fa1..77e362419 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -28,6 +28,9 @@ namespace MediaBrowser.Controller.Entities public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } + [IgnoreDataMember] + public DateTime? DateLastMediaAdded { get; set; } + public Folder() { LinkedChildren = new List<LinkedChild>(); @@ -56,6 +59,36 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public virtual bool SupportsCumulativeRunTimeTicks + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsDateLastMediaAdded + { + get + { + return false; + } + } + + public override bool RequiresRefresh() + { + var baseResult = base.RequiresRefresh(); + + if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue) + { + baseResult = true; + } + + return baseResult; + } + + [IgnoreDataMember] public override string FileNameWithoutExtension { get @@ -199,8 +232,8 @@ namespace MediaBrowser.Controller.Entities /// <returns>Dictionary{System.StringFunc{UserIEnumerable{BaseItem}}}.</returns> protected virtual IEnumerable<string> GetIndexByOptions() { - return new List<string> { - {"None"}, + return new List<string> { + {"None"}, {"Performer"}, {"Genre"}, {"Director"}, @@ -707,8 +740,8 @@ namespace MediaBrowser.Controller.Entities { return ItemRepository.GetItemIdsList(new InternalItemsQuery { - ParentId = Id - + ParentId = Id, + GroupByPresentationUniqueKey = false }); } @@ -751,39 +784,44 @@ namespace MediaBrowser.Controller.Entities return true; } } + + var supportsUserDataQueries = ConfigurationManager.Configuration.SchemaVersion >= 76; if (query.SortBy != null && query.SortBy.Length > 0) { - if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.DatePlayed"); - return true; - } - if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked"); - return true; - } - if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)) + if (!supportsUserDataQueries) { - Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed"); - return true; - } - if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed"); - return true; + if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed"); + return true; + } } if (query.SortBy.Contains(ItemSortBy.AiredEpisodeOrder, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder"); return true; } - if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.Album"); - return true; - } if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist"); @@ -799,11 +837,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget"); return true; } - if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded"); - return true; - } if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem"); @@ -819,11 +852,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.OfficialRating"); return true; } - if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount"); - return true; - } if (query.SortBy.Contains(ItemSortBy.Players, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.Players"); @@ -839,11 +867,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName"); return true; } - if (query.SortBy.Contains(ItemSortBy.StartDate, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.StartDate"); - return true; - } if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio"); @@ -868,34 +891,37 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.IsLiked.HasValue) + if (!supportsUserDataQueries) { - Logger.Debug("Query requires post-filtering due to IsLiked"); - return true; - } + if (query.IsLiked.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsLiked"); + return true; + } - if (query.IsFavoriteOrLiked.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked"); - return true; - } + if (query.IsFavoriteOrLiked.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked"); + return true; + } - if (query.IsFavorite.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsFavorite"); - return true; - } + if (query.IsFavorite.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsFavorite"); + return true; + } - if (query.IsResumable.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsResumable"); - return true; - } + if (query.IsResumable.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsResumable"); + return true; + } - if (query.IsPlayed.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsPlayed"); - return true; + if (query.IsPlayed.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsPlayed"); + return true; + } } if (query.IsInBoxSet.HasValue) @@ -1059,30 +1085,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (!string.IsNullOrWhiteSpace(query.NameContains)) - { - Logger.Debug("Query requires post-filtering due to NameContains"); - return true; - } - - if (!string.IsNullOrWhiteSpace(query.NameLessThan)) - { - Logger.Debug("Query requires post-filtering due to NameLessThan"); - return true; - } - - if (!string.IsNullOrWhiteSpace(query.NameStartsWith)) - { - Logger.Debug("Query requires post-filtering due to NameStartsWith"); - return true; - } - - if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater)) - { - Logger.Debug("Query requires post-filtering due to NameStartsWithOrGreater"); - return true; - } - if (query.AirDays.Length > 0) { Logger.Debug("Query requires post-filtering due to AirDays"); @@ -1107,12 +1109,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AlbumNames.Length > 0) - { - Logger.Debug("Query requires post-filtering due to AlbumNames"); - return true; - } - if (query.ArtistNames.Length > 0) { Logger.Debug("Query requires post-filtering due to ArtistNames"); @@ -1297,33 +1293,42 @@ namespace MediaBrowser.Controller.Entities public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter) { - var list = new List<BaseItem>(); + var result = new Dictionary<Guid, BaseItem>(); - AddChildrenToList(list, true, filter); + AddChildrenToList(result, true, true, filter); - return list; + return result.Values.ToList(); } /// <summary> /// Adds the children to list. /// </summary> - /// <param name="list">The list.</param> - /// <param name="recursive">if set to <c>true</c> [recursive].</param> - /// <param name="filter">The filter.</param> - private void AddChildrenToList(List<BaseItem> list, bool recursive, Func<BaseItem, bool> filter) + private void AddChildrenToList(Dictionary<Guid,BaseItem> result, bool includeLinkedChildren, bool recursive, Func<BaseItem, bool> filter) { foreach (var child in Children) { if (filter == null || filter(child)) { - list.Add(child); + result[child.Id] = child; } if (recursive && child.IsFolder) { var folder = (Folder)child; - folder.AddChildrenToList(list, true, filter); + // We can only support includeLinkedChildren for the first folder, or we might end up stuck in a loop of linked items + folder.AddChildrenToList(result, false, true, filter); + } + } + + if (includeLinkedChildren) + { + foreach (var child in GetLinkedChildren()) + { + if (filter == null || filter(child)) + { + result[child.Id] = child; + } } } } @@ -1567,38 +1572,17 @@ namespace MediaBrowser.Controller.Entities await Task.WhenAll(tasks).ConfigureAwait(false); } - /// <summary> - /// Finds an item by path, recursively - /// </summary> - /// <param name="path">The path.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public BaseItem FindByPath(string path) + public override bool IsPlayed(User user) { - if (string.IsNullOrEmpty(path)) + var itemsResult = GetItems(new InternalItemsQuery(user) { - throw new ArgumentNullException(); - } - - if (string.Equals(Path, path, StringComparison.OrdinalIgnoreCase)) - { - return this; - } - - if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) - { - return this; - } + Recursive = true, + IsFolder = false, + ExcludeLocationTypes = new[] { LocationType.Virtual } - return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) || - (!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) || - i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) - .FirstOrDefault(); - } + }).Result; - public override bool IsPlayed(User user) - { - return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual) + return itemsResult.Items .All(i => i.IsPlayed(user)); } @@ -1607,26 +1591,50 @@ namespace MediaBrowser.Controller.Entities return !IsPlayed(user); } + [IgnoreDataMember] + public virtual bool SupportsUserDataFromChildren + { + get + { + // These are just far too slow. + if (this is ICollectionFolder) + { + return false; + } + if (this is UserView) + { + return false; + } + if (this is UserRootFolder) + { + return false; + } + + return true; + } + } + public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) { + if (!SupportsUserDataFromChildren) + { + return; + } + var recursiveItemCount = 0; var unplayed = 0; double totalPercentPlayed = 0; - IEnumerable<BaseItem> children; - var folder = this; + var itemsResult = GetItems(new InternalItemsQuery(user) + { + Recursive = true, + IsFolder = false, + ExcludeLocationTypes = new[] { LocationType.Virtual } - var season = folder as Season; + }).Result; - if (season != null) - { - children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual); - } - else - { - children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual); - } + var children = itemsResult.Items; // Loop through each recursive child foreach (var child in children) @@ -1635,7 +1643,7 @@ namespace MediaBrowser.Controller.Entities var isUnplayed = true; - var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); + var itemUserData = UserDataManager.GetUserData(user, child); // Incrememt totalPercentPlayed if (itemUserData != null) diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index e597b2a15..9ed240046 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -76,15 +76,16 @@ namespace MediaBrowser.Controller.Entities /// </summary> public List<string> MultiPartGameFiles { get; set; } - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { + var list = base.GetUserDataKeys(); var id = this.GetProviderId(MetadataProviders.Gamesdb); if (!string.IsNullOrEmpty(id)) { - return "Game-Gamesdb-" + id; + list.Insert(0, "Game-Gamesdb-" + id); } - return base.CreateUserDataKey(); + return list; } public override IEnumerable<string> GetDeletePaths() diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index d2b6b4856..7c1e88cb1 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -7,13 +7,12 @@ namespace MediaBrowser.Controller.Entities { public class GameGenre : BaseItem, IItemByName { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "GameGenre-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "GameGenre-" + Name); + return list; } /// <summary> @@ -63,6 +62,14 @@ namespace MediaBrowser.Controller.Entities return i => i is Game && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.Genres = new[] { Name }; + query.IncludeItemTypes = new[] { typeof(Game).Name }; + + return LibraryManager.GetItemList(query); + } + [IgnoreDataMember] public override bool SupportsPeople { diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index bc35c4738..1c09ee507 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using System; +using System.Collections.Generic; using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities @@ -31,17 +32,15 @@ namespace MediaBrowser.Controller.Entities /// <value>The game system.</value> public string GameSystemName { get; set; } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { + var list = base.GetUserDataKeys(); + if (!string.IsNullOrEmpty(GameSystemName)) { - return "GameSystem-" + GameSystemName; + list.Insert(0, "GameSystem-" + GameSystemName); } - return base.CreateUserDataKey(); + return list; } protected override bool GetBlockUnratedValue(UserPolicy config) diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 233e1e0fd..c87d4daaf 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Genre : BaseItem, IItemByName { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "Genre-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "Genre-" + Name); + return list; } /// <summary> @@ -67,6 +66,14 @@ namespace MediaBrowser.Controller.Entities return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.Genres = new[] { Name }; + query.ExcludeItemTypes = new[] { typeof(Game).Name, typeof(MusicVideo).Name, typeof(Audio.Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name }; + + return LibraryManager.GetItemList(query); + } + [IgnoreDataMember] public override bool SupportsPeople { diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 1f680b35f..c7940c8a9 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -49,5 +49,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value><c>true</c> if [supports people]; otherwise, <c>false</c>.</value> bool SupportsPeople { get; } + + bool RequiresRefresh(); } } diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs index 34a820853..244b319bd 100644 --- a/MediaBrowser.Controller/Entities/IHasUserData.cs +++ b/MediaBrowser.Controller/Entities/IHasUserData.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dto; +using System.Collections.Generic; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Entities { @@ -7,11 +8,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> public interface IHasUserData : IHasId { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - string GetUserDataKey(); + List<string> GetUserDataKeys(); /// <summary> /// Fills the user data dto values. @@ -20,5 +17,7 @@ namespace MediaBrowser.Controller.Entities /// <param name="userData">The user data.</param> /// <param name="user">The user.</param> void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user); + + bool EnableRememberingTrackSelections { get; } } } diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs index e6667290c..7747e738c 100644 --- a/MediaBrowser.Controller/Entities/IItemByName.cs +++ b/MediaBrowser.Controller/Entities/IItemByName.cs @@ -20,6 +20,8 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <returns>Func<BaseItem, System.Boolean>.</returns> Func<BaseItem, bool> GetItemFilter(); + + IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query); } public interface IHasDualAccess : IItemByName diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index f3b4f4053..5236b0a27 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -46,8 +46,11 @@ namespace MediaBrowser.Controller.Entities public string NameLessThan { get; set; } public string NameContains { get; set; } + public string PresentationUniqueKey { get; set; } public string Path { get; set; } - + public string Name { get; set; } + public string SlugName { get; set; } + public string Person { get; set; } public string[] PersonIds { get; set; } public string[] ItemIds { get; set; } @@ -106,6 +109,7 @@ namespace MediaBrowser.Controller.Entities internal List<Guid> ItemIdsFromPersonFilters { get; set; } public int? ParentIndexNumber { get; set; } + public int? IndexNumber { get; set; } public int? MinParentalRating { get; set; } public int? MaxParentalRating { get; set; } @@ -130,9 +134,16 @@ namespace MediaBrowser.Controller.Entities public string[] AlbumNames { get; set; } public string[] ArtistNames { get; set; } - + public string AncestorWithPresentationUniqueKey { get; set; } + + public bool GroupByPresentationUniqueKey { get; set; } + public bool EnableTotalRecordCount { get; set; } + public InternalItemsQuery() { + GroupByPresentationUniqueKey = true; + EnableTotalRecordCount = true; + AlbumNames = new string[] { }; ArtistNames = new string[] { }; diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index cd3e07ea3..09a9d97bc 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Controller.Entities.Movies { @@ -118,7 +119,7 @@ namespace MediaBrowser.Controller.Entities.Movies // Gather all possible ratings var ratings = GetRecursiveChildren() .Concat(GetLinkedChildren()) - .Where(i => i is Movie || i is Series) + .Where(i => i is Movie || i is Series || i is MusicAlbum || i is Game) .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrEmpty(i)) .Distinct(StringComparer.OrdinalIgnoreCase) diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 4d2ca9ffe..5882b5f4d 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -18,8 +18,6 @@ namespace MediaBrowser.Controller.Entities.Movies { public List<Guid> SpecialFeatureIds { get; set; } - public string OriginalTitle { get; set; } - public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } public List<string> ProductionLocations { get; set; } @@ -77,34 +75,6 @@ namespace MediaBrowser.Controller.Entities.Movies get { return TmdbCollectionName; } set { TmdbCollectionName = value; } } - - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() - { - var key = GetMovieUserDataKey(this); - - if (string.IsNullOrWhiteSpace(key)) - { - key = base.CreateUserDataKey(); - } - - return key; - } - - public static string GetMovieUserDataKey(BaseItem movie) - { - var key = movie.GetProviderId(MetadataProviders.Tmdb); - - if (string.IsNullOrWhiteSpace(key)) - { - key = movie.GetProviderId(MetadataProviders.Imdb); - } - - return key; - } protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index b52f16a46..7119828e2 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Entities public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo<MusicVideoInfo> { /// <summary> - /// Gets or sets the album. - /// </summary> - /// <value>The album.</value> - public string Album { get; set; } - - /// <summary> /// Gets or sets the budget. /// </summary> /// <value>The budget.</value> @@ -44,15 +38,6 @@ namespace MediaBrowser.Controller.Entities } } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() - { - return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey(); - } - public override UnratedItem GetBlockUnratedType() { return UnratedItem.Music; diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 560ea6e05..2b099e3d5 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -18,13 +18,12 @@ namespace MediaBrowser.Controller.Entities /// <value>The place of birth.</value> public string PlaceOfBirth { get; set; } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "Person-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "Person-" + Name); + return list; } public PersonLookupInfo GetLookupInfo() @@ -32,6 +31,13 @@ namespace MediaBrowser.Controller.Entities return GetItemLookupInfo<PersonLookupInfo>(); } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.Person = Name; + + return LibraryManager.GetItemList(query); + } + /// <summary> /// Returns the folder containing the item. /// If the item is a folder, it returns the folder itself diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index c8ab67a69..b0ddcfb8c 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -8,15 +8,6 @@ namespace MediaBrowser.Controller.Entities public class PhotoAlbum : Folder { [IgnoreDataMember] - public override bool SupportsLocalMetadata - { - get - { - return false; - } - } - - [IgnoreDataMember] public override bool AlwaysScanInternalMetadataPath { get diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index a55527f37..48ca7bbcc 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Studio : BaseItem, IItemByName, IHasTags { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "Studio-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "Studio-" + Name); + return list; } /// <summary> @@ -66,6 +65,13 @@ namespace MediaBrowser.Controller.Entities return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase); } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.Studios = new[] { Name }; + + return LibraryManager.GetItemList(query); + } + [IgnoreDataMember] public override bool SupportsPeople { diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index e1a91086b..a4b0b3082 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -58,60 +58,48 @@ namespace MediaBrowser.Controller.Entities.TV { get { - return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? PhysicalSeasonNumber; + return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber; } } [IgnoreDataMember] - public int? PhysicalSeasonNumber + public override Folder LatestItemsIndexContainer { get { - var value = ParentIndexNumber; - - if (value.HasValue) - { - return value; - } - - var season = Season; - - return season != null ? season.IndexNumber : null; + return Series; } } [IgnoreDataMember] - public override Folder LatestItemsIndexContainer + public override Guid? DisplayParentId { get { - return Series; + return SeasonId; } } [IgnoreDataMember] - public override Guid? DisplayParentId + protected override bool EnableDefaultVideoUserDataKeys { get { - return SeasonId; + return false; } } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - var series = Series; + var list = base.GetUserDataKeys(); + var series = Series; if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue) { - return series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"); + list.InsertRange(0, series.GetUserDataKeys().Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); } - return base.CreateUserDataKey(); + return list; } /// <summary> @@ -223,12 +211,6 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] - public bool IsUnaired - { - get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; } - } - - [IgnoreDataMember] public bool IsVirtualUnaired { get { return LocationType == LocationType.Virtual && IsUnaired; } @@ -310,6 +292,19 @@ namespace MediaBrowser.Controller.Entities.TV Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString()); } + if (!ParentIndexNumber.HasValue) + { + var season = Season; + if (season != null) + { + if (season.ParentIndexNumber.HasValue) + { + ParentIndexNumber = season.ParentIndexNumber; + hasChanges = true; + } + } + } + return hasChanges; } } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index acd02e8ab..ab125eecb 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + + [IgnoreDataMember] public override Guid? DisplayParentId { get @@ -45,27 +54,25 @@ namespace MediaBrowser.Controller.Entities.TV // Genre, Rating and Stuido will all be the same protected override IEnumerable<string> GetIndexByOptions() { - return new List<string> { - {"None"}, + return new List<string> { + {"None"}, {"Performer"}, {"Director"}, {"Year"}, }; } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - if (Series != null) + var list = base.GetUserDataKeys(); + + var series = Series; + if (series != null) { - var seasonNo = IndexNumber ?? 0; - return Series.GetUserDataKey() + seasonNo.ToString("000"); + list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000"))); } - return base.CreateUserDataKey(); + return list; } /// <summary> @@ -94,6 +101,24 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override string PresentationUniqueKey + { + get + { + if (IndexNumber.HasValue) + { + var series = Series; + if (series != null) + { + return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000"); + } + } + + return base.PresentationUniqueKey; + } + } + /// <summary> /// Creates the name of the sort. /// </summary> @@ -103,17 +128,23 @@ namespace MediaBrowser.Controller.Entities.TV return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; } - [IgnoreDataMember] - public bool IsMissingSeason + public override bool RequiresRefresh() { - get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); } + var result = base.RequiresRefresh(); + + if (!result) + { + if (!IsMissingSeason.HasValue) + { + return true; + } + } + + return result; } [IgnoreDataMember] - public bool IsUnaired - { - get { return GetEpisodes().All(i => i.IsUnaired); } - } + public bool? IsMissingSeason { get; set; } [IgnoreDataMember] public bool IsVirtualUnaired @@ -124,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.TV [IgnoreDataMember] public bool IsMissingOrVirtualUnaired { - get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } + get { return (IsMissingSeason ?? false) || (LocationType == LocationType.Virtual && IsUnaired); } } [IgnoreDataMember] @@ -135,22 +166,16 @@ namespace MediaBrowser.Controller.Entities.TV protected override Task<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); - IEnumerable<BaseItem> items; - - if (query.User == null) - { - items = query.Recursive - ? GetRecursiveChildren(filter) - : Children.Where(filter); - } - else - { - items = GetEpisodes(query.User).Where(filter); - } + var items = GetEpisodes(user).Where(filter); var result = PostFilterAndSort(items, query); @@ -171,16 +196,16 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) { - var episodes = GetRecursiveChildren(user) - .OfType<Episode>(); - var series = Series; if (IndexNumber.HasValue && series != null) { - return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes); } + var episodes = GetRecursiveChildren(user) + .OfType<Episode>(); + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) { var seasonNumber = IndexNumber; @@ -199,7 +224,7 @@ namespace MediaBrowser.Controller.Entities.TV episodes = list.DistinctBy(i => i.Id); } - + if (!includeMissingEpisodes) { episodes = episodes.Where(i => !i.IsMissingEpisode); @@ -214,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.TV .Cast<Episode>(); } - private IEnumerable<Episode> GetEpisodes() + public IEnumerable<Episode> GetEpisodes() { var episodes = GetRecursiveChildren().OfType<Episode>(); var series = Series; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 9eeb89a1f..82ab99980 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MoreLinq; namespace MediaBrowser.Controller.Entities.TV { @@ -19,8 +20,6 @@ namespace MediaBrowser.Controller.Entities.TV { public List<Guid> SpecialFeatureIds { get; set; } - public string OriginalTitle { get; set; } - public int? AnimeSeriesIndex { get; set; } public Series() @@ -49,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + public bool DisplaySpecialsWithSeasons { get; set; } public List<Guid> LocalTrailerIds { get; set; } @@ -93,25 +101,40 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override string PresentationUniqueKey + { + get + { + if (EnablePooling()) + { + return GetUserDataKeys().First(); + } + return base.PresentationUniqueKey; + } + } + /// <summary> /// Gets the user data key. /// </summary> /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - var key = this.GetProviderId(MetadataProviders.Tvdb); + var list = base.GetUserDataKeys(); - if (string.IsNullOrWhiteSpace(key)) + var key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(key)) { - key = this.GetProviderId(MetadataProviders.Imdb); + list.Insert(0, key); } - if (string.IsNullOrWhiteSpace(key)) + key = this.GetProviderId(MetadataProviders.Tvdb); + if (!string.IsNullOrWhiteSpace(key)) { - key = base.CreateUserDataKey(); + list.Insert(0, key); } - return key; + return list; } /// <summary> @@ -128,8 +151,8 @@ namespace MediaBrowser.Controller.Entities.TV // Studio, Genre and Rating will all be the same so makes no sense to index by these protected override IEnumerable<string> GetIndexByOptions() { - return new List<string> { - {"None"}, + return new List<string> { + {"None"}, {"Performer"}, {"Director"}, {"Year"}, @@ -174,7 +197,7 @@ namespace MediaBrowser.Controller.Entities.TV else { items = query.Recursive - ? GetRecursiveChildren(user, filter) + ? GetSeasons(user).Cast<BaseItem>().Concat(GetEpisodes(user)).Where(filter) : GetSeasons(user).Where(filter); } @@ -185,8 +208,35 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) { - var seasons = base.GetChildren(user, true) - .OfType<Season>(); + IEnumerable<Season> seasons; + + if (EnablePooling()) + { + var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user) + { + PresentationUniqueKey = PresentationUniqueKey, + IncludeItemTypes = new[] { typeof(Series).Name } + }); + + if (seriesIds.Count > 1) + { + seasons = LibraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(), + IncludeItemTypes = new[] { typeof(Season).Name }, + SortBy = new[] { ItemSortBy.SortName } + + }).Cast<Season>(); + } + else + { + seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>(); + } + } + else + { + seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>(); + } if (!includeMissingSeasons && !includeVirtualUnaired) { @@ -196,7 +246,7 @@ namespace MediaBrowser.Controller.Entities.TV { if (!includeMissingSeasons) { - seasons = seasons.Where(i => !i.IsMissingSeason); + seasons = seasons.Where(i => !(i.IsMissingSeason ?? false)); } if (!includeVirtualUnaired) { @@ -204,9 +254,7 @@ namespace MediaBrowser.Controller.Entities.TV } } - return LibraryManager - .Sort(seasons, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) - .Cast<Season>(); + return seasons; } public IEnumerable<Episode> GetEpisodes(User user) @@ -226,17 +274,8 @@ namespace MediaBrowser.Controller.Entities.TV // 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 - var returnList = new List<Episode>(); - - foreach (var episode in allEpisodes) - { - if (!returnList.Contains(episode)) - { - returnList.Insert(0, episode); - } - } - return returnList; + return allEpisodes.DistinctBy(i => i.Id).Reverse(); } public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) @@ -257,7 +296,7 @@ namespace MediaBrowser.Controller.Entities.TV // Refresh current item await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - // Refresh TV + // Refresh seasons foreach (var item in seasons) { cancellationToken.ThrowIfCancellationRequested(); @@ -270,12 +309,30 @@ namespace MediaBrowser.Controller.Entities.TV progress.Report(percent * 100); } - // Refresh all non-songs + // Refresh episodes and other children foreach (var item in otherItems) { cancellationToken.ThrowIfCancellationRequested(); - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + var skipItem = false; + + var episode = item as Episode; + + if (episode != null + && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh + && !refreshOptions.ReplaceAllMetadata + && episode.IsMissingEpisode + && episode.LocationType == Model.Entities.LocationType.Virtual + && episode.PremiereDate.HasValue + && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30) + { + skipItem = true; + } + + if (!skipItem) + { + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } numComplete++; double percent = numComplete; @@ -295,20 +352,46 @@ namespace MediaBrowser.Controller.Entities.TV return GetEpisodes(user, seasonNumber, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } - public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + private bool EnablePooling() { - return GetEpisodes(user, seasonNumber, includeMissingEpisodes, includeVirtualUnairedEpisodes, - new List<Episode>()); + return false; } - internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes) + public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) { - var episodes = GetRecursiveChildren(user, i => i is Episode) - .Cast<Episode>(); + IEnumerable<Episode> episodes; - episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons); + if (EnablePooling()) + { + var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user) + { + PresentationUniqueKey = PresentationUniqueKey, + IncludeItemTypes = new[] { typeof(Series).Name } + }); - episodes = episodes.Concat(additionalEpisodes).Distinct(); + if (seriesIds.Count > 1) + { + episodes = LibraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(), + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.SortName } + + }).Cast<Episode>(); + } + else + { + episodes = GetRecursiveChildren(user, i => i is Episode) + .Cast<Episode>(); + } + } + else + { + episodes = GetRecursiveChildren(user, i => i is Episode) + .Cast<Episode>(); + } + + episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons); if (!includeMissingEpisodes) { @@ -336,7 +419,7 @@ namespace MediaBrowser.Controller.Entities.TV { if (!includeSpecials || seasonNumber < 1) { - return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber); + return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber); } return episodes.Where(i => diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index c3e24090c..3be2fc624 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Class Trailer /// </summary> - public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo> + public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo> { public List<string> ProductionLocations { get; set; } @@ -56,26 +56,6 @@ namespace MediaBrowser.Controller.Entities /// <value>The revenue.</value> public double? Revenue { get; set; } - protected override string CreateUserDataKey() - { - var key = Movie.GetMovieUserDataKey(this); - - if (!string.IsNullOrWhiteSpace(key)) - { - key = key + "-trailer"; - - // Make sure different trailers have their own data. - if (RunTimeTicks.HasValue) - { - key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); - } - - return key; - } - - return base.CreateUserDataKey(); - } - public override UnratedItem GetBlockUnratedType() { return UnratedItem.Trailer; diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs index 16c37e7d3..f95fd7036 100644 --- a/MediaBrowser.Controller/Entities/UserItemData.cs +++ b/MediaBrowser.Controller/Entities/UserItemData.cs @@ -88,6 +88,8 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value>The index of the subtitle stream.</value> public int? SubtitleStreamIndex { get; set; } + + public const double MinLikeValue = 6.5; /// <summary> /// This is an interpreted property to indicate likes or dislikes @@ -101,7 +103,7 @@ namespace MediaBrowser.Controller.Entities { if (Rating != null) { - return Rating >= 6.5; + return Rating >= MinLikeValue; } return null; diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 8ce39c697..b9e997d17 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -102,10 +102,5 @@ namespace MediaBrowser.Controller.Entities LibraryManager.RegisterItem(item); } } - - public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) - { - // Nothing meaninful here and will only waste resources - } } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index db669ca37..ba496fa5c 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -348,7 +348,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !i.IsFolder) .OfType<IHasAlbumArtist>(); - var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite); + var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user, i).IsFavorite); return GetResult(artists, parent, query); } @@ -1077,7 +1077,7 @@ namespace MediaBrowser.Controller.Entities var e = i as Season; if (e != null) { - return e.IsMissingSeason == val; + return (e.IsMissingSeason ?? false) == val; } return true; }); @@ -1218,7 +1218,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsLiked.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value) { @@ -1228,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsFavoriteOrLiked.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false); if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value) @@ -1239,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsFavorite.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); if (userData.IsFavorite != query.IsFavorite.Value) { @@ -1249,7 +1249,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsResumable.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); var isResumable = userData.PlaybackPositionTicks > 0; if (isResumable != query.IsResumable.Value) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 2c7d3856b..6a9d7cb51 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -28,7 +28,8 @@ namespace MediaBrowser.Controller.Entities IThemeMedia, IArchivable { - public Guid? PrimaryVersionId { get; set; } + [IgnoreDataMember] + public string PrimaryVersionId { get; set; } public List<string> AdditionalParts { get; set; } public List<string> LocalAlternateVersions { get; set; } @@ -44,6 +45,20 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override string PresentationUniqueKey + { + get + { + if (!string.IsNullOrWhiteSpace(PrimaryVersionId)) + { + return PrimaryVersionId; + } + + return base.PresentationUniqueKey; + } + } + public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } @@ -56,6 +71,72 @@ namespace MediaBrowser.Controller.Entities /// <value>The timestamp.</value> public TransportStreamTimestamp? Timestamp { get; set; } + /// <summary> + /// Gets or sets the subtitle paths. + /// </summary> + /// <value>The subtitle paths.</value> + public List<string> SubtitleFiles { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has subtitles. + /// </summary> + /// <value><c>true</c> if this instance has subtitles; otherwise, <c>false</c>.</value> + public bool HasSubtitles { get; set; } + + public bool IsPlaceHolder { get; set; } + public bool IsShortcut { get; set; } + public string ShortcutPath { get; set; } + + /// <summary> + /// Gets or sets the video bit rate. + /// </summary> + /// <value>The video bit rate.</value> + public int? VideoBitRate { get; set; } + + /// <summary> + /// Gets or sets the default index of the video stream. + /// </summary> + /// <value>The default index of the video stream.</value> + public int? DefaultVideoStreamIndex { get; set; } + + /// <summary> + /// Gets or sets the type of the video. + /// </summary> + /// <value>The type of the video.</value> + public VideoType VideoType { get; set; } + + /// <summary> + /// Gets or sets the type of the iso. + /// </summary> + /// <value>The type of the iso.</value> + public IsoType? IsoType { get; set; } + + /// <summary> + /// Gets or sets the video3 D format. + /// </summary> + /// <value>The video3 D format.</value> + public Video3DFormat? Video3DFormat { get; set; } + + /// <summary> + /// If the video is a folder-rip, this will hold the file list for the largest playlist + /// </summary> + public List<string> PlayableStreamFileNames { get; set; } + + /// <summary> + /// Gets the playable stream files. + /// </summary> + /// <returns>List{System.String}.</returns> + public List<string> GetPlayableStreamFiles() + { + return GetPlayableStreamFiles(Path); + } + + /// <summary> + /// Gets or sets the aspect ratio. + /// </summary> + /// <value>The aspect ratio.</value> + public string AspectRatio { get; set; } + public Video() { PlayableStreamFileNames = new List<string>(); @@ -90,6 +171,14 @@ namespace MediaBrowser.Controller.Entities { get { + if (!string.IsNullOrWhiteSpace(PrimaryVersionId)) + { + var item = LibraryManager.GetItemById(PrimaryVersionId) as Video; + if (item != null) + { + return item.MediaSourceCount; + } + } return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1; } } @@ -131,42 +220,65 @@ namespace MediaBrowser.Controller.Entities return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video))); } - protected override string CreateUserDataKey() + [IgnoreDataMember] + protected virtual bool EnableDefaultVideoUserDataKeys { - if (ExtraType.HasValue) + get { - var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb); + return true; + } + } - if (!string.IsNullOrWhiteSpace(key)) + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + if (EnableDefaultVideoUserDataKeys) + { + if (ExtraType.HasValue) { - key = key + "-" + ExtraType.ToString().ToLower(); + var key = this.GetProviderId(MetadataProviders.Tmdb); + if (!string.IsNullOrWhiteSpace(key)) + { + list.Insert(0, GetUserDataKey(key)); + } - // Make sure different trailers have their own data. - if (RunTimeTicks.HasValue) + key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(key)) + { + list.Insert(0, GetUserDataKey(key)); + } + } + else + { + var key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(key)) { - key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); + list.Insert(0, key); } - return key; + key = this.GetProviderId(MetadataProviders.Tmdb); + if (!string.IsNullOrWhiteSpace(key)) + { + list.Insert(0, key); + } } } - return base.CreateUserDataKey(); + return list; } - /// <summary> - /// Gets the linked children. - /// </summary> - /// <returns>IEnumerable{BaseItem}.</returns> - public IEnumerable<Video> GetAlternateVersions() + private string GetUserDataKey(string providerId) { - var filesWithinSameDirectory = GetLocalAlternateVersionIds() - .Select(i => LibraryManager.GetItemById(i)) - .Where(i => i != null) - .OfType<Video>(); + var key = providerId + "-" + ExtraType.ToString().ToLower(); - return filesWithinSameDirectory.Concat(GetLinkedAlternateVersions()) - .OrderBy(i => i.SortName); + // Make sure different trailers have their own data. + if (RunTimeTicks.HasValue) + { + key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); + } + + return key; } public IEnumerable<Video> GetLinkedAlternateVersions() @@ -193,72 +305,6 @@ namespace MediaBrowser.Controller.Entities .OrderBy(i => i.SortName); } - /// <summary> - /// Gets or sets the subtitle paths. - /// </summary> - /// <value>The subtitle paths.</value> - public List<string> SubtitleFiles { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this instance has subtitles. - /// </summary> - /// <value><c>true</c> if this instance has subtitles; otherwise, <c>false</c>.</value> - public bool HasSubtitles { get; set; } - - public bool IsPlaceHolder { get; set; } - public bool IsShortcut { get; set; } - public string ShortcutPath { get; set; } - - /// <summary> - /// Gets or sets the video bit rate. - /// </summary> - /// <value>The video bit rate.</value> - public int? VideoBitRate { get; set; } - - /// <summary> - /// Gets or sets the default index of the video stream. - /// </summary> - /// <value>The default index of the video stream.</value> - public int? DefaultVideoStreamIndex { get; set; } - - /// <summary> - /// Gets or sets the type of the video. - /// </summary> - /// <value>The type of the video.</value> - public VideoType VideoType { get; set; } - - /// <summary> - /// Gets or sets the type of the iso. - /// </summary> - /// <value>The type of the iso.</value> - public IsoType? IsoType { get; set; } - - /// <summary> - /// Gets or sets the video3 D format. - /// </summary> - /// <value>The video3 D format.</value> - public Video3DFormat? Video3DFormat { get; set; } - - /// <summary> - /// If the video is a folder-rip, this will hold the file list for the largest playlist - /// </summary> - public List<string> PlayableStreamFileNames { get; set; } - - /// <summary> - /// Gets the playable stream files. - /// </summary> - /// <returns>List{System.String}.</returns> - public List<string> GetPlayableStreamFiles() - { - return GetPlayableStreamFiles(Path); - } - - /// <summary> - /// Gets or sets the aspect ratio. - /// </summary> - /// <value>The aspect ratio.</value> - public string AspectRatio { get; set; } - [IgnoreDataMember] public override string ContainingFolderPath { @@ -317,6 +363,11 @@ namespace MediaBrowser.Controller.Entities { return false; } + + if (newAsVideo.VideoType != VideoType) + { + return false; + } } return base.IsValidFromResolver(newItem); @@ -463,6 +514,36 @@ namespace MediaBrowser.Controller.Entities }).FirstOrDefault(); } + private List<Tuple<Video, MediaSourceType>> GetAllVideosForMediaSources() + { + var list = new List<Tuple<Video, MediaSourceType>>(); + + list.Add(new Tuple<Video, MediaSourceType>(this, MediaSourceType.Default)); + list.AddRange(GetLinkedAlternateVersions().Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping))); + + if (!string.IsNullOrWhiteSpace(PrimaryVersionId)) + { + var primary = LibraryManager.GetItemById(PrimaryVersionId) as Video; + if (primary != null) + { + var existingIds = list.Select(i => i.Item1.Id).ToList(); + list.Add(new Tuple<Video, MediaSourceType>(primary, MediaSourceType.Grouping)); + list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping))); + } + } + + var localAlternates = list + .SelectMany(i => i.Item1.GetLocalAlternateVersionIds()) + .Select(LibraryManager.GetItemById) + .Where(i => i != null) + .OfType<Video>() + .ToList(); + + list.AddRange(localAlternates.Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Default))); + + return list; + } + public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) { if (SourceType == SourceType.Channel) @@ -481,13 +562,8 @@ namespace MediaBrowser.Controller.Entities }; } - var item = this; - - var result = item.GetAlternateVersions() - .Select(i => GetVersionInfo(enablePathSubstitution, i, MediaSourceType.Grouping)) - .ToList(); - - result.Add(GetVersionInfo(enablePathSubstitution, item, MediaSourceType.Default)); + var list = GetAllVideosForMediaSources(); + var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList(); return result.OrderBy(i => { diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 163dcd667..db896f1fc 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Year : BaseItem, IItemByName { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return "Year-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, "Year-" + Name); + return list; } /// <summary> @@ -71,6 +70,22 @@ namespace MediaBrowser.Controller.Entities return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year); } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + int year; + + var usCulture = new CultureInfo("en-US"); + + if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year)) + { + return new List<BaseItem>(); + } + + query.Years = new[] { year }; + + return LibraryManager.GetItemList(query); + } + public int? GetYearValue() { int i; diff --git a/MediaBrowser.Controller/Health/IHealthMonitor.cs b/MediaBrowser.Controller/Health/IHealthMonitor.cs new file mode 100644 index 000000000..b8ad98fc1 --- /dev/null +++ b/MediaBrowser.Controller/Health/IHealthMonitor.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Notifications; + +namespace MediaBrowser.Controller.Health +{ + public interface IHealthMonitor + { + Task<List<Notification>> GetNotifications(CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index c8b3d5131..936b97c6e 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Library /// <param name="fileInfo">The file information.</param> /// <param name="parent">The parent.</param> /// <returns>BaseItem.</returns> - BaseItem ResolvePath(FileSystemMetadata fileInfo, + BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null); /// <summary> @@ -36,9 +36,9 @@ namespace MediaBrowser.Controller.Library /// <param name="parent">The parent.</param> /// <param name="collectionType">Type of the collection.</param> /// <returns>List{``0}.</returns> - IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, + IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, - Folder parent, string + Folder parent, string collectionType = null); /// <summary> @@ -59,8 +59,8 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="path">The path.</param> /// <returns>BaseItem.</returns> - BaseItem FindByPath(string path); - + BaseItem FindByPath(string path, bool? isFolder); + /// <summary> /// Gets the artist. /// </summary> @@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Library /// <param name="id">The identifier.</param> /// <returns>BaseItem.</returns> BaseItem GetMemoryItemById(Guid id); - + /// <summary> /// Gets the intros. /// </summary> @@ -243,6 +243,8 @@ namespace MediaBrowser.Controller.Library /// <returns>BaseItem.</returns> BaseItem RetrieveItem(Guid id); + bool IsScanRunning { get; } + /// <summary> /// Occurs when [item added]. /// </summary> @@ -290,7 +292,7 @@ namespace MediaBrowser.Controller.Library /// <param name="path">The path.</param> /// <returns>System.String.</returns> string GetConfiguredContentType(string path); - + /// <summary> /// Normalizes the root path list. /// </summary> @@ -332,8 +334,8 @@ namespace MediaBrowser.Controller.Library Task<UserView> GetNamedView(User user, string name, string parentId, - string viewType, - string sortName, + string viewType, + string sortName, CancellationToken cancellationToken); /// <summary> @@ -346,8 +348,8 @@ namespace MediaBrowser.Controller.Library /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<UserView>.</returns> Task<UserView> GetNamedView(User user, - string name, - string viewType, + string name, + string viewType, string sortName, CancellationToken cancellationToken); @@ -393,7 +395,7 @@ namespace MediaBrowser.Controller.Library string viewType, string sortName, CancellationToken cancellationToken); - + /// <summary> /// Determines whether [is video file] [the specified path]. /// </summary> @@ -477,14 +479,14 @@ namespace MediaBrowser.Controller.Library /// <param name="query">The query.</param> /// <returns>List<PersonInfo>.</returns> List<PersonInfo> GetPeople(InternalPeopleQuery query); - + /// <summary> /// Gets the people items. /// </summary> /// <param name="query">The query.</param> /// <returns>List<Person>.</returns> List<Person> GetPeopleItems(InternalPeopleQuery query); - + /// <summary> /// Gets all people names. /// </summary> @@ -559,7 +561,7 @@ namespace MediaBrowser.Controller.Library /// <param name="query">The query.</param> /// <returns>QueryResult<BaseItem>.</returns> QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query); - + /// <summary> /// Ignores the file. /// </summary> @@ -567,5 +569,10 @@ namespace MediaBrowser.Controller.Library /// <param name="parent">The parent.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> bool IgnoreFile(FileSystemMetadata file, BaseItem parent); + + void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, bool refreshLibrary); + void RemoveVirtualFolder(string name, bool refreshLibrary); + void AddMediaPath(string virtualFolderName, string path); + void RemoveMediaPath(string virtualFolderName, string path); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 56ac14e9d..e2358650b 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -29,21 +29,10 @@ namespace MediaBrowser.Controller.Library /// <returns>Task.</returns> Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken); - /// <summary> - /// Gets the user data. - /// </summary> - /// <param name="userId">The user id.</param> - /// <param name="key">The key.</param> - /// <returns>Task{UserItemData}.</returns> - UserItemData GetUserData(string userId, string key); + UserItemData GetUserData(IHasUserData user, IHasUserData item); - /// <summary> - /// Gets the user data. - /// </summary> - /// <param name="userId">The user id.</param> - /// <param name="key">The key.</param> - /// <returns>Task{UserItemData}.</returns> - UserItemData GetUserData(Guid userId, string key); + UserItemData GetUserData(string userId, IHasUserData item); + UserItemData GetUserData(Guid userId, IHasUserData item); /// <summary> /// Gets the user data dto. diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index f8039b2cf..29421ebaf 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Library /// <summary> /// The banner URL /// </summary> - public static readonly string BannerUrl = "http://www.thetvdb.com/banners/"; + public static readonly string BannerUrl = "https://www.thetvdb.com/banners/"; /// <summary> /// Gets the air days. diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs index ba328ff75..654c6b581 100644 --- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs +++ b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using System; +using System.Collections.Generic; namespace MediaBrowser.Controller.Library { @@ -15,11 +16,7 @@ namespace MediaBrowser.Controller.Library /// <value>The user id.</value> public Guid UserId { get; set; } - /// <summary> - /// Gets or sets the key. - /// </summary> - /// <value>The key.</value> - public string Key { get; set; } + public List<string> Keys { get; set; } /// <summary> /// Gets or sets the save reason. diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 498602ddf..1e7aa3de5 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -46,6 +46,8 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<List<MediaSourceInfo>>.</returns> Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + + string ApplyDuration(string streamPath, TimeSpan duration); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 17a27eac1..e6f472414 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -46,17 +46,6 @@ namespace MediaBrowser.Controller.LiveTv } /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() - { - var name = GetClientTypeName(); - - return name + "-" + Name + (EpisodeTitle ?? string.Empty); - } - - /// <summary> /// Gets a value indicating whether this instance is owned item. /// </summary> /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 35b9a1959..50aeed27d 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.LiveTv { public class LiveTvChannel : BaseItem, IHasMediaSources { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - return GetClientTypeName() + "-" + Name; + var list = base.GetUserDataKeys(); + + list.Insert(0, GetClientTypeName() + "-" + Name); + return list; } public override UnratedItem GetBlockUnratedType() @@ -45,6 +44,15 @@ namespace MediaBrowser.Controller.LiveTv set { } } + [IgnoreDataMember] + public override bool EnableRememberingTrackSelections + { + get + { + return false; + } + } + /// <summary> /// Gets or sets the number. /// </summary> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 59b921c6a..cc30709db 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -4,36 +4,40 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.LiveTv; using System; +using System.Collections.Generic; using System.Runtime.Serialization; +using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.LiveTv { public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes { - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() + public override List<string> GetUserDataKeys() { - if (IsMovie) + var list = base.GetUserDataKeys(); + + if (!IsSeries) { - var key = Movie.GetMovieUserDataKey(this); + var key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(key)) + { + list.Insert(0, key); + } + key = this.GetProviderId(MetadataProviders.Tmdb); if (!string.IsNullOrWhiteSpace(key)) { - return key; + list.Insert(0, key); } } - - if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle)) + else if (!string.IsNullOrWhiteSpace(EpisodeTitle)) { var name = GetClientTypeName(); - return name + "-" + Name + (EpisodeTitle ?? string.Empty); + list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty)); } - return base.CreateUserDataKey(); + return list; } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index f310a957c..a8c737673 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -45,32 +45,6 @@ namespace MediaBrowser.Controller.LiveTv set { } } - /// <summary> - /// Gets the user data key. - /// </summary> - /// <returns>System.String.</returns> - protected override string CreateUserDataKey() - { - if (IsMovie) - { - var key = Movie.GetMovieUserDataKey(this); - - if (!string.IsNullOrWhiteSpace(key)) - { - return key; - } - } - - if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle)) - { - var name = GetClientTypeName(); - - return name + "-" + Name + (EpisodeTitle ?? string.Empty); - } - - return base.CreateUserDataKey(); - } - [IgnoreDataMember] public override string MediaType { diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6ff4e39e2..bc28ec015 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -180,6 +180,7 @@ <Compile Include="Entities\UserView.cs" /> <Compile Include="Entities\UserViewBuilder.cs" /> <Compile Include="FileOrganization\IFileOrganizationService.cs" /> + <Compile Include="Health\IHealthMonitor.cs" /> <Compile Include="IO\ThrottledStream.cs" /> <Compile Include="Library\DeleteOptions.cs" /> <Compile Include="Library\ILibraryPostScanTask.cs" /> diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index d2feb4116..7c3959f6e 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -4,13 +4,14 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Controller.MediaEncoding { /// <summary> /// Interface IMediaEncoder /// </summary> - public interface IMediaEncoder + public interface IMediaEncoder : ITranscoderSupport { /// <summary> /// Gets the encoder path. diff --git a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs index 6ad4a5377..cd587a509 100644 --- a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs +++ b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs @@ -19,12 +19,6 @@ namespace MediaBrowser.Controller.Notifications /// Occurs when [notifications marked read]. /// </summary> event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead; - - /// <summary> - /// Opens the connection to the repository - /// </summary> - /// <returns>Task.</returns> - Task Initialize(); /// <summary> /// Gets the notifications. diff --git a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs index 17de730cb..abf96994f 100644 --- a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs +++ b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs @@ -12,12 +12,6 @@ namespace MediaBrowser.Controller.Persistence public interface IDisplayPreferencesRepository : IRepository { /// <summary> - /// Opens the connection to the repository - /// </summary> - /// <returns>Task.</returns> - Task Initialize(); - - /// <summary> /// Saves display preferences for an item /// </summary> /// <param name="displayPreferences">The display preferences.</param> diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 15df1f649..7bcc36958 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -14,12 +14,6 @@ namespace MediaBrowser.Controller.Persistence public interface IItemRepository : IRepository { /// <summary> - /// Opens the connection to the repository - /// </summary> - /// <returns>Task.</returns> - Task Initialize(); - - /// <summary> /// Saves an item /// </summary> /// <param name="item">The item.</param> diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 2a904be0d..2e165f416 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -12,12 +12,6 @@ namespace MediaBrowser.Controller.Persistence public interface IUserDataRepository : IRepository { /// <summary> - /// Opens the connection to the repository - /// </summary> - /// <returns>Task.</returns> - Task Initialize(); - - /// <summary> /// Saves the user data. /// </summary> /// <param name="userId">The user id.</param> diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 0f9af6550..67b1d479b 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Playlists { @@ -38,6 +39,15 @@ namespace MediaBrowser.Controller.Playlists } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + public override bool IsAuthorizedToDelete(User user) { return true; @@ -50,12 +60,12 @@ namespace MediaBrowser.Controller.Playlists public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { - return GetPlayableItems(user); + return GetPlayableItems(user).Result; } public override IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter) { - var items = GetPlayableItems(user); + var items = GetPlayableItems(user).Result; if (filter != null) { @@ -70,32 +80,40 @@ namespace MediaBrowser.Controller.Playlists return GetLinkedChildrenInfos(); } - private IEnumerable<BaseItem> GetPlayableItems(User user) + private Task<IEnumerable<BaseItem>> GetPlayableItems(User user) { return GetPlaylistItems(MediaType, base.GetChildren(user, true), user); } - public static IEnumerable<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user) + public static async Task<IEnumerable<BaseItem>> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user) { if (user != null) { inputItems = inputItems.Where(i => i.IsVisible(user)); } - return inputItems.SelectMany(i => GetPlaylistItems(i, user)) - .Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase)); + var list = new List<BaseItem>(); + + foreach (var item in inputItems) + { + var playlistItems = await GetPlaylistItems(item, user, playlistMediaType).ConfigureAwait(false); + list.AddRange(playlistItems); + } + + return list; } - private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem item, User user) + private static async Task<IEnumerable<BaseItem>> GetPlaylistItems(BaseItem item, User user, string mediaType) { var musicGenre = item as MusicGenre; if (musicGenre != null) { - Func<BaseItem, bool> filter = i => i is Audio && i.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase); - - var items = user == null - ? LibraryManager.RootFolder.GetRecursiveChildren(filter) - : user.RootFolder.GetRecursiveChildren(user, filter); + var items = LibraryManager.GetItemList(new InternalItemsQuery(user) + { + Recursive = true, + IncludeItemTypes = new[] { typeof(Audio).Name }, + Genres = new[] { musicGenre.Name } + }); return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); } @@ -119,15 +137,19 @@ namespace MediaBrowser.Controller.Playlists var folder = item as Folder; if (folder != null) { - var items = user == null - ? folder.GetRecursiveChildren(m => !m.IsFolder) - : folder.GetRecursiveChildren(user, m => !m.IsFolder); - - if (folder.IsPreSorted) + var query = new InternalItemsQuery(user) { - return items; - } - return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending); + Recursive = true, + IsFolder = false, + SortBy = new[] { ItemSortBy.SortName }, + MediaTypes = new[] { mediaType }, + EnableTotalRecordCount = false + }; + + var itemsResult = await folder.GetItems(query).ConfigureAwait(false); + var items = itemsResult.Items; + + return items; } return new[] { item }; diff --git a/MediaBrowser.Controller/Providers/IProviderRepository.cs b/MediaBrowser.Controller/Providers/IProviderRepository.cs index 1f77d0ca1..891275d77 100644 --- a/MediaBrowser.Controller/Providers/IProviderRepository.cs +++ b/MediaBrowser.Controller/Providers/IProviderRepository.cs @@ -21,11 +21,5 @@ namespace MediaBrowser.Controller.Providers /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task SaveMetadataStatus(MetadataStatus status, CancellationToken cancellationToken); - - /// <summary> - /// Initializes this instance. - /// </summary> - /// <returns>Task.</returns> - Task Initialize(); } } |
