diff options
Diffstat (limited to 'MediaBrowser.Controller')
96 files changed, 1954 insertions, 2132 deletions
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 43f7b6637..718a0d878 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Channels set { } } - public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + protected override async Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) { try { diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs deleted file mode 100644 index 41e9dd203..000000000 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ /dev/null @@ -1,101 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Threading; - -namespace MediaBrowser.Controller.Channels -{ - public class ChannelAudioItem : Audio - { - public ChannelMediaContentType ContentType { get; set; } - - public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - - public override UnratedItem GetBlockUnratedType() - { - return UnratedItem.ChannelContent; - } - - protected override string CreateUserDataKey() - { - return ExternalId; - } - - [IgnoreDataMember] - public override bool SupportsLocalMetadata - { - get - { - return false; - } - } - - public override bool IsSaveLocalMetadataEnabled() - { - return false; - } - - public ChannelAudioItem() - { - ChannelMediaSources = new List<ChannelMediaInfo>(); - } - - [IgnoreDataMember] - public override LocationType LocationType - { - get - { - if (string.IsNullOrEmpty(Path)) - { - return LocationType.Remote; - } - - return base.LocationType; - } - } - - protected override string GetInternalMetadataPath(string basePath) - { - return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); - } - - public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) - { - var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None) - .Result.ToList(); - - if (sources.Count > 0) - { - return sources; - } - - var list = base.GetMediaSources(enablePathSubstitution).ToList(); - - foreach (var mediaSource in list) - { - if (string.IsNullOrWhiteSpace(mediaSource.Path)) - { - mediaSource.Type = MediaSourceType.Placeholder; - } - } - - return list; - } - - public override bool CanDelete() - { - return false; - } - - public override bool IsVisibleStandalone(User user) - { - return IsVisibleStandaloneInternal(user, false) && ChannelVideoItem.IsChannelVisible(this, user); - } - } -} diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs deleted file mode 100644 index da5d60863..000000000 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ /dev/null @@ -1,89 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Users; -using System; -using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Controller.Channels -{ - public class ChannelFolderItem : Folder - { - public ChannelFolderType ChannelFolderType { get; set; } - - protected override bool GetBlockUnratedValue(UserPolicy config) - { - // Don't block. - return false; - } - - public override UnratedItem GetBlockUnratedType() - { - return UnratedItem.ChannelContent; - } - - [IgnoreDataMember] - public override bool SupportsLocalMetadata - { - get - { - return false; - } - } - - public override bool IsSaveLocalMetadataEnabled() - { - return false; - } - - protected override string CreateUserDataKey() - { - return ExternalId; - } - - public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) - { - try - { - // Don't blow up here because it could cause parent screens with other content to fail - return await ChannelManager.GetChannelItemsInternal(new ChannelItemQuery - { - ChannelId = ChannelId, - FolderId = Id.ToString("N"), - Limit = query.Limit, - StartIndex = query.StartIndex, - UserId = query.User.Id.ToString("N"), - SortBy = query.SortBy, - SortOrder = query.SortOrder - - }, new Progress<double>(), CancellationToken.None); - } - catch - { - // Already logged at lower levels - return new QueryResult<BaseItem> - { - - }; - } - } - - protected override string GetInternalMetadataPath(string basePath) - { - return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); - } - - public override bool CanDelete() - { - return false; - } - - public override bool IsVisibleStandalone(User user) - { - return IsVisibleStandaloneInternal(user, false) && ChannelVideoItem.IsChannelVisible(this, user); - } - } -} diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 587023ab4..57c2f1f7f 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -53,6 +53,12 @@ namespace MediaBrowser.Controller.Channels public bool IsInfiniteStream { get; set; } + public string HomePageUrl { get; set; } + + public List<string> Artists { get; set; } + + public List<string> AlbumArtists { get; set; } + public ChannelItemInfo() { MediaSources = new List<ChannelMediaInfo>(); @@ -62,6 +68,8 @@ namespace MediaBrowser.Controller.Channels People = new List<PersonInfo>(); Tags = new List<string>(); ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + Artists = new List<string>(); + AlbumArtists = new List<string>(); } } } diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 1672b75fa..9424568b4 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -65,7 +65,7 @@ namespace MediaBrowser.Controller.Channels Name = id, Id = id, ReadAtNativeFramerate = ReadAtNativeFramerate, - SupportsDirectStream = Protocol == MediaProtocol.File || Protocol == MediaProtocol.Http, + SupportsDirectStream = Protocol == MediaProtocol.File, SupportsDirectPlay = SupportsDirectPlay }; diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs deleted file mode 100644 index 9fe04812e..000000000 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ /dev/null @@ -1,126 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Runtime.Serialization; -using System.Threading; - -namespace MediaBrowser.Controller.Channels -{ - public class ChannelVideoItem : Video - { - public ChannelMediaContentType ContentType { get; set; } - - public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - - protected override string CreateUserDataKey() - { - if (ContentType == ChannelMediaContentType.MovieExtra) - { - var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb); - - if (!string.IsNullOrWhiteSpace(key)) - { - key = key + "-" + ExtraType.ToString().ToLower(); - - // Make sure different trailers have their own data. - if (RunTimeTicks.HasValue) - { - key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); - } - - return key; - } - } - - return ExternalId; - } - - public override UnratedItem GetBlockUnratedType() - { - return UnratedItem.ChannelContent; - } - - [IgnoreDataMember] - public override bool SupportsLocalMetadata - { - get - { - return false; - } - } - - public override bool IsSaveLocalMetadataEnabled() - { - return false; - } - - public ChannelVideoItem() - { - ChannelMediaSources = new List<ChannelMediaInfo>(); - } - - [IgnoreDataMember] - public override LocationType LocationType - { - get - { - if (string.IsNullOrEmpty(Path)) - { - return LocationType.Remote; - } - - return base.LocationType; - } - } - - public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) - { - var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None) - .Result.ToList(); - - if (sources.Count > 0) - { - return sources; - } - - var list = base.GetMediaSources(enablePathSubstitution).ToList(); - - foreach (var mediaSource in list) - { - if (string.IsNullOrWhiteSpace(mediaSource.Path)) - { - mediaSource.Type = MediaSourceType.Placeholder; - } - } - - return list; - } - - protected override string GetInternalMetadataPath(string basePath) - { - return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); - } - - public override bool CanDelete() - { - return false; - } - - public override bool IsVisibleStandalone(User user) - { - return IsVisibleStandaloneInternal(user, false) && IsChannelVisible(this, user); - } - - internal static bool IsChannelVisible(BaseItem item, User user) - { - var channel = ChannelManager.GetChannel(item.ChannelId); - - return channel.IsVisible(user); - } - } -} diff --git a/MediaBrowser.Controller/Channels/IChannelItem.cs b/MediaBrowser.Controller/Channels/IChannelItem.cs deleted file mode 100644 index 9b5f0359b..000000000 --- a/MediaBrowser.Controller/Channels/IChannelItem.cs +++ /dev/null @@ -1,11 +0,0 @@ -using MediaBrowser.Controller.Entities; - -namespace MediaBrowser.Controller.Channels -{ - public interface IChannelItem : IHasImages, IHasTags - { - string ChannelId { get; set; } - - string ExternalId { get; set; } - } -} diff --git a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs deleted file mode 100644 index 60a29da90..000000000 --- a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Channels -{ - public interface IChannelMediaItem : IChannelItem - { - long? RunTimeTicks { get; set; } - string MediaType { get; } - - ChannelMediaContentType ContentType { get; set; } - - ExtraType? ExtraType { get; set; } - - List<ChannelMediaInfo> ChannelMediaSources { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs index 676ef9c56..27e06fb8d 100644 --- a/MediaBrowser.Controller/Chapters/IChapterManager.cs +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Chapters /// <param name="chapters">The chapters.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task SaveChapters(string itemId, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken); + Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken); /// <summary> /// Searches the specified video. diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index d42a04f2e..19f391b4a 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Drawing /// </summary> /// <param name="options">The options.</param> /// <returns>Task.</returns> - Task<Tuple<string, string>> ProcessImage(ImageProcessingOptions options); + Task<Tuple<string, string, DateTime>> ProcessImage(ImageProcessingOptions options); /// <summary> /// Gets the enhanced image. diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs index edc4f8558..92a7f5ac9 100644 --- a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs @@ -23,10 +23,5 @@ namespace MediaBrowser.Controller.Drawing /// </summary> /// <value>The height.</value> public int Height { get; set; } - /// <summary> - /// Gets or sets the text. - /// </summary> - /// <value>The text.</value> - public string Text { get; set; } } } diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 5f0442f93..e4aa466df 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using System.Collections.Generic; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Dto { @@ -68,7 +69,7 @@ namespace MediaBrowser.Controller.Dto /// <param name="user">The user.</param> /// <param name="owner">The owner.</param> /// <returns>IEnumerable<BaseItemDto>.</returns> - IEnumerable<BaseItemDto> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, + Task<List<BaseItemDto>> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null); /// <summary> diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 018ff6da0..588a65e98 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -64,10 +64,37 @@ namespace MediaBrowser.Controller.Entities protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService) { - return CreateResolveArgs(directoryService).FileSystemChildren; + return CreateResolveArgs(directoryService, true).FileSystemChildren; } - private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService) + private bool _requiresRefresh; + public override bool RequiresRefresh() + { + var changed = base.RequiresRefresh() || _requiresRefresh; + + if (!changed) + { + var locations = PhysicalLocations.ToList(); + + var newLocations = CreateResolveArgs(new DirectoryService(BaseItem.FileSystem), false).PhysicalLocations.ToList(); + + if (!locations.SequenceEqual(newLocations)) + { + changed = true; + } + } + + return changed; + } + + public override bool BeforeMetadataRefresh() + { + var changed = base.BeforeMetadataRefresh() || _requiresRefresh; + _requiresRefresh = false; + return changed; + } + + private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations) { var path = ContainingFolderPath; @@ -100,7 +127,11 @@ namespace MediaBrowser.Controller.Entities args.FileSystemDictionary = fileSystemDictionary; } - PhysicalLocationsList = args.PhysicalLocations.ToList(); + _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations); + if (setPhysicalLocations) + { + PhysicalLocationsList = args.PhysicalLocations.ToList(); + } return args; } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 929308ba0..1897511af 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -20,15 +20,12 @@ namespace MediaBrowser.Controller.Entities.Audio IHasArtist, IHasMusicGenres, IHasLookupInfo<SongInfo>, - IHasTags, IHasMediaSources, IThemeMedia, IArchivable { public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - - public long? Size { get; set; } - public string Container { get; set; } + public int? TotalBitrate { get; set; } public ExtraType? ExtraType { get; set; } @@ -40,12 +37,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 { @@ -55,6 +46,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public Audio() { Artists = new List<string>(); @@ -150,12 +147,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 +160,7 @@ namespace MediaBrowser.Controller.Entities.Audio { songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; } - songKey+= Name; + songKey += Name; if (!string.IsNullOrWhiteSpace(Album)) { @@ -178,25 +173,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..1f3b0c92a 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) @@ -172,17 +179,13 @@ namespace MediaBrowser.Controller.Entities.Audio { var items = GetRecursiveChildren().ToList(); - var songs = items.OfType<Audio>().ToList(); - - var others = items.Except(songs).ToList(); - - var totalItems = songs.Count + others.Count; + var totalItems = items.Count; var numComplete = 0; var childUpdateType = ItemUpdateType.None; // Refresh songs - foreach (var item in songs) + foreach (var item in items) { cancellationToken.ThrowIfCancellationRequested(); @@ -192,7 +195,7 @@ namespace MediaBrowser.Controller.Entities.Audio numComplete++; double percent = numComplete; percent /= totalItems; - progress.Report(percent * 100); + progress.Report(percent * 95); } var parentRefreshOptions = refreshOptions; @@ -205,19 +208,6 @@ namespace MediaBrowser.Controller.Entities.Audio // Refresh current item await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false); - // Refresh all non-songs - foreach (var item in others) - { - cancellationToken.ThrowIfCancellationRequested(); - - var updateType = await item.RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false); - - numComplete++; - double percent = numComplete; - percent /= totalItems; - progress.Report(percent * 100); - } - progress.Report(100); } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 02bcceada..6790a1bcf 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.Controller.Entities.Audio { @@ -17,7 +18,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 +36,15 @@ namespace MediaBrowser.Controller.Entities.Audio } [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] public override bool SupportsAddingToPlaylist { get { return true; } @@ -40,6 +55,18 @@ namespace MediaBrowser.Controller.Entities.Audio return !IsAccessedByName; } + public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name }; + query.ArtistNames = new[] { Name }; + } + + return LibraryManager.GetItemList(query); + } + + [IgnoreDataMember] protected override IEnumerable<BaseItem> ActualChildren { get @@ -53,6 +80,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + public override int GetChildCount(User user) + { + if (IsAccessedByName) + { + return 0; + } + return base.GetChildCount(user); + } + public override bool IsSaveLocalMetadataEnabled() { if (IsAccessedByName) @@ -80,13 +116,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,18 +156,27 @@ 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 ?? string.Empty).RemoveDiacritics()); + return list; } + public override string PresentationUniqueKey + { + get + { + return "Artist-" + (Name ?? string.Empty).RemoveDiacritics(); + } + } protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Music); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 45304d47e..798bc79fb 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.Controller.Entities.Audio { @@ -10,13 +11,20 @@ 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, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + + public override string PresentationUniqueKey + { + get + { + return GetUserDataKeys()[0]; + } } [IgnoreDataMember] @@ -80,5 +88,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 903c5cfd5..c7a6b75ff 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -26,6 +26,7 @@ using System.Threading.Tasks; using CommonIO; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Entities { @@ -36,6 +37,7 @@ namespace MediaBrowser.Controller.Entities { protected BaseItem() { + Keywords = new List<string>(); Tags = new List<string>(); Genres = new List<string>(); Studios = new List<string>(); @@ -44,6 +46,9 @@ namespace MediaBrowser.Controller.Entities ImageInfos = new List<ItemImageInfo>(); } + public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; + public static char SlugChar = '-'; + /// <summary> /// The supported image extensions /// </summary> @@ -64,8 +69,22 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public string PreferredMetadataLanguage { get; set; } + public long? Size { get; set; } + public string Container { get; set; } + public string ShortOverview { get; set; } + public List<ItemImageInfo> ImageInfos { get; set; } + [IgnoreDataMember] + public bool IsVirtualItem { get; set; } + + /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + [IgnoreDataMember] + public string Album { get; set; } + /// <summary> /// Gets or sets the channel identifier. /// </summary> @@ -125,6 +144,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> @@ -255,6 +297,11 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(Path)) { + if (SourceType == SourceType.Channel) + { + return LocationType.Remote; + } + return LocationType.Virtual; } @@ -301,7 +348,7 @@ namespace MediaBrowser.Controller.Entities } } - private List<Tuple<StringBuilder,bool>> GetSortChunks(string s1) + private List<Tuple<StringBuilder, bool>> GetSortChunks(string s1) { var list = new List<Tuple<StringBuilder, bool>>(); @@ -407,6 +454,12 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public DateTime DateLastRefreshed { get; set; } + [IgnoreDataMember] + public virtual bool EnableForceSaveOnDateModifiedChange + { + get { return false; } + } + /// <summary> /// The logger /// </summary> @@ -494,7 +547,19 @@ namespace MediaBrowser.Controller.Entities { get { - return _sortName ?? (_sortName = CreateSortName()); + if (_sortName == null) + { + if (!string.IsNullOrWhiteSpace(ForcedSortName)) + { + // Need the ToLower because that's what CreateSortName does + _sortName = ModifySortChunks(ForcedSortName).ToLower(); + } + else + { + _sortName = CreateSortName(); + } + } + return _sortName; } set { @@ -529,11 +594,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>System.String.</returns> protected virtual string CreateSortName() { - if (!string.IsNullOrWhiteSpace(ForcedSortName)) - { - return ModifySortChunks(ForcedSortName).ToLower(); - } - if (Name == null) return null; //some items may not have name filled in properly if (!EnableAlphaNumericSorting) @@ -588,7 +648,7 @@ namespace MediaBrowser.Controller.Entities builder.Append(chunkBuilder); } //Logger.Debug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); - return builder.ToString(); + return builder.ToString().RemoveDiacritics(); } [IgnoreDataMember] @@ -653,9 +713,30 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] - public virtual BaseItem DisplayParent + public virtual Guid? DisplayParentId + { + get + { + if (ParentId == Guid.Empty) + { + return null; + } + return ParentId; + } + } + + [IgnoreDataMember] + public BaseItem DisplayParent { - get { return GetParent(); } + get + { + var id = DisplayParentId; + if (!id.HasValue || id.Value == Guid.Empty) + { + return null; + } + return LibraryManager.GetItemById(id.Value); + } } /// <summary> @@ -690,12 +771,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> @@ -740,6 +823,8 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public List<string> Tags { get; set; } + public List<string> Keywords { get; set; } + /// <summary> /// Gets or sets the home page URL. /// </summary> @@ -961,9 +1046,7 @@ namespace MediaBrowser.Controller.Entities } : options; - var result = await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); - - return result; + return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); } [IgnoreDataMember] @@ -1111,33 +1194,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) @@ -1150,7 +1231,6 @@ namespace MediaBrowser.Controller.Entities public void AfterMetadataRefresh() { _sortName = null; - _userDataKey = null; } /// <summary> @@ -1312,17 +1392,25 @@ namespace MediaBrowser.Controller.Entities return LocalizationManager.GetRatingLevel(rating); } - private bool IsVisibleViaTags(User user) + public List<string> GetInheritedTags() { - var hasTags = this as IHasTags; + var list = new List<string>(); + list.AddRange(Tags); - if (hasTags != null) + foreach (var parent in GetParents()) { - var policy = user.Policy; - if (policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) - { - return false; - } + list.AddRange(parent.Tags); + } + + return list.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + } + + private bool IsVisibleViaTags(User user) + { + var policy = user.Policy; + if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) + { + return false; } return true; @@ -1439,7 +1527,7 @@ namespace MediaBrowser.Controller.Entities public virtual string GetClientTypeName() { - if (IsFolder && SourceType == SourceType.Channel) + if (IsFolder && SourceType == SourceType.Channel && !(this is Channel)) { return "ChannelFolderItem"; } @@ -1491,11 +1579,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; @@ -1504,6 +1592,15 @@ namespace MediaBrowser.Controller.Entities return null; } + [IgnoreDataMember] + public virtual bool EnableRememberingTrackSelections + { + get + { + return true; + } + } + /// <summary> /// Adds a studio to the item /// </summary> @@ -1557,13 +1654,11 @@ 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) { - // Incremenet + // Increment data.PlayCount++; } @@ -1575,7 +1670,7 @@ namespace MediaBrowser.Controller.Entities data.PlaybackPositionTicks = 0; } - data.LastPlayedDate = datePlayed ?? data.LastPlayedDate; + data.LastPlayedDate = datePlayed ?? data.LastPlayedDate ?? DateTime.UtcNow; data.Played = true; await UserDataManager.SaveUserData(user.Id, this, data, UserDataSaveReason.TogglePlayed, CancellationToken.None).ConfigureAwait(false); @@ -1594,9 +1689,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 @@ -1788,7 +1881,7 @@ namespace MediaBrowser.Controller.Entities return new ItemImageInfo { Path = path, - DateModified = FileSystem.GetLastWriteTimeUtc(path), + DateModified = chapter.ImageDateModified, Type = imageType }; } @@ -1927,14 +2020,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)); } @@ -1946,7 +2039,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; } @@ -1977,7 +2070,6 @@ namespace MediaBrowser.Controller.Entities /// </summary> public virtual bool BeforeMetadataRefresh() { - _userDataKey = null; _sortName = null; var hasChanges = false; @@ -2004,7 +2096,7 @@ namespace MediaBrowser.Controller.Entities return path; } - public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) + public virtual Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user) { if (RunTimeTicks.HasValue) { @@ -2020,6 +2112,8 @@ namespace MediaBrowser.Controller.Entities } } } + + return Task.FromResult(true); } protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, string path, CancellationToken cancellationToken) @@ -2084,7 +2178,7 @@ namespace MediaBrowser.Controller.Entities { get { - if (GetParent() is AggregateFolder || this is Channel || this is BasePluginFolder) + if (GetParent() is AggregateFolder || this is BasePluginFolder || this is Channel) { return true; } @@ -2094,6 +2188,10 @@ namespace MediaBrowser.Controller.Entities { return true; } + if (view != null && string.Equals(view.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase)) + { + return true; + } return false; } @@ -2126,5 +2224,10 @@ namespace MediaBrowser.Controller.Entities DeleteFileLocation = false }); } + + public virtual List<ExternalUrl> GetRelatedUrls() + { + return new List<ExternalUrl>(); + } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index 1c86a53f0..59ab95437 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Providers; +using System; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using System.Linq; using System.Runtime.Serialization; @@ -6,7 +7,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities { - public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries + public class Book : BaseItem, IHasLookupInfo<BookInfo>, IHasSeries { [IgnoreDataMember] public override string MediaType @@ -17,7 +18,32 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] public string SeriesName { get; set; } + [IgnoreDataMember] + public Guid? SeriesId { get; set; } + [IgnoreDataMember] + public string SeriesSortName { get; set; } + + public string FindSeriesSortName() + { + return SeriesSortName; + } + public string FindSeriesName() + { + return SeriesName; + } + + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + + public Guid? FindSeriesId() + { + return SeriesId; + } public override bool CanDownload() { diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 429700327..35dfd52e9 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 { @@ -22,19 +23,6 @@ namespace MediaBrowser.Controller.Entities PhysicalLocationsList = new List<string>(); } - /// <summary> - /// Gets a value indicating whether this instance is virtual folder. - /// </summary> - /// <value><c>true</c> if this instance is virtual folder; otherwise, <c>false</c>.</value> - [IgnoreDataMember] - public override bool IsVirtualFolder - { - get - { - return true; - } - } - [IgnoreDataMember] protected override bool SupportsShortcutChildren { @@ -82,7 +70,34 @@ namespace MediaBrowser.Controller.Entities protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService) { - return CreateResolveArgs(directoryService).FileSystemChildren; + return CreateResolveArgs(directoryService, true).FileSystemChildren; + } + + private bool _requiresRefresh; + public override bool RequiresRefresh() + { + var changed = base.RequiresRefresh() || _requiresRefresh; + + if (!changed) + { + var locations = PhysicalLocations.ToList(); + + var newLocations = CreateResolveArgs(new DirectoryService(BaseItem.FileSystem), false).PhysicalLocations.ToList(); + + if (!locations.SequenceEqual(newLocations)) + { + changed = true; + } + } + + return changed; + } + + public override bool BeforeMetadataRefresh() + { + var changed = base.BeforeMetadataRefresh() || _requiresRefresh; + _requiresRefresh = false; + return changed; } internal override bool IsValidFromResolver(BaseItem newItem) @@ -97,11 +112,10 @@ namespace MediaBrowser.Controller.Entities } } - return base.IsValidFromResolver(newItem); } - private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService) + private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations) { var path = ContainingFolderPath; @@ -135,7 +149,11 @@ namespace MediaBrowser.Controller.Entities args.FileSystemDictionary = fileSystemDictionary; } - PhysicalLocationsList = args.PhysicalLocations.ToList(); + _requiresRefresh = _requiresRefresh || !args.PhysicalLocations.SequenceEqual(PhysicalLocations); + if (setPhysicalLocations) + { + PhysicalLocationsList = args.PhysicalLocations.ToList(); + } return args; } @@ -153,15 +171,6 @@ namespace MediaBrowser.Controller.Entities /// <returns>Task.</returns> protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { - var list = PhysicalLocationsList.ToList(); - - CreateResolveArgs(directoryService); - - if (!list.SequenceEqual(PhysicalLocationsList)) - { - return UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); - } - return Task.FromResult(true); } @@ -188,6 +197,7 @@ namespace MediaBrowser.Controller.Entities /// Our children are actually just references to the ones in the physical root... /// </summary> /// <value>The actual children.</value> + [IgnoreDataMember] protected override IEnumerable<BaseItem> ActualChildren { get { return GetActualChildren(); } @@ -200,9 +210,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 af1cbdf2c..0397e9a88 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -13,6 +13,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Model.Channels; namespace MediaBrowser.Controller.Entities @@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Class Folder /// </summary> - public class Folder : BaseItem, IHasThemeMedia, IHasTags + public class Folder : BaseItem, IHasThemeMedia { public static IUserManager UserManager { get; set; } public static IUserViewManager UserViewManager { get; set; } @@ -28,6 +29,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 +60,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 @@ -93,25 +127,12 @@ namespace MediaBrowser.Controller.Entities /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value> public bool IsRoot { get; set; } - /// <summary> - /// Gets a value indicating whether this instance is virtual folder. - /// </summary> - /// <value><c>true</c> if this instance is virtual folder; otherwise, <c>false</c>.</value> - [IgnoreDataMember] - public virtual bool IsVirtualFolder - { - get - { - return false; - } - } - public virtual List<LinkedChild> LinkedChildren { get; set; } [IgnoreDataMember] protected virtual bool SupportsShortcutChildren { - get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; } + get { return false; } } /// <summary> @@ -144,49 +165,15 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.UtcNow; } - AddChildInternal(item.Id); - await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); } - protected void AddChildrenInternal(List<Guid> children) - { - lock (_childrenSyncLock) - { - var newChildren = ChildIds.ToList(); - newChildren.AddRange(children); - _children = newChildren.ToList(); - } - } - protected void AddChildInternal(Guid child) - { - lock (_childrenSyncLock) - { - var childIds = ChildIds.ToList(); - if (!childIds.Contains(child)) - { - childIds.Add(child); - _children = childIds.ToList(); - } - } - } - - protected void RemoveChildrenInternal(List<Guid> children) - { - lock (_childrenSyncLock) - { - _children = ChildIds.Except(children).ToList(); - } - } - /// <summary> /// Removes the child. /// </summary> /// <param name="item">The item.</param> public void RemoveChild(BaseItem item) { - RemoveChildrenInternal(new[] { item.Id }.ToList()); - item.SetParent(null); } @@ -199,8 +186,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"}, @@ -222,41 +209,15 @@ namespace MediaBrowser.Controller.Entities #endregion /// <summary> - /// The children - /// </summary> - private IReadOnlyList<Guid> _children; - /// <summary> - /// The _children sync lock - /// </summary> - private readonly object _childrenSyncLock = new object(); - /// <summary> - /// Gets or sets the actual children. - /// </summary> - /// <value>The actual children.</value> - protected virtual IEnumerable<Guid> ChildIds - { - get - { - lock (_childrenSyncLock) - { - if (_children == null) - { - _children = LoadChildren().ToList(); - } - return _children.ToList(); - } - } - } - - /// <summary> /// Gets the actual children. /// </summary> /// <value>The actual children.</value> + [IgnoreDataMember] protected virtual IEnumerable<BaseItem> ActualChildren { get { - return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null); + return LoadChildren(); } } @@ -310,7 +271,7 @@ namespace MediaBrowser.Controller.Entities /// Loads our children. Validation will occur externally. /// We want this sychronous. /// </summary> - protected virtual IEnumerable<Guid> LoadChildren() + protected virtual IEnumerable<BaseItem> LoadChildren() { //just load our children from the repo - the library will be validated and maintained in other processes return GetCachedChildren(); @@ -412,13 +373,6 @@ namespace MediaBrowser.Controller.Entities if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { - var currentChildLocationType = currentChild.LocationType; - if (currentChildLocationType != LocationType.Remote && - currentChildLocationType != LocationType.Virtual) - { - currentChild.DateModified = child.DateModified; - } - await UpdateIsOffline(currentChild, false).ConfigureAwait(false); validChildren.Add(currentChild); @@ -440,17 +394,15 @@ namespace MediaBrowser.Controller.Entities foreach (var item in itemsRemoved) { - if (item.LocationType == LocationType.Virtual || - item.LocationType == LocationType.Remote) + var itemLocationType = item.LocationType; + if (itemLocationType == LocationType.Virtual || + itemLocationType == LocationType.Remote) { - // Don't remove these because there's no way to accurately validate them. - validChildren.Add(item); } else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) { await UpdateIsOffline(item, true).ConfigureAwait(false); - validChildren.Add(item); } else { @@ -460,8 +412,6 @@ namespace MediaBrowser.Controller.Entities if (actualRemovals.Count > 0) { - RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList()); - foreach (var item in actualRemovals) { Logger.Debug("Removed item: " + item.Path); @@ -474,8 +424,6 @@ namespace MediaBrowser.Controller.Entities } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); - - AddChildrenInternal(newItems.Select(i => i.Id).ToList()); } } @@ -703,20 +651,41 @@ namespace MediaBrowser.Controller.Entities /// Get our children from the repo - stubbed for now /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> - protected IEnumerable<Guid> GetCachedChildren() + protected IEnumerable<BaseItem> GetCachedChildren() { - return ItemRepository.GetItemIdsList(new InternalItemsQuery + return ItemRepository.GetItemList(new InternalItemsQuery { + ParentId = Id, + GroupByPresentationUniqueKey = false + }); + } + + public virtual int GetChildCount(User user) + { + if (LinkedChildren.Count > 0) + { + if (!(this is ICollectionFolder)) + { + return GetChildren(user, true).Count(); + } + } + + var result = GetItems(new InternalItemsQuery(user) + { + Recursive = false, + Limit = 0, ParentId = Id - }); + }).Result; + + return result.TotalRecordCount; } public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query) { var user = query.User; - if (RequiresPostFiltering(query)) + if (!query.ForceDirect && RequiresPostFiltering(query)) { IEnumerable<BaseItem> items; Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); @@ -727,7 +696,7 @@ namespace MediaBrowser.Controller.Entities } else { - items = GetRecursiveChildren(user, filter); + items = GetRecursiveChildren(user, query); } return PostFilterAndSort(items, query); @@ -747,63 +716,23 @@ namespace MediaBrowser.Controller.Entities { if (!(this is ICollectionFolder)) { - Logger.Debug("Query requires post-filtering due to LinkedChildren"); + Logger.Debug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name); return true; } } 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)) - { - 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"); - return true; - } - if (query.SortBy.Contains(ItemSortBy.Artist, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.Artist"); - return true; - } if (query.SortBy.Contains(ItemSortBy.Budget, StringComparer.OrdinalIgnoreCase)) { 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"); @@ -814,16 +743,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Metascore"); return true; } - if (query.SortBy.Contains(ItemSortBy.OfficialRating, StringComparer.OrdinalIgnoreCase)) - { - 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"); @@ -834,21 +753,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Revenue"); return true; } - if (query.SortBy.Contains(ItemSortBy.SeriesSortName, StringComparer.OrdinalIgnoreCase)) - { - 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"); - return true; - } if (query.SortBy.Contains(ItemSortBy.VideoBitRate, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.VideoBitRate"); @@ -862,42 +766,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.PersonIds.Length > 0) - { - Logger.Debug("Query requires post-filtering due to PersonIds"); - 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.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.IsPlayed.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsPlayed"); - return true; - } - if (query.IsInBoxSet.HasValue) { Logger.Debug("Query requires post-filtering due to IsInBoxSet"); @@ -911,30 +779,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.HasImdbId.HasValue) - { - Logger.Debug("Query requires post-filtering due to HasImdbId"); - return true; - } - - if (query.HasTmdbId.HasValue) - { - Logger.Debug("Query requires post-filtering due to HasTmdbId"); - return true; - } - - if (query.HasTvdbId.HasValue) - { - Logger.Debug("Query requires post-filtering due to HasTvdbId"); - return true; - } - - if (query.IsYearMismatched.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsYearMismatched"); - return true; - } - if (query.HasOfficialRating.HasValue) { Logger.Debug("Query requires post-filtering due to HasOfficialRating"); @@ -984,26 +828,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.ImageTypes.Length > 0) - { - Logger.Debug("Query requires post-filtering due to ImageTypes"); - return true; - } - - // Apply studio filter - if (query.StudioIds.Length > 0) - { - Logger.Debug("Query requires post-filtering due to StudioIds"); - return true; - } - - // Apply genre filter - if (query.GenreIds.Length > 0) - { - Logger.Debug("Query requires post-filtering due to GenreIds"); - return true; - } - // Apply person filter if (query.ItemIdsFromPersonFilters != null) { @@ -1023,31 +847,7 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.OfficialRatings.Length > 0) - { - Logger.Debug("Query requires post-filtering due to OfficialRatings"); - return true; - } - - if (query.IsMissing.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsMissing"); - return true; - } - - if (query.IsUnaired.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsUnaired"); - return true; - } - - if (query.IsVirtualUnaired.HasValue) - { - Logger.Debug("Query requires post-filtering due to IsVirtualUnaired"); - return true; - } - - if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User)) + if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User, ConfigurationManager)) { Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems"); return true; @@ -1059,30 +859,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,22 +883,21 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AlbumNames.Length > 0) - { - Logger.Debug("Query requires post-filtering due to AlbumNames"); - return true; - } + return false; + } - if (query.ArtistNames.Length > 0) + public Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + { + if (query.ItemIds.Length > 0) { - Logger.Debug("Query requires post-filtering due to ArtistNames"); - return true; + var specificItems = query.ItemIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList(); + return Task.FromResult(PostFilterAndSort(specificItems, query)); } - return false; + return GetItemsInternal(query); } - public virtual async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + protected virtual async Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) { if (SourceType == SourceType.Channel) { @@ -1171,18 +946,16 @@ namespace MediaBrowser.Controller.Entities else { items = query.Recursive - ? GetRecursiveChildren(user, filter) + ? GetRecursiveChildren(user, query) : GetChildren(user, true).Where(filter); } - var result = PostFilterAndSort(items, query); - - return result; + return PostFilterAndSort(items, query); } protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query) { - return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager); + return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager); } public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) @@ -1210,19 +983,14 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Adds the children to list. /// </summary> - /// <param name="user">The user.</param> - /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> - /// <param name="result">The result.</param> - /// <param name="recursive">if set to <c>true</c> [recursive].</param> - /// <param name="filter">The filter.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter) + private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, InternalItemsQuery query) { foreach (var child in GetEligibleChildrenForRecursiveChildren(user)) { if (child.IsVisible(user)) { - if (filter == null || filter(child)) + if (query == null || UserViewBuilder.FilterItem(child, query)) { result[child.Id] = child; } @@ -1231,7 +999,7 @@ namespace MediaBrowser.Controller.Entities { var folder = (Folder)child; - folder.AddChildren(user, includeLinkedChildren, result, true, filter); + folder.AddChildren(user, includeLinkedChildren, result, true, query); } } } @@ -1242,7 +1010,7 @@ namespace MediaBrowser.Controller.Entities { if (child.IsVisible(user)) { - if (filter == null || filter(child)) + if (query == null || UserViewBuilder.FilterItem(child, query)) { result[child.Id] = child; } @@ -1260,10 +1028,10 @@ namespace MediaBrowser.Controller.Entities /// <exception cref="System.ArgumentNullException"></exception> public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) { - return GetRecursiveChildren(user, i => true); + return GetRecursiveChildren(user, null); } - public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter) + public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query) { if (user == null) { @@ -1272,7 +1040,7 @@ namespace MediaBrowser.Controller.Entities var result = new Dictionary<Guid, BaseItem>(); - AddChildren(user, true, result, true, filter); + AddChildren(user, true, result, true, query); return result.Values; } @@ -1288,33 +1056,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; + } } } } @@ -1520,13 +1297,12 @@ namespace MediaBrowser.Controller.Entities User = user, Recursive = true, IsFolder = false, - IsUnaired = false - + EnableTotalRecordCount = false }; - if (!user.Configuration.DisplayMissingEpisodes) + if (!user.Configuration.DisplayMissingEpisodes || !user.Configuration.DisplayUnairedEpisodes) { - query.IsMissing = false; + query.ExcludeLocationTypes = new[] { LocationType.Virtual }; } var itemsResult = await GetItems(query).ConfigureAwait(false); @@ -1548,7 +1324,8 @@ namespace MediaBrowser.Controller.Entities { User = user, Recursive = true, - IsFolder = false + IsFolder = false, + EnableTotalRecordCount = false }).ConfigureAwait(false); @@ -1558,38 +1335,18 @@ 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)) - { - throw new ArgumentNullException(); - } - - if (string.Equals(Path, path, StringComparison.OrdinalIgnoreCase)) - { - return this; - } - - if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) + var itemsResult = GetItems(new InternalItemsQuery(user) { - return this; - } + Recursive = true, + IsFolder = false, + ExcludeLocationTypes = new[] { LocationType.Virtual }, + EnableTotalRecordCount = false - 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)); } @@ -1598,65 +1355,79 @@ namespace MediaBrowser.Controller.Entities return !IsPlayed(user); } - public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) + [IgnoreDataMember] + public virtual bool SupportsUserDataFromChildren { - var recursiveItemCount = 0; - var unplayed = 0; - - double totalPercentPlayed = 0; - - IEnumerable<BaseItem> children; - var folder = this; - - var season = folder as Season; - - if (season != null) + get { - children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual); + // These are just far too slow. + if (this is ICollectionFolder) + { + return false; + } + if (this is UserView) + { + return false; + } + if (this is UserRootFolder) + { + return false; + } + if (this is Channel) + { + return false; + } + if (SourceType != SourceType.Library) + { + return false; + } + + return true; } - else + } + + public override async Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user) + { + if (!SupportsUserDataFromChildren) { - children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual); + return; } - // Loop through each recursive child - foreach (var child in children) + var unplayedQueryResult = await GetItems(new InternalItemsQuery(user) { - recursiveItemCount++; - - var isUnplayed = true; + Recursive = true, + IsFolder = false, + IsVirtualItem = false, + EnableTotalRecordCount = true, + Limit = 0, + IsPlayed = false - var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); + }).ConfigureAwait(false); - // Incrememt totalPercentPlayed - if (itemUserData != null) - { - if (itemUserData.Played) - { - totalPercentPlayed += 100; + var allItemsQueryResult = await GetItems(new InternalItemsQuery(user) + { + Recursive = true, + IsFolder = false, + IsVirtualItem = false, + EnableTotalRecordCount = true, + Limit = 0 - isUnplayed = false; - } - else if (itemUserData.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0) - { - double itemPercent = itemUserData.PlaybackPositionTicks; - itemPercent /= child.RunTimeTicks.Value; - totalPercentPlayed += itemPercent; - } - } + }).ConfigureAwait(false); - if (isUnplayed) - { - unplayed++; - } + if (itemDto != null) + { + itemDto.RecursiveItemCount = allItemsQueryResult.TotalRecordCount; } - dto.UnplayedItemCount = unplayed; + double recursiveItemCount = allItemsQueryResult.TotalRecordCount; + double unplayedCount = unplayedQueryResult.TotalRecordCount; if (recursiveItemCount > 0) { - dto.PlayedPercentage = totalPercentPlayed / recursiveItemCount; + var unplayedPercentage = (unplayedCount / recursiveItemCount) * 100; + dto.PlayedPercentage = 100 - unplayedPercentage; dto.Played = dto.PlayedPercentage.Value >= 100; + dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount; } } } diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index e597b2a15..54386a179 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -4,10 +4,11 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities { - public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo> + public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo> { public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } @@ -32,6 +33,12 @@ namespace MediaBrowser.Controller.Entities locationType != LocationType.Virtual; } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + /// <summary> /// Gets or sets the remote trailers. /// </summary> @@ -42,6 +49,7 @@ namespace MediaBrowser.Controller.Entities /// Gets the type of the media. /// </summary> /// <value>The type of the media.</value> + [IgnoreDataMember] public override string MediaType { get { return Model.Entities.MediaType.Game; } @@ -76,15 +84,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..45e766c0f 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -2,18 +2,26 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Common.Extensions; 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, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + + public override string PresentationUniqueKey + { + get + { + return GetUserDataKeys()[0]; + } } /// <summary> @@ -63,6 +71,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..cc5aebb2a 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.Audio; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.Controller.Entities { @@ -11,13 +12,20 @@ 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, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + + public override string PresentationUniqueKey + { + get + { + return GetUserDataKeys()[0]; + } } /// <summary> @@ -67,6 +75,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/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index a38b7394d..226748098 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Providers; +using System; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Threading; diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 0e4ae04ff..378c4a390 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities /// Gets the date modified. /// </summary> /// <value>The date modified.</value> - DateTime DateModified { get; } + DateTime DateModified { get; set; } /// <summary> /// Gets or sets the date last saved. @@ -25,6 +25,8 @@ namespace MediaBrowser.Controller.Entities /// <value>The date last saved.</value> DateTime DateLastSaved { get; set; } + SourceType SourceType { get; set; } + /// <summary> /// Gets or sets the date last refreshed. /// </summary> @@ -47,5 +49,9 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value><c>true</c> if [supports people]; otherwise, <c>false</c>.</value> bool SupportsPeople { get; } + + bool RequiresRefresh(); + + bool EnableForceSaveOnDateModifiedChange { get; } } } diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs index 64c33a376..531f58788 100644 --- a/MediaBrowser.Controller/Entities/IHasSeries.cs +++ b/MediaBrowser.Controller/Entities/IHasSeries.cs @@ -1,4 +1,6 @@ +using System; + namespace MediaBrowser.Controller.Entities { public interface IHasSeries @@ -7,6 +9,11 @@ namespace MediaBrowser.Controller.Entities /// Gets the name of the series. /// </summary> /// <value>The name of the series.</value> - string SeriesName { get; } + string SeriesName { get; set; } + string FindSeriesName(); + string SeriesSortName { get; set; } + string FindSeriesSortName(); + Guid? SeriesId { get; set; } + Guid? FindSeriesId(); } } diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs index bc1c7d875..e5cbdff72 100644 --- a/MediaBrowser.Controller/Entities/IHasTrailers.cs +++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs @@ -1,6 +1,7 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; +using System.Linq; namespace MediaBrowser.Controller.Entities { @@ -18,11 +19,20 @@ namespace MediaBrowser.Controller.Entities /// <value>The local trailer ids.</value> List<Guid> LocalTrailerIds { get; set; } List<Guid> RemoteTrailerIds { get; set; } + } + public static class HasTrailerExtensions + { /// <summary> /// Gets the trailer ids. /// </summary> /// <returns>List<Guid>.</returns> - List<Guid> GetTrailerIds(); + public static List<Guid> GetTrailerIds(this IHasTrailers item) + { + var list = item.LocalTrailerIds.ToList(); + list.AddRange(item.RemoteTrailerIds); + return list; + } + } } diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs index 34a820853..2495b0ccd 100644 --- a/MediaBrowser.Controller/Entities/IHasUserData.cs +++ b/MediaBrowser.Controller/Entities/IHasUserData.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Dto; +using System.Collections.Generic; +using System.Threading.Tasks; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Entities { @@ -7,11 +9,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. @@ -19,6 +17,8 @@ namespace MediaBrowser.Controller.Entities /// <param name="dto">The dto.</param> /// <param name="userData">The user data.</param> /// <param name="user">The user.</param> - void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user); + Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user); + + bool EnableRememberingTrackSelections { get; } } } diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs index e6667290c..2ac4af1af 100644 --- a/MediaBrowser.Controller/Entities/IItemByName.cs +++ b/MediaBrowser.Controller/Entities/IItemByName.cs @@ -15,11 +15,7 @@ namespace MediaBrowser.Controller.Entities /// <returns>IEnumerable{BaseItem}.</returns> IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems); - /// <summary> - /// Gets the item filter. - /// </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 b568aec18..69cab5ec5 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Entities public User User { get; set; } - public Func<BaseItem, bool> Filter { get; set; } + public BaseItem SimilarTo { get; set; } public bool? IsFolder { get; set; } public bool? IsFavorite { get; set; } @@ -33,7 +33,9 @@ namespace MediaBrowser.Controller.Entities public string[] IncludeItemTypes { get; set; } public string[] ExcludeItemTypes { get; set; } public string[] ExcludeTags { get; set; } + public string[] ExcludeInheritedTags { get; set; } public string[] Genres { get; set; } + public string[] Keywords { get; set; } public bool? IsMissing { get; set; } public bool? IsUnaired { get; set; } @@ -44,12 +46,17 @@ namespace MediaBrowser.Controller.Entities public string NameStartsWith { get; set; } public string NameLessThan { get; set; } public string NameContains { get; set; } + public string MinSortName { 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; } + public string[] ExcludeItemIds { get; set; } public string AdjacentTo { get; set; } public string[] PersonTypes { get; set; } @@ -58,7 +65,6 @@ namespace MediaBrowser.Controller.Entities public bool? IsInBoxSet { get; set; } public bool? IsLocked { get; set; } public bool? IsPlaceHolder { get; set; } - public bool? IsYearMismatched { get; set; } public bool? HasImdbId { get; set; } public bool? HasOverview { get; set; } @@ -105,12 +111,15 @@ namespace MediaBrowser.Controller.Entities internal List<Guid> ItemIdsFromPersonFilters { get; set; } public int? ParentIndexNumber { get; set; } + public int? ParentIndexNumberNotEquals { get; set; } + public int? IndexNumber { get; set; } public int? MinParentalRating { get; set; } public int? MaxParentalRating { get; set; } public bool? IsCurrentSchema { get; set; } public bool? HasDeadParentId { get; set; } public bool? IsOffline { get; set; } + public bool? IsVirtualItem { get; set; } public Guid? ParentId { get; set; } public string[] AncestorIds { get; set; } @@ -122,7 +131,6 @@ namespace MediaBrowser.Controller.Entities public SourceType[] SourceTypes { get; set; } public SourceType[] ExcludeSourceTypes { get; set; } public TrailerType[] TrailerTypes { get; set; } - public TrailerType[] ExcludeTrailerTypes { get; set; } public DayOfWeek[] AirDays { get; set; } public SeriesStatus[] SeriesStatuses { get; set; } @@ -130,17 +138,31 @@ namespace MediaBrowser.Controller.Entities public string[] AlbumNames { get; set; } public string[] ArtistNames { get; set; } - + public string[] ExcludeArtistIds { get; set; } + public string AncestorWithPresentationUniqueKey { get; set; } + + public bool GroupByPresentationUniqueKey { get; set; } + public bool EnableTotalRecordCount { get; set; } + public bool ForceDirect { get; set; } + public Dictionary<string, string> ExcludeProviderIds { get; set; } + public bool EnableGroupByMetadataKey { get; set; } + public InternalItemsQuery() { + GroupByPresentationUniqueKey = true; + EnableTotalRecordCount = true; + AlbumNames = new string[] { }; ArtistNames = new string[] { }; - + ExcludeArtistIds = new string[] { }; + ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + BlockUnratedItems = new UnratedItem[] { }; Tags = new string[] { }; OfficialRatings = new string[] { }; SortBy = new string[] { }; MediaTypes = new string[] { }; + Keywords = new string[] { }; IncludeItemTypes = new string[] { }; ExcludeItemTypes = new string[] { }; Genres = new string[] { }; @@ -154,16 +176,17 @@ namespace MediaBrowser.Controller.Entities PersonIds = new string[] { }; ChannelIds = new string[] { }; ItemIds = new string[] { }; + ExcludeItemIds = new string[] { }; AncestorIds = new string[] { }; TopParentIds = new string[] { }; ExcludeTags = new string[] { }; + ExcludeInheritedTags = new string[] { }; LocationTypes = new LocationType[] { }; ExcludeLocationTypes = new LocationType[] { }; PresetViews = new string[] { }; SourceTypes = new SourceType[] { }; ExcludeSourceTypes = new SourceType[] { }; TrailerTypes = new TrailerType[] { }; - ExcludeTrailerTypes = new TrailerType[] { }; AirDays = new DayOfWeek[] { }; SeriesStatuses = new SeriesStatus[] { }; } @@ -171,6 +194,11 @@ namespace MediaBrowser.Controller.Entities public InternalItemsQuery(User user) : this() { + SetUser(user); + } + + public void SetUser(User user) + { if (user != null) { var policy = user.Policy; @@ -181,7 +209,7 @@ namespace MediaBrowser.Controller.Entities BlockUnratedItems = policy.BlockUnratedItems; } - ExcludeTags = policy.BlockedTags; + ExcludeInheritedTags = policy.BlockedTags; User = user; } diff --git a/MediaBrowser.Controller/Entities/IHasKeywords.cs b/MediaBrowser.Controller/Entities/KeywordExtensions.cs index ab9eb4aee..5c9afdf3d 100644 --- a/MediaBrowser.Controller/Entities/IHasKeywords.cs +++ b/MediaBrowser.Controller/Entities/KeywordExtensions.cs @@ -1,21 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Controller.Entities { - public interface IHasKeywords - { - /// <summary> - /// Gets or sets the keywords. - /// </summary> - /// <value>The keywords.</value> - List<string> Keywords { get; set; } - } - public static class KeywordExtensions { - public static void AddKeyword(this IHasKeywords item, string name) + public static void AddKeyword(this BaseItem item, string name) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index cd3e07ea3..4effc162e 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -8,13 +8,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Controller.Entities.Movies { /// <summary> /// Class BoxSet /// </summary> - public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares + public class BoxSet : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares { public List<Share> Shares { get; set; } @@ -25,7 +26,6 @@ namespace MediaBrowser.Controller.Entities.Movies RemoteTrailerIds = new List<Guid>(); DisplayOrder = ItemSortBy.PremiereDate; - Keywords = new List<string>(); Shares = new List<Share>(); } @@ -47,12 +47,6 @@ namespace MediaBrowser.Controller.Entities.Movies public List<MediaUrl> RemoteTrailers { get; set; } /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Keywords { get; set; } - - /// <summary> /// Gets or sets the display order. /// </summary> /// <value>The display order.</value> @@ -118,7 +112,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 f5c2a9935..c7a833c58 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -8,18 +8,17 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Entities.Movies { /// <summary> /// Class Movie /// </summary> - public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle + public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle { 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; } @@ -33,7 +32,6 @@ namespace MediaBrowser.Controller.Entities.Movies ThemeSongIds = new List<Guid>(); ThemeVideoIds = new List<Guid>(); Taglines = new List<string>(); - Keywords = new List<string>(); ProductionLocations = new List<string>(); } @@ -43,7 +41,6 @@ namespace MediaBrowser.Controller.Entities.Movies public List<Guid> LocalTrailerIds { get; set; } public List<Guid> RemoteTrailerIds { get; set; } - public List<string> Keywords { get; set; } public List<MediaUrl> RemoteTrailers { get; set; } @@ -77,45 +74,6 @@ namespace MediaBrowser.Controller.Entities.Movies get { return TmdbCollectionName; } set { TmdbCollectionName = value; } } - - /// <summary> - /// Gets the trailer ids. - /// </summary> - /// <returns>List<Guid>.</returns> - public List<Guid> GetTrailerIds() - { - var list = LocalTrailerIds.ToList(); - list.AddRange(RemoteTrailerIds); - return list; - } - - /// <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) { @@ -204,5 +162,22 @@ namespace MediaBrowser.Controller.Entities.Movies return hasChanges; } + + public override List<ExternalUrl> GetRelatedUrls() + { + var list = base.GetRelatedUrls(); + + var imdbId = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(imdbId)) + { + list.Add(new ExternalUrl + { + Name = "Trakt", + Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + }); + } + + return list; + } } } 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..8ef0d70bf 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -18,13 +19,20 @@ 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, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + + public override string PresentationUniqueKey + { + get + { + return GetUserDataKeys()[0]; + } } public PersonLookupInfo GetLookupInfo() @@ -32,6 +40,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/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 3358ccc6f..804ea04a5 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -5,7 +5,7 @@ using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities { - public class Photo : BaseItem, IHasTags, IHasTaglines + public class Photo : BaseItem, IHasTaglines { public List<string> Taglines { get; set; } @@ -51,6 +51,12 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public override bool CanDownload() { return true; 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..762798b55 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -2,21 +2,29 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Common.Extensions; namespace MediaBrowser.Controller.Entities { /// <summary> /// Class Studio /// </summary> - public class Studio : BaseItem, IItemByName, IHasTags + public class Studio : BaseItem, IItemByName { - /// <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, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + + public override string PresentationUniqueKey + { + get + { + return GetUserDataKeys()[0]; + } } /// <summary> @@ -66,6 +74,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 88dae3c7c..726390f65 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -11,8 +11,19 @@ namespace MediaBrowser.Controller.Entities.TV /// <summary> /// Class Episode /// </summary> - public class Episode : Video, IHasLookupInfo<EpisodeInfo>, IHasSeries - { + public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries + {
+ public Episode()
+ {
+ RemoteTrailers = new List<MediaUrl>();
+ LocalTrailerIds = new List<Guid>();
+ RemoteTrailerIds = new List<Guid>();
+ }
+
+ public List<Guid> LocalTrailerIds { get; set; }
+ public List<Guid> RemoteTrailerIds { get; set; }
+ public List<MediaUrl> RemoteTrailers { get; set; }
+
/// <summary> /// Gets the season in which it aired. /// </summary> @@ -42,7 +53,16 @@ namespace MediaBrowser.Controller.Entities.TV /// This is the ending episode number for double episodes. /// </summary> /// <value>The index number.</value> - public int? IndexNumberEnd { get; set; } + public int? IndexNumberEnd { get; set; }
+
+ [IgnoreDataMember]
+ public string SeriesSortName { get; set; }
+
+ public string FindSeriesSortName()
+ {
+ var series = Series;
+ return series == null ? SeriesSortName : series.SortName;
+ }
[IgnoreDataMember] protected override bool SupportsOwnedItems @@ -58,60 +78,54 @@ 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 BaseItem DisplayParent + protected override bool EnableDefaultVideoUserDataKeys { get { - return Season ?? GetParent(); + 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"); + var seriesUserDataKeys = series.GetUserDataKeys(); + var take = seriesUserDataKeys.Count; + if (seriesUserDataKeys.Count > 1) + { + take--; + } + list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); } - return base.CreateUserDataKey(); + return list; } /// <summary> @@ -160,13 +174,27 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] - public string SeriesName - { - get - { - var series = Series; - return series == null ? null : series.Name; - } + public string SeriesName { get; set; }
+
+ [IgnoreDataMember]
+ public string SeasonName { get; set; } + + public string FindSeasonName() + {
+ var season = Season;
+ return season == null ? SeasonName : season.Name;
+ }
+
+ public string FindSeriesName()
+ {
+ var series = Series;
+ return series == null ? SeriesName : series.Name;
+ }
+
+ public Guid? FindSeasonId()
+ {
+ var season = Season;
+ return season == null ? (Guid?)null : season.Id;
} /// <summary> @@ -175,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.TV /// <returns>System.String.</returns> protected override string CreateSortName() { - return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000-") : "") + return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ") : "") + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; } @@ -223,32 +251,20 @@ 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; } } [IgnoreDataMember] - public Guid? SeasonId - { - get - { - // First see if the parent is a Season - var season = Season; - - if (season != null) - { - return season.Id; - } - - return null; - } + public Guid? SeasonId { get; set; }
+ [IgnoreDataMember]
+ public Guid? SeriesId { get; set; }
+
+ public Guid? FindSeriesId()
+ {
+ var series = Series;
+ return series == null ? (Guid?)null : series.Id;
} public override IEnumerable<Guid> GetAncestorIds() @@ -287,7 +303,9 @@ namespace MediaBrowser.Controller.Entities.TV id.AnimeSeriesIndex = series.AnimeSeriesIndex; } + id.IsMissingEpisode = IsMissingEpisode; id.IndexNumberEnd = IndexNumberEnd; + id.IsVirtualUnaired = IsVirtualUnaired; return id; } @@ -308,6 +326,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 9efa609ef..ee01c60b1 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -33,35 +33,60 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] - public override BaseItem DisplayParent + public override bool SupportsDateLastMediaAdded { - get { return Series ?? GetParent(); } + get + { + return true; + } + } + + [IgnoreDataMember] + public override Guid? DisplayParentId + { + get + { + var series = Series; + return series == null ? ParentId : series.Id; + } + } + + [IgnoreDataMember] + public string SeriesSortName { get; set; } + + public string FindSeriesSortName() + { + var series = Series; + return series == null ? SeriesSortName : series.SortName; } // 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; + } + + public override int GetChildCount(User user) + { + return GetChildren(user, true).Count(); } /// <summary> @@ -90,6 +115,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> @@ -102,25 +145,13 @@ namespace MediaBrowser.Controller.Entities.TV [IgnoreDataMember] public bool IsMissingSeason { - get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); } - } - - [IgnoreDataMember] - public bool IsUnaired - { - get { return GetEpisodes().All(i => i.IsUnaired); } + get { return (IsVirtualItem) && !IsUnaired; } } [IgnoreDataMember] public bool IsVirtualUnaired { - get { return LocationType == LocationType.Virtual && IsUnaired; } - } - - [IgnoreDataMember] - public bool IsMissingOrVirtualUnaired - { - get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } + get { return (IsVirtualItem) && IsUnaired; } } [IgnoreDataMember] @@ -129,24 +160,18 @@ namespace MediaBrowser.Controller.Entities.TV get { return (IndexNumber ?? -1) == 0; } } - public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + 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); @@ -162,55 +187,20 @@ namespace MediaBrowser.Controller.Entities.TV { var config = user.Configuration; - return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); + return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } - public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + public IEnumerable<Episode> GetEpisodes(Series series, 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); - } - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - var list = episodes.ToList(); - - if (seasonNumber.HasValue) - { - list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); - } - else - { - list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>() - .Where(i => !i.ParentIndexNumber.HasValue)); - } - - episodes = list.DistinctBy(i => i.Id); - } - - if (!includeMissingEpisodes) - { - episodes = episodes.Where(i => !i.IsMissingEpisode); - } - if (!includeVirtualUnairedEpisodes) - { - episodes = episodes.Where(i => !i.IsVirtualUnaired); - } + return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null); + } - return LibraryManager - .Sort(episodes, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) - .Cast<Episode>(); + public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes) + { + return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes); } - private IEnumerable<Episode> GetEpisodes() + public IEnumerable<Episode> GetEpisodes() { var episodes = GetRecursiveChildren().OfType<Episode>(); var series = Series; @@ -254,13 +244,21 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] - public string SeriesName + public string SeriesName { get; set; } + + [IgnoreDataMember] + public Guid? SeriesId { get; set; } + + public string FindSeriesName() { - get - { - var series = Series; - return series == null ? null : series.Name; - } + var series = Series; + return series == null ? SeriesName : series.Name; + } + + public Guid? FindSeriesId() + { + var series = Series; + return series == null ? (Guid?)null : series.Id; } /// <summary> @@ -290,19 +288,14 @@ namespace MediaBrowser.Controller.Entities.TV { var hasChanges = base.BeforeMetadataRefresh(); - var locationType = LocationType; - - if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) + if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) { - if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) - { - IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path); + IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path); - // If a change was made record it - if (IndexNumber.HasValue) - { - hasChanges = true; - } + // If a change was made record it + if (IndexNumber.HasValue) + { + hasChanges = true; } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index aa07ab378..ad35b3d36 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -9,6 +9,8 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Providers; +using MoreLinq; namespace MediaBrowser.Controller.Entities.TV { @@ -19,8 +21,6 @@ namespace MediaBrowser.Controller.Entities.TV { public List<Guid> SpecialFeatureIds { get; set; } - public string OriginalTitle { get; set; } - public int? AnimeSeriesIndex { get; set; } public Series() @@ -31,7 +31,6 @@ namespace MediaBrowser.Controller.Entities.TV RemoteTrailers = new List<MediaUrl>(); LocalTrailerIds = new List<Guid>(); RemoteTrailerIds = new List<Guid>(); - DisplaySpecialsWithSeasons = true; } [IgnoreDataMember] @@ -49,7 +48,14 @@ namespace MediaBrowser.Controller.Entities.TV } } - public bool DisplaySpecialsWithSeasons { get; set; } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } public List<Guid> LocalTrailerIds { get; set; } public List<Guid> RemoteTrailerIds { get; set; } @@ -86,32 +92,69 @@ namespace MediaBrowser.Controller.Entities.TV { get { - return GetRecursiveChildren(i => i is Episode) - .Select(i => i.DateCreated) - .OrderByDescending(i => i) - .FirstOrDefault(); + return DateLastMediaAdded ?? DateTime.MinValue; } } + [IgnoreDataMember] + public override string PresentationUniqueKey + { + get + { + var userdatakeys = GetUserDataKeys(); + + if (userdatakeys.Count > 1) + { + return userdatakeys[0]; + } + return base.PresentationUniqueKey; + } + } + + private static string GetUniqueSeriesKey(BaseItem series) + { + if (ConfigurationManager.Configuration.SchemaVersion < 97) + { + return series.Id.ToString("N"); + } + return series.PresentationUniqueKey; + } + + public override int GetChildCount(User user) + { + var result = LibraryManager.GetItemsResult(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), + IncludeItemTypes = new[] { typeof(Season).Name }, + SortBy = new[] { ItemSortBy.SortName }, + IsVirtualItem = false, + Limit = 0 + }); + + return result.TotalRecordCount; + } + /// <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 +171,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"}, @@ -157,56 +200,59 @@ namespace MediaBrowser.Controller.Entities.TV return GetSeasons(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } - public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery 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); + return base.GetItemsInternal(query); } - else + + var user = query.User; + + if (query.Recursive) { - items = query.Recursive - ? GetRecursiveChildren(user, filter) - : GetSeasons(user).Where(filter); + query.AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this); + if (query.SortBy.Length == 0) + { + query.SortBy = new[] { ItemSortBy.SortName }; + } + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }; + } + query.IsVirtualItem = false; + return Task.FromResult(LibraryManager.GetItemsResult(query)); } - var result = PostFilterAndSort(items, query); + Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + var items = GetSeasons(user).Where(filter); + var result = PostFilterAndSort(items, query); return Task.FromResult(result); } public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) { - var seasons = base.GetChildren(user, true) - .OfType<Season>(); + IEnumerable<Season> seasons; + + seasons = LibraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), + IncludeItemTypes = new[] { typeof(Season).Name }, + SortBy = new[] { ItemSortBy.SortName } + + }).Cast<Season>(); - if (!includeMissingSeasons && !includeVirtualUnaired) + if (!includeMissingSeasons) { - seasons = seasons.Where(i => !i.IsMissingOrVirtualUnaired); + seasons = seasons.Where(i => !(i.IsMissingSeason)); } - else + if (!includeVirtualUnaired) { - if (!includeMissingSeasons) - { - seasons = seasons.Where(i => !i.IsMissingSeason); - } - if (!includeVirtualUnaired) - { - seasons = seasons.Where(i => !i.IsVirtualUnaired); - } + seasons = seasons.Where(i => !i.IsVirtualUnaired); } - return LibraryManager - .Sort(seasons, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) - .Cast<Season>(); + return seasons; } public IEnumerable<Episode> GetEpisodes(User user) @@ -218,25 +264,26 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired) { - var allEpisodes = GetSeasons(user, true, true) - .SelectMany(i => i.GetEpisodes(user, includeMissing, includeVirtualUnaired)) + var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), + IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }, + SortBy = new[] { ItemSortBy.SortName } + + }).ToList(); + + var allSeriesEpisodes = allItems.OfType<Episode>().ToList(); + + var allEpisodes = allItems.OfType<Season>() + .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes)) .Reverse() .ToList(); // 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) @@ -251,13 +298,10 @@ namespace MediaBrowser.Controller.Entities.TV var totalItems = seasons.Count + otherItems.Count; var numComplete = 0; - refreshOptions = new MetadataRefreshOptions(refreshOptions); - refreshOptions.IsPostRecursiveRefresh = true; - // Refresh current item await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - // Refresh TV + // Refresh seasons foreach (var item in seasons) { cancellationToken.ThrowIfCancellationRequested(); @@ -270,12 +314,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 == 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; @@ -283,32 +345,46 @@ namespace MediaBrowser.Controller.Entities.TV progress.Report(percent * 100); } + refreshOptions = new MetadataRefreshOptions(refreshOptions); + refreshOptions.IsPostRecursiveRefresh = true; await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); progress.Report(100); } - public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber) + public IEnumerable<Episode> GetEpisodes(User user, Season season) { var config = user.Configuration; - return GetEpisodes(user, seasonNumber, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); + return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } - public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + private IEnumerable<Episode> GetAllEpisodes(User user) { - return GetEpisodes(user, seasonNumber, includeMissingEpisodes, includeVirtualUnairedEpisodes, - new List<Episode>()); + return LibraryManager.GetItemList(new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.SortName } + + }).Cast<Episode>(); } - internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes) + public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) { - var episodes = GetRecursiveChildren(user, i => i is Episode) - .Cast<Episode>(); + IEnumerable<Episode> episodes = GetAllEpisodes(user); - episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons); + return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + } - episodes = episodes.Concat(additionalEpisodes).Distinct(); + public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes) + { + if (allSeriesEpisodes == null) + { + return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes); + } + + var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); if (!includeMissingEpisodes) { @@ -319,7 +395,7 @@ namespace MediaBrowser.Controller.Entities.TV episodes = episodes.Where(i => !i.IsVirtualUnaired); } - var sortBy = seasonNumber == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; + var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending) .Cast<Episode>(); @@ -328,15 +404,11 @@ namespace MediaBrowser.Controller.Entities.TV /// <summary> /// Filters the episodes by season. /// </summary> - /// <param name="episodes">The episodes.</param> - /// <param name="seasonNumber">The season number.</param> - /// <param name="includeSpecials">if set to <c>true</c> [include specials].</param> - /// <returns>IEnumerable{Episode}.</returns> public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials) { if (!includeSpecials || seasonNumber < 1) { - return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber); + return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber); } return episodes.Where(i => @@ -354,6 +426,34 @@ namespace MediaBrowser.Controller.Entities.TV }); } + /// <summary> + /// Filters the episodes by season. + /// </summary> + public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, Season parentSeason, bool includeSpecials) + { + var seasonNumber = parentSeason.IndexNumber; + var seasonPresentationKey = GetUniqueSeriesKey(parentSeason); + + var supportSpecialsInSeason = includeSpecials && seasonNumber.HasValue && seasonNumber.Value != 0; + + return episodes.Where(episode => + { + var currentSeasonNumber = supportSpecialsInSeason ? episode.AiredSeasonNumber : episode.ParentIndexNumber; + if (currentSeasonNumber.HasValue && seasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber.Value) + { + return true; + } + + if (!currentSeasonNumber.HasValue && !seasonNumber.HasValue && parentSeason.LocationType == LocationType.Virtual) + { + return true; + } + + var season = episode.Season; + return season != null && string.Equals(GetUniqueSeriesKey(season), seasonPresentationKey, StringComparison.OrdinalIgnoreCase); + }); + } + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Series); @@ -392,5 +492,22 @@ namespace MediaBrowser.Controller.Entities.TV return hasChanges; } + + public override List<ExternalUrl> GetRelatedUrls() + { + var list = base.GetRelatedUrls(); + + var imdbId = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(imdbId)) + { + list.Add(new ExternalUrl + { + Name = "Trakt", + Url = string.Format("https://trakt.tv/shows/{0}", imdbId) + }); + } + + return list; + } } } diff --git a/MediaBrowser.Controller/Entities/IHasTags.cs b/MediaBrowser.Controller/Entities/TagExtensions.cs index 45a56009d..0e1df72cd 100644 --- a/MediaBrowser.Controller/Entities/IHasTags.cs +++ b/MediaBrowser.Controller/Entities/TagExtensions.cs @@ -1,24 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Controller.Entities { - /// <summary> - /// Interface IHasTags - /// </summary> - public interface IHasTags - { - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - List<string> Tags { get; set; } - } - public static class TagExtensions { - public static void AddTag(this IHasTags item, string name) + public static void AddTag(this BaseItem item, string name) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index bd7d30858..eab5ab679 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -5,13 +5,14 @@ using System.Collections.Generic; using System.Globalization; using System.Runtime.Serialization; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Model.Providers; 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, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo> { public List<string> ProductionLocations { get; set; } @@ -21,17 +22,15 @@ namespace MediaBrowser.Controller.Entities Taglines = new List<string>(); Keywords = new List<string>(); ProductionLocations = new List<string>(); - TrailerTypes = new List<TrailerType>(); + TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer }; } public List<TrailerType> TrailerTypes { get; set; } - + public float? Metascore { get; set; } public List<MediaUrl> RemoteTrailers { get; set; } - public List<string> Keywords { get; set; } - [IgnoreDataMember] public bool IsLocalTrailer { @@ -56,26 +55,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; @@ -86,7 +65,7 @@ namespace MediaBrowser.Controller.Entities var info = GetItemLookupInfo<TrailerInfo>(); info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer); - + if (!IsInMixedFolder) { info.Name = System.IO.Path.GetFileName(ContainingFolderPath); @@ -130,5 +109,22 @@ namespace MediaBrowser.Controller.Entities return hasChanges; } + + public override List<ExternalUrl> GetRelatedUrls() + { + var list = base.GetRelatedUrls(); + + var imdbId = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(imdbId)) + { + list.Add(new ExternalUrl + { + Name = "Trakt", + Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + }); + } + + return list; + } } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index e5b39003d..5c68308f5 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -305,14 +305,7 @@ namespace MediaBrowser.Controller.Entities public bool IsFolderGrouped(Guid id) { - var config = Configuration; - - if (config.ExcludeFoldersFromGrouping != null) - { - return !config.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).Contains(id); - } - - return config.GroupedFolders.Select(i => new Guid(i)).Contains(id); + return Configuration.GroupedFolders.Select(i => new Guid(i)).Contains(id); } [IgnoreDataMember] 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 104408860..8e6f11c2c 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Entities { @@ -17,7 +18,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class UserRootFolder : Folder { - public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + protected override async Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) { if (query.Recursive) { @@ -37,6 +38,11 @@ namespace MediaBrowser.Controller.Entities return PostFilterAndSort(result.Where(filter), query); } + public override int GetChildCount(User user) + { + return GetChildren(user, true).Count(); + } + [IgnoreDataMember] protected override bool SupportsShortcutChildren { @@ -63,15 +69,6 @@ namespace MediaBrowser.Controller.Entities return list; } - /// <summary> - /// Get the children of this folder from the actual file system - /// </summary> - /// <returns>IEnumerable{BaseItem}.</returns> - protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService) - { - return base.GetNonCachedChildren(directoryService); - } - public override bool BeforeMetadataRefresh() { var hasChanges = base.BeforeMetadataRefresh(); @@ -101,10 +98,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/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 41c19f11d..35375e7e6 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -45,7 +45,12 @@ namespace MediaBrowser.Controller.Entities return list; } - public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + public override int GetChildCount(User user) + { + return GetChildren(user, true).Count(); + } + + protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) { var parent = this as Folder; @@ -58,7 +63,7 @@ namespace MediaBrowser.Controller.Entities parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent; } - return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, CollectionManager, PlaylistManager) + return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager, PlaylistManager) .GetUserItems(parent, this, ViewType, query); } @@ -66,7 +71,8 @@ namespace MediaBrowser.Controller.Entities { var result = GetItems(new InternalItemsQuery { - User = user + User = user, + EnableTotalRecordCount = false }).Result; @@ -83,17 +89,19 @@ namespace MediaBrowser.Controller.Entities return true; } - public override IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter) + public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query) { var result = GetItems(new InternalItemsQuery { User = user, Recursive = true, - Filter = filter + EnableTotalRecordCount = false, + + ForceDirect = true }).Result; - return result.Items; + return result.Items.Where(i => UserViewBuilder.FilterItem(i, query)); } protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 2df2227bc..e2228bcaf 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -18,6 +18,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Configuration; +using MoreLinq; namespace MediaBrowser.Controller.Entities { @@ -30,10 +33,10 @@ namespace MediaBrowser.Controller.Entities private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; private readonly ITVSeriesManager _tvSeriesManager; - private readonly ICollectionManager _collectionManager; + private readonly IServerConfigurationManager _config; private readonly IPlaylistManager _playlistManager; - public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, ICollectionManager collectionManager, IPlaylistManager playlistManager) + public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config, IPlaylistManager playlistManager) { _userViewManager = userViewManager; _liveTvManager = liveTvManager; @@ -42,7 +45,7 @@ namespace MediaBrowser.Controller.Entities _logger = logger; _userDataManager = userDataManager; _tvSeriesManager = tvSeriesManager; - _collectionManager = collectionManager; + _config = config; _playlistManager = playlistManager; } @@ -125,13 +128,17 @@ namespace MediaBrowser.Controller.Entities case CollectionType.HomeVideos: case CollectionType.Games: case CollectionType.MusicVideos: - { - if (query.Recursive) { - return GetResult(queryParent.GetRecursiveChildren(user, true), queryParent, query); + if (query.Recursive) + { + query.Recursive = true; + query.ParentId = queryParent.Id; + query.SetUser(user); + + return _libraryManager.GetItemsResult(query); + } + return GetResult(queryParent.GetChildren(user, true), queryParent, query); } - return GetResult(queryParent.GetChildren(user, true), queryParent, query); - } case CollectionType.Folders: return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query); @@ -140,7 +147,7 @@ namespace MediaBrowser.Controller.Entities return await GetPlaylistsView(queryParent, user, query).ConfigureAwait(false); case CollectionType.BoxSets: - return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false); + return GetBoxsetView(queryParent, user, query); case CollectionType.TvShows: return await GetTvView(queryParent, user, query).ConfigureAwait(false); @@ -155,7 +162,7 @@ namespace MediaBrowser.Controller.Entities return await GetTvGenres(queryParent, user, query).ConfigureAwait(false); case SpecialFolder.TvGenre: - return await GetTvGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false); + return GetTvGenreItems(queryParent, displayParent, user, query); case SpecialFolder.TvResume: return GetTvResume(queryParent, user, query); @@ -206,7 +213,7 @@ namespace MediaBrowser.Controller.Entities return GetMusicLatest(queryParent, user, query); case SpecialFolder.MusicPlaylists: - return await GetMusicPlaylists(queryParent, user, query).ConfigureAwait(false); + return GetMusicPlaylists(queryParent, user, query); case SpecialFolder.MusicAlbums: return GetMusicAlbums(queryParent, user, query); @@ -250,9 +257,15 @@ namespace MediaBrowser.Controller.Entities { if (query.Recursive) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => FilterItem(i, query)); + query.Recursive = true; + query.SetUser(user); - return PostFilterAndSort(items, parent, null, query); + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(Audio.Audio).Name, typeof(MusicVideo).Name }; + } + + return parent.QueryRecursive(query); } var list = new List<BaseItem>(); @@ -310,21 +323,26 @@ namespace MediaBrowser.Controller.Entities private async Task<QueryResult<BaseItem>> GetMusicGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }) - .Where(i => !i.IsFolder) - .Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)) - .OfType<IHasAlbumArtist>(); + query.Recursive = true; + query.ParentId = queryParent.Id; + query.Genres = new[] { displayParent.Name }; + query.SetUser(user); - var artists = _libraryManager.GetAlbumArtists(items); + query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name }; - return GetResult(artists, queryParent, query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMusicAlbumArtists(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }) - .Where(i => !i.IsFolder) - .OfType<IHasAlbumArtist>(); + var items = parent.QueryRecursive(new InternalItemsQuery(user) + { + Recursive = true, + ParentId = parent.Id, + IncludeItemTypes = new[] { typeof(Audio.Audio).Name }, + EnableTotalRecordCount = false + + }).Items.Cast<IHasAlbumArtist>(); var artists = _libraryManager.GetAlbumArtists(items); @@ -333,9 +351,14 @@ namespace MediaBrowser.Controller.Entities private QueryResult<BaseItem> GetMusicArtists(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }) - .Where(i => !i.IsFolder) - .OfType<IHasArtist>(); + var items = parent.QueryRecursive(new InternalItemsQuery(user) + { + Recursive = true, + ParentId = parent.Id, + IncludeItemTypes = new[] { typeof(Audio.Audio).Name, typeof(MusicVideo).Name }, + EnableTotalRecordCount = false + + }).Items.Cast<IHasArtist>(); var artists = _libraryManager.GetArtists(items); @@ -344,35 +367,50 @@ namespace MediaBrowser.Controller.Entities private QueryResult<BaseItem> GetFavoriteArtists(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }) - .Where(i => !i.IsFolder) - .OfType<IHasAlbumArtist>(); + var items = parent.QueryRecursive(new InternalItemsQuery(user) + { + Recursive = true, + ParentId = parent.Id, + IncludeItemTypes = new[] { typeof(Audio.Audio).Name }, + EnableTotalRecordCount = false - var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite); + }).Items.Cast<IHasAlbumArtist>(); + + var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user, i).IsFavorite); return GetResult(artists, parent, query); } - private Task<QueryResult<BaseItem>> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query) + private QueryResult<BaseItem> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query) { - query.IncludeItemTypes = new[] { "Playlist" }; + query.ParentId = null; + query.IncludeItemTypes = new[] { typeof(Playlist).Name }; + query.SetUser(user); query.Recursive = true; - return parent.GetItems(query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is MusicAlbum && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); - return PostFilterAndSort(items, parent, null, query); + query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name }; + + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMusicSongs(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is Audio.Audio && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); - return PostFilterAndSort(items, parent, null, query); + query.IncludeItemTypes = new[] { typeof(Audio.Audio).Name }; + + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query) @@ -389,27 +427,29 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new string[] { }; - //var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is MusicVideo || i is Audio.Audio && FilterItem(i, query)); - return PostFilterAndSort(items, parent, null, query); } private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query) { + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); query.IsFavorite = true; + query.IncludeItemTypes = new[] { typeof(Audio.Audio).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => i is Audio.Audio && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query) { + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); query.IsFavorite = true; + query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => i is MusicAlbum && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return _libraryManager.GetItemsResult(query); } private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query) @@ -428,19 +468,15 @@ namespace MediaBrowser.Controller.Entities { if (query.Recursive) { - var recursiveItems = GetRecursiveChildren(parent, user, - new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }) - .Where(i => i is Movie || i is BoxSet); + query.Recursive = true; + query.SetUser(user); - //var collections = _collectionManager.CollapseItemsWithinBoxSets(recursiveItems, user).ToList(); - - //if (collections.Count > 0) - //{ - // recursiveItems.AddRange(_collectionManager.CollapseItemsWithinBoxSets(recursiveItems, user)); - // recursiveItems = recursiveItems.DistinctBy(i => i.Id).ToList(); - //} + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Movie).Name, typeof(BoxSet).Name }; + } - return GetResult(recursiveItems, parent, query); + return parent.QueryRecursive(query); } var list = new List<BaseItem>(); @@ -457,43 +493,51 @@ namespace MediaBrowser.Controller.Entities private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query) { + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); query.IsFavorite = true; + query.IncludeItemTypes = new[] { typeof(Movie).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query) { + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); query.IsFavorite = true; + query.IncludeItemTypes = new[] { typeof(Series).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Series && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query) { + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); query.IsFavorite = true; + query.IncludeItemTypes = new[] { typeof(Episode).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); - return PostFilterAndSort(items, parent, null, query); + query.IncludeItemTypes = new[] { typeof(Movie).Name }; + + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetMovieCollections(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is BoxSet && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); + return GetBoxsetView(parent, user, query); } private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query) @@ -501,9 +545,13 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); + query.Limit = GetSpecialItemsLimit(); + query.IncludeItemTypes = new[] { typeof(Movie).Name }; - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); + return ConvertToResult(_libraryManager.GetItemList(query)); } private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query) @@ -511,16 +559,34 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; query.IsResumable = true; + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); + query.Limit = GetSpecialItemsLimit(); + query.IncludeItemTypes = new[] { typeof(Movie).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query)); + return ConvertToResult(_libraryManager.GetItemList(query)); + } - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); + private QueryResult<BaseItem> ConvertToResult(IEnumerable<BaseItem> items) + { + var arr = items.ToArray(); + return new QueryResult<BaseItem> + { + Items = arr, + TotalRecordCount = arr.Length + }; } private async Task<QueryResult<BaseItem>> GetMovieGenres(Folder parent, User user, InternalItemsQuery query) { - var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }) - .Where(i => i is Movie) + var tasks = parent.QueryRecursive(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Movie).Name }, + Recursive = true, + EnableTotalRecordCount = false + + }).Items .SelectMany(i => i.Genres) .DistinctNames() .Select(i => @@ -547,11 +613,14 @@ namespace MediaBrowser.Controller.Entities private async Task<QueryResult<BaseItem>> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }) - .Where(i => i is Movie) - .Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)); + query.Recursive = true; + query.ParentId = queryParent.Id; + query.Genres = new[] { displayParent.Name }; + query.SetUser(user); + + query.IncludeItemTypes = new[] { typeof(Movie).Name }; - return GetResult(items, queryParent, query); + return _libraryManager.GetItemsResult(query); } private async Task<QueryResult<BaseItem>> GetPlaylistsView(Folder parent, User user, InternalItemsQuery query) @@ -559,20 +628,29 @@ namespace MediaBrowser.Controller.Entities return GetResult(_playlistManager.GetPlaylists(user.Id.ToString("N")), parent, query); } - private async Task<QueryResult<BaseItem>> GetBoxsetView(Folder parent, User user, InternalItemsQuery query) + private QueryResult<BaseItem> GetBoxsetView(Folder parent, User user, InternalItemsQuery query) { - var collections = _collectionManager.GetCollections(user); + query.ParentId = null; + query.IncludeItemTypes = new[] { typeof(BoxSet).Name }; + query.SetUser(user); + query.Recursive = true; - return GetResult(collections, parent, query); + return _libraryManager.GetItemsResult(query); } private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query) { if (query.Recursive) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => (i is Series || i is Season || i is Episode) && FilterItem(i, query)); + query.Recursive = true; + query.SetUser(user); + + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Series).Name, typeof(Season).Name, typeof(Episode).Name }; + } - return PostFilterAndSort(items, parent, null, query); + return parent.QueryRecursive(query); } var list = new List<BaseItem>(); @@ -593,9 +671,14 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); + query.Limit = GetSpecialItemsLimit(); + query.IncludeItemTypes = new[] { typeof(Episode).Name }; + query.ExcludeLocationTypes = new[] { LocationType.Virtual }; - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); + return ConvertToResult(_libraryManager.GetItemList(query)); } private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query) @@ -618,23 +701,35 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; query.IsResumable = true; + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); + query.Limit = GetSpecialItemsLimit(); + query.IncludeItemTypes = new[] { typeof(Episode).Name }; - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); + return ConvertToResult(_libraryManager.GetItemList(query)); } private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Series && FilterItem(i, query)); + query.Recursive = true; + query.ParentId = parent.Id; + query.SetUser(user); - return PostFilterAndSort(items, parent, null, query); + query.IncludeItemTypes = new[] { typeof(Series).Name }; + + return _libraryManager.GetItemsResult(query); } private async Task<QueryResult<BaseItem>> GetTvGenres(Folder parent, User user, InternalItemsQuery query) { - var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }) - .OfType<Series>() + var tasks = parent.QueryRecursive(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Series).Name }, + Recursive = true, + EnableTotalRecordCount = false + + }).Items .SelectMany(i => i.Genres) .DistinctNames() .Select(i => @@ -659,13 +754,16 @@ namespace MediaBrowser.Controller.Entities return GetResult(genres, parent, query); } - private async Task<QueryResult<BaseItem>> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) + private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) { - var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.TvShows, string.Empty }) - .Where(i => i is Series) - .Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)); + query.Recursive = true; + query.ParentId = queryParent.Id; + query.Genres = new[] { displayParent.Name }; + query.SetUser(user); - return GetResult(items, queryParent, query); + query.IncludeItemTypes = new[] { typeof(Series).Name }; + + return _libraryManager.GetItemsResult(query); } private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result) @@ -685,12 +783,12 @@ namespace MediaBrowser.Controller.Entities { items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager)); - return PostFilterAndSort(items, queryParent, null, query, _libraryManager); + return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config); } - public bool FilterItem(BaseItem item, InternalItemsQuery query) + public static bool FilterItem(BaseItem item, InternalItemsQuery query) { - return Filter(item, query.User, query, _userDataManager, _libraryManager); + return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager); } private QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, @@ -698,14 +796,15 @@ namespace MediaBrowser.Controller.Entities int? totalRecordLimit, InternalItemsQuery query) { - return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager); + return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _config); } public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, BaseItem queryParent, int? totalRecordLimit, InternalItemsQuery query, - ILibraryManager libraryManager) + ILibraryManager libraryManager, + IServerConfigurationManager configurationManager) { var user = query.User; @@ -714,7 +813,7 @@ namespace MediaBrowser.Controller.Entities query.IsVirtualUnaired, query.IsUnaired); - items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user); + items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager); // This must be the last filter if (!string.IsNullOrEmpty(query.AdjacentTo)) @@ -728,14 +827,15 @@ namespace MediaBrowser.Controller.Entities public static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items, InternalItemsQuery query, BaseItem queryParent, - User user) + User user, + IServerConfigurationManager configurationManager) { if (items == null) { throw new ArgumentNullException("items"); } - if (CollapseBoxSetItems(query, queryParent, user)) + if (CollapseBoxSetItems(query, queryParent, user, configurationManager)) { items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user); } @@ -768,7 +868,8 @@ namespace MediaBrowser.Controller.Entities public static bool CollapseBoxSetItems(InternalItemsQuery query, BaseItem queryParent, - User user) + User user, + IServerConfigurationManager configurationManager) { // Could end up stuck in a loop like this if (queryParent is BoxSet) @@ -780,12 +881,12 @@ namespace MediaBrowser.Controller.Entities if (!param.HasValue) { - if (user != null && !user.Configuration.GroupMoviesIntoBoxSets) + if (user != null && !configurationManager.Configuration.EnableGroupingIntoCollections) { return false; } - if (query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase)) + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase)) { param = true; } @@ -911,11 +1012,6 @@ namespace MediaBrowser.Controller.Entities return false; } - if (request.IsYearMismatched.HasValue) - { - return false; - } - if (!string.IsNullOrWhiteSpace(request.Person)) { return false; @@ -1053,22 +1149,6 @@ namespace MediaBrowser.Controller.Entities bool? isVirtualUnaired, bool? isUnaired) { - if (isMissing.HasValue && isVirtualUnaired.HasValue) - { - if (!isMissing.Value && !isVirtualUnaired.Value) - { - return items.Where(i => - { - var e = i as Season; - if (e != null) - { - return !e.IsMissingOrVirtualUnaired; - } - return true; - }); - } - } - if (isMissing.HasValue) { var val = isMissing.Value; @@ -1077,7 +1157,7 @@ namespace MediaBrowser.Controller.Entities var e = i as Season; if (e != null) { - return e.IsMissingSeason == val; + return (e.IsMissingSeason) == val; } return true; }); @@ -1121,7 +1201,7 @@ namespace MediaBrowser.Controller.Entities { var user = query.User; - items = libraryManager.ReplaceVideosWithPrimaryVersions(items); + items = items.DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase); if (query.SortBy.Length > 0) { @@ -1209,16 +1289,11 @@ namespace MediaBrowser.Controller.Entities return false; } - if (query.Filter != null && !query.Filter(item)) - { - return false; - } - UserItemData userData = null; 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 +1303,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 +1314,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 +1324,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) @@ -1355,16 +1430,6 @@ namespace MediaBrowser.Controller.Entities } } - if (query.IsYearMismatched.HasValue) - { - var filterValue = query.IsYearMismatched.Value; - - if (IsYearMismatched(item, libraryManager) != filterValue) - { - return false; - } - } - if (query.HasOfficialRating.HasValue) { var filterValue = query.HasOfficialRating.Value; @@ -1598,12 +1663,7 @@ namespace MediaBrowser.Controller.Entities var tags = query.Tags; if (tags.Length > 0) { - var hasTags = item as IHasTags; - if (hasTags == null) - { - return false; - } - if (!tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) + if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) { return false; } @@ -1735,7 +1795,7 @@ namespace MediaBrowser.Controller.Entities if (query.SeriesStatuses.Length > 0) { - var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value)); + var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value)); if (!ok) { return false; @@ -1884,26 +1944,6 @@ namespace MediaBrowser.Controller.Entities return parent.GetRecursiveChildren(user); } - private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes, Func<BaseItem, bool> filter) - { - if (parent == null || parent is UserView) - { - if (user == null) - { - return GetMediaFolders(null, viewTypes).SelectMany(i => i.GetRecursiveChildren(filter)); - } - - return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user, filter)); - } - - if (user == null) - { - return parent.GetRecursiveChildren(filter); - } - - return parent.GetRecursiveChildren(user, filter); - } - private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query) { if (query.Recursive) @@ -1936,34 +1976,6 @@ namespace MediaBrowser.Controller.Entities return _userViewManager.GetUserSubView(parent.Id.ToString("N"), type, sortName, CancellationToken.None); } - public static bool IsYearMismatched(BaseItem item, ILibraryManager libraryManager) - { - if (item.ProductionYear.HasValue) - { - var path = item.Path; - - if (!string.IsNullOrEmpty(path)) - { - var info = libraryManager.ParseName(Path.GetFileName(path)); - var yearInName = info.Year; - - // Go up a level if we didn't get a year - if (!yearInName.HasValue) - { - info = libraryManager.ParseName(Path.GetFileName(Path.GetDirectoryName(path))); - yearInName = info.Year; - } - - if (yearInName.HasValue) - { - return yearInName.Value != item.ProductionYear.Value; - } - } - } - - return false; - } - public static IEnumerable<BaseItem> FilterForAdjacency(IEnumerable<BaseItem> items, string adjacentToId) { var list = items.ToList(); diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 511606efc..eba1e466a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -21,14 +21,14 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Video : BaseItem, IHasAspectRatio, - IHasTags, ISupportsPlaceHolders, IHasMediaSources, IHasShortOverview, 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,10 +44,27 @@ namespace MediaBrowser.Controller.Entities } } - public long? Size { get; set; } - public string Container { get; set; } + [IgnoreDataMember] + public override string PresentationUniqueKey + { + get + { + if (!string.IsNullOrWhiteSpace(PrimaryVersionId)) + { + return PrimaryVersionId; + } + + return base.PresentationUniqueKey; + } + } + + [IgnoreDataMember] + public override bool EnableForceSaveOnDateModifiedChange + { + get { return true; } + } + public int? TotalBitrate { get; set; } - public string ShortOverview { get; set; } public ExtraType? ExtraType { get; set; } /// <summary> @@ -56,6 +73,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>(); @@ -80,23 +163,6 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] - public override LocationType LocationType - { - get - { - if (SourceType == SourceType.Channel) - { - if (string.IsNullOrEmpty(Path)) - { - return LocationType.Remote; - } - } - - return base.LocationType; - } - } - - [IgnoreDataMember] public override bool SupportsAddingToPlaylist { get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } @@ -107,6 +173,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; } } @@ -148,42 +222,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; + } + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); - if (!string.IsNullOrWhiteSpace(key)) + 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)) { - key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); + list.Insert(0, GetUserDataKey(key)); + } + } + else + { + var key = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(key)) + { + 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() @@ -210,72 +307,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 { @@ -334,6 +365,11 @@ namespace MediaBrowser.Controller.Entities { return false; } + + if (newAsVideo.VideoType != VideoType) + { + return false; + } } return base.IsValidFromResolver(newItem); @@ -480,6 +516,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) @@ -498,13 +564,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/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index e4eecec18..f4c0e7658 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.System; using System; using System.Collections.Generic; using System.Net; +using System.Threading.Tasks; namespace MediaBrowser.Controller { @@ -12,12 +13,12 @@ namespace MediaBrowser.Controller public interface IServerApplicationHost : IApplicationHost { event EventHandler HasUpdateAvailableChanged; - + /// <summary> /// Gets the system info. /// </summary> /// <returns>SystemInfo.</returns> - SystemInfo GetSystemInfo(); + Task<SystemInfo> GetSystemInfo(); /// <summary> /// Gets a value indicating whether [supports automatic run at startup]. @@ -65,13 +66,13 @@ namespace MediaBrowser.Controller /// Gets the local ip address. /// </summary> /// <value>The local ip address.</value> - List<IPAddress> LocalIpAddresses { get; } + Task<List<IPAddress>> GetLocalIpAddresses(); /// <summary> /// Gets the local API URL. /// </summary> /// <value>The local API URL.</value> - string LocalApiUrl { get; } + Task<string> GetLocalApiUrl(); /// <summary> /// Gets the local API URL. @@ -86,5 +87,7 @@ namespace MediaBrowser.Controller /// <param name="ipAddress">The ip address.</param> /// <returns>System.String.</returns> string GetLocalApiUrl(IPAddress ipAddress); + + void LaunchUrl(string url); } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index c8b3d5131..ad38b9ea5 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Library { @@ -25,7 +26,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 +37,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 +60,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> @@ -151,13 +152,6 @@ namespace MediaBrowser.Controller.Library BaseItem GetItemById(Guid id); /// <summary> - /// Gets the memory item by identifier. - /// </summary> - /// <param name="id">The identifier.</param> - /// <returns>BaseItem.</returns> - BaseItem GetMemoryItemById(Guid id); - - /// <summary> /// Gets the intros. /// </summary> /// <param name="item">The item.</param> @@ -243,6 +237,8 @@ namespace MediaBrowser.Controller.Library /// <returns>BaseItem.</returns> BaseItem RetrieveItem(Guid id); + bool IsScanRunning { get; } + /// <summary> /// Occurs when [item added]. /// </summary> @@ -290,7 +286,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> @@ -313,13 +309,6 @@ namespace MediaBrowser.Controller.Library Task DeleteItem(BaseItem item, DeleteOptions options); /// <summary> - /// Replaces the videos with primary versions. - /// </summary> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items); - - /// <summary> /// Gets the named view. /// </summary> /// <param name="user">The user.</param> @@ -332,8 +321,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 +335,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 +382,7 @@ namespace MediaBrowser.Controller.Library string viewType, string sortName, CancellationToken cancellationToken); - + /// <summary> /// Determines whether [is video file] [the specified path]. /// </summary> @@ -477,14 +466,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 +548,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 +556,17 @@ 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); + + QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 56ac14e9d..86c52c4c3 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. @@ -51,7 +40,9 @@ namespace MediaBrowser.Controller.Library /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>UserItemDataDto.</returns> - UserItemDataDto GetUserDataDto(IHasUserData item, User user); + Task<UserItemDataDto> GetUserDataDto(IHasUserData item, User user); + + Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user); /// <summary> /// Get all user data for the given user 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/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 7d8df96ed..372b095fd 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -59,5 +59,9 @@ namespace MediaBrowser.Controller.LiveTv /// </summary> /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value> public bool? IsFavorite { get; set; } + + public bool? IsHD { get; set; } + public string AudioCodec { get; set; } + public string VideoCodec { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs index f5048bdda..5ecd70cc5 100644 --- a/MediaBrowser.Controller/LiveTv/IListingsProvider.cs +++ b/MediaBrowser.Controller/LiveTv/IListingsProvider.cs @@ -15,5 +15,6 @@ namespace MediaBrowser.Controller.LiveTv Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken); Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings); Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location); + Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 56b7a307a..fe69b38cb 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -8,6 +8,7 @@ using MediaBrowser.Model.Querying; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Events; namespace MediaBrowser.Controller.LiveTv { @@ -344,6 +345,13 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="validateListings">if set to <c>true</c> [validate listings].</param> /// <returns>Task.</returns> Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings); + + void DeleteListingsProvider(string id); + + Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber); + + TunerChannelMapping GetTunerChannelMapping(ChannelInfo channel, List<NameValuePair> mappings, List<ChannelInfo> providerChannels); + /// <summary> /// Gets the lineups. /// </summary> @@ -383,5 +391,17 @@ namespace MediaBrowser.Controller.LiveTv /// </summary> /// <returns>List<NameValuePair>.</returns> List<NameValuePair> GetSatIniMappings(); + + Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken); + + Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken); + Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); + + List<IListingsProvider> ListingProviders { get;} + + event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled; + event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled; + event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated; + event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated; } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index 4ef4847a3..d7d8336d0 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -226,4 +226,23 @@ namespace MediaBrowser.Controller.LiveTv /// <returns>Task.</returns> Task ResetTuner(string id, CancellationToken cancellationToken); } + + public interface ISupportsNewTimerIds + { + /// <summary> + /// Creates the timer asynchronous. + /// </summary> + /// <param name="info">The information.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task<string> CreateTimer(TimerInfo info, CancellationToken cancellationToken); + + /// <summary> + /// Creates the series timer asynchronous. + /// </summary> + /// <param name="info">The information.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken); + } } 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..74c993248 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -4,36 +4,41 @@ 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; +using MediaBrowser.Model.Providers; 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] @@ -231,5 +236,25 @@ namespace MediaBrowser.Controller.LiveTv return false; } } + + public override List<ExternalUrl> GetRelatedUrls() + { + var list = base.GetRelatedUrls(); + + var imdbId = this.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(imdbId)) + { + if (IsMovie) + { + list.Add(new ExternalUrl + { + Name = "Trakt", + Url = string.Format("https://trakt.tv/movies/{0}", imdbId) + }); + } + } + + return list; + } } } 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/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs new file mode 100644 index 000000000..0e1a05475 --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.LiveTv +{ + public class TimerEventInfo + { + public string Id { get; set; } + public string ProgramId { get; set; } + } +} diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs new file mode 100644 index 000000000..da0527eea --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.LiveTv +{ + public class TunerChannelMapping + { + public string Name { get; set; } + public string Number { get; set; } + public string ProviderChannelNumber { get; set; } + public string ProviderChannelName { get; set; } + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6f429ed2f..0462117cb 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -75,20 +75,15 @@ </Compile> <Compile Include="Activity\IActivityManager.cs" /> <Compile Include="Activity\IActivityRepository.cs" /> - <Compile Include="Channels\ChannelAudioItem.cs" /> - <Compile Include="Channels\ChannelFolderItem.cs" /> <Compile Include="Channels\ChannelItemInfo.cs" /> <Compile Include="Channels\ChannelItemResult.cs" /> <Compile Include="Channels\ChannelItemType.cs" /> <Compile Include="Channels\ChannelMediaInfo.cs" /> <Compile Include="Channels\ChannelParentalRating.cs" /> <Compile Include="Channels\ChannelSearchInfo.cs" /> - <Compile Include="Channels\ChannelVideoItem.cs" /> <Compile Include="Channels\IChannel.cs" /> - <Compile Include="Channels\IChannelItem.cs" /> <Compile Include="Channels\IChannelManager.cs" /> <Compile Include="Channels\Channel.cs" /> - <Compile Include="Channels\IChannelMediaItem.cs" /> <Compile Include="Channels\IHasCacheKey.cs" /> <Compile Include="Channels\IIndexableChannel.cs" /> <Compile Include="Channels\InternalAllChannelMediaQuery.cs" /> @@ -147,7 +142,7 @@ <Compile Include="Entities\IHasDisplayOrder.cs" /> <Compile Include="Entities\IHasId.cs" /> <Compile Include="Entities\IHasImages.cs" /> - <Compile Include="Entities\IHasKeywords.cs" /> + <Compile Include="Entities\KeywordExtensions.cs" /> <Compile Include="Entities\IHasMediaSources.cs" /> <Compile Include="Entities\IHasMetascore.cs" /> <Compile Include="Entities\IHasOriginalTitle.cs" /> @@ -159,7 +154,6 @@ <Compile Include="Entities\IHasSpecialFeatures.cs" /> <Compile Include="Entities\IHasStartDate.cs" /> <Compile Include="Entities\IHasTaglines.cs" /> - <Compile Include="Entities\IHasTags.cs" /> <Compile Include="Entities\IHasThemeMedia.cs" /> <Compile Include="Entities\IHasTrailers.cs" /> <Compile Include="Entities\IHasUserData.cs" /> @@ -182,9 +176,11 @@ <Compile Include="Entities\PhotoAlbum.cs" /> <Compile Include="Entities\Share.cs" /> <Compile Include="Entities\SourceType.cs" /> + <Compile Include="Entities\TagExtensions.cs" /> <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" /> @@ -222,7 +218,9 @@ <Compile Include="LiveTv\ProgramInfo.cs" /> <Compile Include="LiveTv\RecordingInfo.cs" /> <Compile Include="LiveTv\SeriesTimerInfo.cs" /> + <Compile Include="LiveTv\TimerEventInfo.cs" /> <Compile Include="LiveTv\TimerInfo.cs" /> + <Compile Include="LiveTv\TunerChannelMapping.cs" /> <Compile Include="Localization\ILocalizationManager.cs" /> <Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" /> <Compile Include="MediaEncoding\EncodingJobOptions.cs" /> @@ -294,21 +292,17 @@ <Compile Include="Providers\IImageFileSaver.cs" /> <Compile Include="Providers\IImageProvider.cs" /> <Compile Include="Providers\IImageSaver.cs" /> - <Compile Include="Providers\IItemIdentityConverter.cs" /> - <Compile Include="Providers\IItemIdentityProvider.cs" /> <Compile Include="Providers\ILocalImageFileProvider.cs" /> <Compile Include="Providers\ILocalMetadataProvider.cs" /> <Compile Include="Providers\ImageRefreshMode.cs" /> <Compile Include="Providers\ImageRefreshOptions.cs" /> <Compile Include="Providers\IPreRefreshProvider.cs" /> - <Compile Include="Providers\IProviderRepository.cs" /> <Compile Include="Providers\IRemoteImageProvider.cs" /> <Compile Include="Providers\ILocalImageProvider.cs" /> <Compile Include="Providers\IMetadataProvider.cs" /> <Compile Include="Providers\IMetadataService.cs" /> <Compile Include="Providers\IRemoteMetadataProvider.cs" /> <Compile Include="Providers\IRemoteSearchProvider.cs" /> - <Compile Include="Providers\ISeriesOrderProvider.cs" /> <Compile Include="Providers\ItemInfo.cs" /> <Compile Include="Providers\LiveTvProgramLookupInfo.cs" /> <Compile Include="Providers\LocalImageInfo.cs" /> @@ -334,12 +328,8 @@ <Compile Include="Sorting\SortHelper.cs" /> <Compile Include="Subtitles\ISubtitleManager.cs" /> <Compile Include="Subtitles\ISubtitleProvider.cs" /> - <Compile Include="Providers\ItemIdentifier.cs" /> - <Compile Include="Providers\ItemIdentities.cs" /> <Compile Include="Providers\ItemLookupInfo.cs" /> <Compile Include="Providers\MetadataRefreshOptions.cs" /> - <Compile Include="Providers\MetadataStatus.cs" /> - <Compile Include="Providers\ISeriesOrderManager.cs" /> <Compile Include="Session\ISessionManager.cs" /> <Compile Include="Entities\AggregateFolder.cs" /> <Compile Include="Entities\Audio\Audio.cs" /> diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index ddaf7ff6d..44b741755 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dlna; +using System.Linq; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Controller.MediaEncoding { @@ -58,8 +59,6 @@ namespace MediaBrowser.Controller.MediaEncoding } } - public bool? Cabac { get; set; } - public EncodingJobOptions() { @@ -76,7 +75,7 @@ namespace MediaBrowser.Controller.MediaEncoding Level = info.VideoLevel; ItemId = info.ItemId; MediaSourceId = info.MediaSourceId; - AudioCodec = info.AudioCodec; + AudioCodec = info.TargetAudioCodec; MaxAudioChannels = info.MaxAudioChannels; AudioBitRate = info.AudioBitrate; AudioSampleRate = info.TargetAudioSampleRate; @@ -87,7 +86,6 @@ namespace MediaBrowser.Controller.MediaEncoding MaxRefFrames = info.MaxRefFrames; MaxVideoBitDepth = info.MaxVideoBitDepth; SubtitleMethod = info.SubtitleDeliveryMethod; - Cabac = info.Cabac; Context = info.Context; if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External) diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 76ef054de..c8a8caa91 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -4,14 +4,17 @@ 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 { + string EncoderLocationType { get; } + /// <summary> /// Gets the encoder path. /// </summary> @@ -19,12 +22,6 @@ namespace MediaBrowser.Controller.MediaEncoding string EncoderPath { get; } /// <summary> - /// Gets the version. - /// </summary> - /// <value>The version.</value> - string Version { get; } - - /// <summary> /// Supportses the decoder. /// </summary> /// <param name="decoder">The decoder.</param> @@ -38,7 +35,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="imageStreamIndex">Index of the image stream.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{Stream}.</returns> - Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken); + Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken); /// <summary> /// Extracts the video image. @@ -49,7 +46,9 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="offset">The offset.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{Stream}.</returns> - Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); + Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); + + Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken); /// <summary> /// Extracts the video images on interval. @@ -63,12 +62,12 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="maxWidth">The maximum width.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task ExtractVideoImagesOnInterval(string[] inputFiles, - MediaProtocol protocol, - Video3DFormat? threedFormat, - TimeSpan interval, - string targetDirectory, - string filenamePrefix, + Task ExtractVideoImagesOnInterval(string[] inputFiles, + MediaProtocol protocol, + Video3DFormat? threedFormat, + TimeSpan interval, + string targetDirectory, + string filenamePrefix, int? maxWidth, CancellationToken cancellationToken); @@ -131,5 +130,9 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="path">The path.</param> /// <returns>System.String.</returns> string EscapeSubtitleFilterPath(string path); + + Task Init(); + + Task UpdateEncoderPath(string path, string pathType); } } diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 826711e51..44489cbf5 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -10,13 +10,6 @@ namespace MediaBrowser.Controller.MediaEncoding /// <summary> /// Gets the subtitles. /// </summary> - /// <param name="itemId">The item identifier.</param> - /// <param name="mediaSourceId">The media source identifier.</param> - /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> - /// <param name="outputFormat">The output format.</param> - /// <param name="startTimeTicks">The start time ticks.</param> - /// <param name="endTimeTicks">The end time ticks.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{Stream}.</returns> Task<Stream> GetSubtitles(string itemId, string mediaSourceId, @@ -24,6 +17,7 @@ namespace MediaBrowser.Controller.MediaEncoding string outputFormat, long startTimeTicks, long? endTimeTicks, + bool preserveOriginalTimestamps, CancellationToken cancellationToken); /// <summary> @@ -33,6 +27,6 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="protocol">The protocol.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>System.String.</returns> - Task<string> GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken); + Task<string> GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index 526bf4be2..49d4614d8 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -28,6 +28,8 @@ namespace MediaBrowser.Controller.Net /// <returns>System.Object.</returns> object GetResult(object content, string contentType, IDictionary<string,string> responseHeaders = null); + object GetAsyncStreamWriter(Func<Stream,Task> streamWriter, IDictionary<string, string> responseHeaders = null); + /// <summary> /// Gets the optimized result. /// </summary> @@ -80,7 +82,7 @@ namespace MediaBrowser.Controller.Net /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>System.Object.</returns> - object GetStaticResult(IRequest requestContext, + Task<object> GetStaticResult(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, @@ -94,7 +96,7 @@ namespace MediaBrowser.Controller.Net /// <param name="requestContext">The request context.</param> /// <param name="options">The options.</param> /// <returns>System.Object.</returns> - object GetStaticResult(IRequest requestContext, StaticResultOptions options); + Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options); /// <summary> /// Gets the static file result. @@ -103,7 +105,7 @@ namespace MediaBrowser.Controller.Net /// <param name="path">The path.</param> /// <param name="fileShare">The file share.</param> /// <returns>System.Object.</returns> - object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read); + Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read); /// <summary> /// Gets the static file result. @@ -111,7 +113,7 @@ namespace MediaBrowser.Controller.Net /// <param name="requestContext">The request context.</param> /// <param name="options">The options.</param> /// <returns>System.Object.</returns> - object GetStaticFileResult(IRequest requestContext, + Task<object> GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options); } } 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..78138999c 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Persistence @@ -14,12 +15,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> @@ -87,7 +82,7 @@ namespace MediaBrowser.Controller.Persistence /// <param name="chapters">The chapters.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken); + Task SaveChapters(Guid id, List<ChapterInfo> chapters, CancellationToken cancellationToken); /// <summary> /// Gets the media streams. @@ -103,7 +98,7 @@ namespace MediaBrowser.Controller.Persistence /// <param name="streams">The streams.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken); + Task SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken); /// <summary> /// Gets the item ids. @@ -159,7 +154,7 @@ namespace MediaBrowser.Controller.Persistence /// </summary> /// <param name="query">The query.</param> /// <returns>List<BaseItem>.</returns> - IEnumerable<BaseItem> GetItemList(InternalItemsQuery query); + List<BaseItem> GetItemList(InternalItemsQuery query); /// <summary> /// Updates the inherited values. @@ -167,6 +162,13 @@ namespace MediaBrowser.Controller.Persistence /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task UpdateInheritedValues(CancellationToken cancellationToken); + + QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query); + QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query); } } diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 2a904be0d..ca4dc9751 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> @@ -35,6 +29,8 @@ namespace MediaBrowser.Controller.Persistence /// <returns>Task{UserItemData}.</returns> UserItemData GetUserData(Guid userId, string key); + UserItemData GetUserData(Guid userId, List<string> keys); + /// <summary> /// Return all user data associated with the given user /// </summary> diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 0f9af6550..5ffe3d5da 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,16 +60,16 @@ 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) + public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query) { - var items = GetPlayableItems(user); + var items = GetPlayableItems(user).Result; - if (filter != null) + if (query != null) { - items = items.Where(filter); + items = items.Where(i => UserViewBuilder.FilterItem(i, query)); } return items; @@ -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); } @@ -111,7 +129,11 @@ namespace MediaBrowser.Controller.Playlists var items = user == null ? LibraryManager.RootFolder.GetRecursiveChildren(filter) - : user.RootFolder.GetRecursiveChildren(user, filter); + : user.RootFolder.GetRecursiveChildren(user, new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Audio).Name }, + ArtistNames = new[] { musicArtist.Name } + }); return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); } @@ -119,15 +141,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/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index f9060d184..a783910e3 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -61,16 +61,7 @@ namespace MediaBrowser.Controller.Providers }; //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken); - - try - { - Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); - } - catch - { - Logger.Error("Error parsing xml file {0}", metadataFile); - throw; - } + Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken); } /// <summary> @@ -812,11 +803,7 @@ namespace MediaBrowser.Controller.Providers { using (var subtree = reader.ReadSubtree()) { - var hasTags = item as IHasTags; - if (hasTags != null) - { - FetchFromTagsNode(subtree, hasTags); - } + FetchFromTagsNode(subtree, item); } break; } @@ -825,11 +812,7 @@ namespace MediaBrowser.Controller.Providers { using (var subtree = reader.ReadSubtree()) { - var hasTags = item as IHasKeywords; - if (hasTags != null) - { - FetchFromKeywordsNode(subtree, hasTags); - } + FetchFromKeywordsNode(subtree, item); } break; } @@ -1079,7 +1062,7 @@ namespace MediaBrowser.Controller.Providers } } - private void FetchFromTagsNode(XmlReader reader, IHasTags item) + private void FetchFromTagsNode(XmlReader reader, BaseItem item) { reader.MoveToContent(); @@ -1108,7 +1091,7 @@ namespace MediaBrowser.Controller.Providers } } - private void FetchFromKeywordsNode(XmlReader reader, IHasKeywords item) + private void FetchFromKeywordsNode(XmlReader reader, BaseItem item) { reader.MoveToContent(); diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index c057c9707..e7e3323c2 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -102,6 +102,12 @@ namespace MediaBrowser.Controller.Providers { var directory = Path.GetDirectoryName(path); + if (string.IsNullOrWhiteSpace(directory)) + { + _logger.Debug("Parent path is null for {0}", path); + return null; + } + var dict = GetFileSystemDictionary(directory, false); FileSystemMetadata entry; diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index 28abd636a..b879040f8 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -10,6 +10,9 @@ namespace MediaBrowser.Controller.Providers public int? IndexNumberEnd { get; set; } public int? AnimeSeriesIndex { get; set; } + public bool IsMissingEpisode { get; set; } + public bool IsVirtualUnaired { get; set; } + public EpisodeInfo() { SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs index 4c7069dd6..9441c3ecd 100644 --- a/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs +++ b/MediaBrowser.Controller/Providers/IHasItemChangeMonitor.cs @@ -8,9 +8,8 @@ namespace MediaBrowser.Controller.Providers /// Determines whether the specified item has changed. /// </summary> /// <param name="item">The item.</param> - /// <param name="status">The status.</param> /// <param name="directoryService">The directory service.</param> /// <returns><c>true</c> if the specified item has changed; otherwise, <c>false</c>.</returns> - bool HasChanged(IHasMetadata item, MetadataStatus status, IDirectoryService directoryService); + bool HasChanged(IHasMetadata item, IDirectoryService directoryService); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs b/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs deleted file mode 100644 index bfdd1dbf3..000000000 --- a/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public interface IItemIdentityConverter { } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs b/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs deleted file mode 100644 index 6b403bb55..000000000 --- a/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public interface IItemIdentityProvider { } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 57e4ff320..3eefa9647 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -97,13 +97,11 @@ namespace MediaBrowser.Controller.Providers /// </summary> /// <param name="imageProviders">The image providers.</param> /// <param name="metadataServices">The metadata services.</param> - /// <param name="identityProviders">The identity providers.</param> - /// <param name="identityConverters">The identity converters.</param> /// <param name="metadataProviders">The metadata providers.</param> /// <param name="savers">The savers.</param> /// <param name="imageSavers">The image savers.</param> /// <param name="externalIds">The external ids.</param> - void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IItemIdentityProvider> identityProviders, IEnumerable<IItemIdentityConverter> identityConverters, IEnumerable<IMetadataProvider> metadataProviders, + void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> savers, IEnumerable<IImageSaver> imageSavers, IEnumerable<IExternalId> externalIds); @@ -135,7 +133,7 @@ namespace MediaBrowser.Controller.Providers /// </summary> /// <param name="item">The item.</param> /// <returns>IEnumerable{ExternalUrl}.</returns> - IEnumerable<ExternalUrl> GetExternalUrls(IHasProviderIds item); + IEnumerable<ExternalUrl> GetExternalUrls(BaseItem item); /// <summary> /// Gets the external identifier infos. @@ -190,21 +188,5 @@ namespace MediaBrowser.Controller.Providers /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{HttpResponseInfo}.</returns> Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken); - - /// <summary> - /// Gets the item identity providers. - /// </summary> - /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam> - /// <returns>IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>>.</returns> - IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>() - where TLookupInfo : ItemLookupInfo; - - /// <summary> - /// Gets the item identity converters. - /// </summary> - /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam> - /// <returns>IEnumerable<IItemIdentityConverter<TIdentity>>.</returns> - IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>() - where TLookupInfo : ItemLookupInfo; } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IProviderRepository.cs b/MediaBrowser.Controller/Providers/IProviderRepository.cs deleted file mode 100644 index 1f77d0ca1..000000000 --- a/MediaBrowser.Controller/Providers/IProviderRepository.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MediaBrowser.Controller.Persistence; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface IProviderRepository : IRepository - { - /// <summary> - /// Gets the metadata status. - /// </summary> - /// <param name="itemId">The item identifier.</param> - /// <returns>MetadataStatus.</returns> - MetadataStatus GetMetadataStatus(Guid itemId); - - /// <summary> - /// Saves the metadata status. - /// </summary> - /// <param name="status">The status.</param> - /// <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(); - } -} diff --git a/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs b/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs deleted file mode 100644 index 970f7a7be..000000000 --- a/MediaBrowser.Controller/Providers/ISeriesOrderManager.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface ISeriesOrderManager - { - Task<int?> FindSeriesIndex(string orderType, string seriesName); - void AddParts(IEnumerable<ISeriesOrderProvider> orderProviders); - } -} diff --git a/MediaBrowser.Controller/Providers/ISeriesOrderProvider.cs b/MediaBrowser.Controller/Providers/ISeriesOrderProvider.cs deleted file mode 100644 index ee0f3c197..000000000 --- a/MediaBrowser.Controller/Providers/ISeriesOrderProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface ISeriesOrderProvider - { - string OrderType { get; } - Task<int?> FindSeriesIndex(string seriesName); - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/ItemIdentifier.cs b/MediaBrowser.Controller/Providers/ItemIdentifier.cs deleted file mode 100644 index bbc6dd76c..000000000 --- a/MediaBrowser.Controller/Providers/ItemIdentifier.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public static class ItemIdentifier<TLookupInfo> - where TLookupInfo : ItemLookupInfo - { - public static async Task FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken) - { - var providers = providerManager.GetItemIdentityProviders<TLookupInfo>(); - var converters = providerManager.GetItemIdentityConverters<TLookupInfo>().ToList(); - - foreach (var provider in providers) - { - await provider.Identify(item); - } - - bool changesMade = true; - - while (changesMade) - { - changesMade = false; - - foreach (var converter in converters) - { - if (await converter.Convert(item)) - { - changesMade = true; - } - } - } - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/ItemIdentities.cs b/MediaBrowser.Controller/Providers/ItemIdentities.cs deleted file mode 100644 index 48316d0f4..000000000 --- a/MediaBrowser.Controller/Providers/ItemIdentities.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface IItemIdentityProvider<in TLookupInfo> : IItemIdentityProvider - where TLookupInfo : ItemLookupInfo - { - Task Identify(TLookupInfo info); - } - - public interface IItemIdentityConverter<in TLookupInfo> : IItemIdentityConverter - where TLookupInfo : ItemLookupInfo - { - Task<bool> Convert(TLookupInfo info); - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs index d16a73028..63cc48058 100644 --- a/MediaBrowser.Controller/Providers/ItemInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemInfo.cs @@ -1,3 +1,4 @@ +using System; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; @@ -5,10 +6,6 @@ namespace MediaBrowser.Controller.Providers { public class ItemInfo { - public ItemInfo() - { - } - public ItemInfo(IHasMetadata item) { Path = item.Path; @@ -21,8 +18,11 @@ namespace MediaBrowser.Controller.Providers VideoType = video.VideoType; IsPlaceHolder = video.IsPlaceHolder; } + + ItemType = item.GetType(); } + public Type ItemType { get; set; } public string Path { get; set; } public string ContainingFolderPath { get; set; } public VideoType VideoType { get; set; } diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index 29f4feb3d..9427b2afd 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -1,5 +1,6 @@ using System.Linq; using CommonIO; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Providers { @@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Providers public bool IsPostRecursiveRefresh { get; set; } public MetadataRefreshMode MetadataRefreshMode { get; set; } + public RemoteSearchResult SearchResult { get; set; } public bool ForceSave { get; set; } @@ -37,6 +39,7 @@ namespace MediaBrowser.Controller.Providers ImageRefreshMode = copy.ImageRefreshMode; ReplaceAllImages = copy.ReplaceAllImages; ReplaceImages = copy.ReplaceImages.ToList(); + SearchResult = copy.SearchResult; } } } diff --git a/MediaBrowser.Controller/Providers/MetadataStatus.cs b/MediaBrowser.Controller/Providers/MetadataStatus.cs deleted file mode 100644 index b19377a68..000000000 --- a/MediaBrowser.Controller/Providers/MetadataStatus.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; - -namespace MediaBrowser.Controller.Providers -{ - public class MetadataStatus - { - /// <summary> - /// Gets or sets the item identifier. - /// </summary> - /// <value>The item identifier.</value> - public Guid ItemId { get; set; } - - /// <summary> - /// Gets or sets the date last metadata refresh. - /// </summary> - /// <value>The date last metadata refresh.</value> - public DateTime? DateLastMetadataRefresh { get; set; } - - /// <summary> - /// Gets or sets the date last images refresh. - /// </summary> - /// <value>The date last images refresh.</value> - public DateTime? DateLastImagesRefresh { get; set; } - - public DateTime? ItemDateModified { get; set; } - - public bool IsDirty { get; private set; } - - public void SetDateLastMetadataRefresh(DateTime? date) - { - if (date != DateLastMetadataRefresh) - { - IsDirty = true; - } - - DateLastMetadataRefresh = date; - } - - public void SetDateLastImagesRefresh(DateTime? date) - { - if (date != DateLastImagesRefresh) - { - IsDirty = true; - } - - DateLastImagesRefresh = date; - } - } -} diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index fa74c5749..6659d1553 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -315,9 +315,8 @@ namespace MediaBrowser.Controller.Session /// <summary> /// Revokes the user tokens. /// </summary> - /// <param name="userId">The user identifier.</param> /// <returns>Task.</returns> - Task RevokeUserTokens(string userId); + Task RevokeUserTokens(string userId, string currentAccessToken); /// <summary> /// Revokes the token. diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs index e781c048b..a2371426a 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs @@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Subtitles public int? ParentIndexNumber { get; set; } public int? ProductionYear { get; set; } public long? RuntimeTicks { get; set; } + public bool IsPerfectMatch { get; set; } public Dictionary<string, string> ProviderIds { get; set; } public bool SearchAllProviders { get; set; } |
