diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index c9b1c5c53..c3b031448 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -183,50 +183,6 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); } - protected IList GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func filter) - { - if (!string.IsNullOrEmpty(parentId)) - { - var folder = (Folder)libraryManager.GetItemById(new Guid(parentId)); - - if (!string.IsNullOrWhiteSpace(userId)) - { - var user = userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return folder - .GetRecursiveChildren(user, filter) - .ToList(); - } - - return folder - .GetRecursiveChildren(filter); - } - if (!string.IsNullOrWhiteSpace(userId)) - { - var user = userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return userManager - .GetUserById(userId) - .RootFolder - .GetRecursiveChildren(user, filter) - .ToList(); - } - - return libraryManager - .RootFolder - .GetRecursiveChildren(filter); - } - /// /// Deslugs an artist name by finding the correct entry in the library /// diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 93cc01079..a27c872f1 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -102,12 +102,16 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetGameSystemSummaries request) { - var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem) + var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(GameSystem).Name } + }; + var parentIds = new string[] { } ; + var gameSystems = _libraryManager.GetItems(query, parentIds) .Cast() .ToList(); - var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); - var result = gameSystems .Select(i => GetSummary(i, user)) .ToList(); @@ -119,8 +123,15 @@ namespace MediaBrowser.Api public object Get(GetPlayerIndex request) { - var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game) - .Cast(); + var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Game).Name } + }; + var parentIds = new string[] { }; + var games = _libraryManager.GetItems(query, parentIds) + .Cast() + .ToList(); var lookup = games .ToLookup(i => i.PlayersSupported ?? -1) diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs index 29a982629..1224fa957 100644 --- a/MediaBrowser.Api/Library/FileOrganizationService.cs +++ b/MediaBrowser.Api/Library/FileOrganizationService.cs @@ -1,9 +1,11 @@ -using MediaBrowser.Controller.FileOrganization; +using System.Collections.Generic; +using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Net; using MediaBrowser.Model.FileOrganization; using MediaBrowser.Model.Querying; using ServiceStack; using System.Threading.Tasks; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Api.Library { @@ -74,6 +76,31 @@ namespace MediaBrowser.Api.Library public bool RememberCorrection { get; set; } } + [Route("/Library/FileOrganizations/SmartMatches", "GET", Summary = "Gets smart match entries")] + public class GetSmartMatchInfos : IReturn> + { + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + } + + [Route("/Library/FileOrganizations/SmartMatches/Delete", "POST", Summary = "Deletes a smart match entry")] + public class DeleteSmartMatchEntry + { + [ApiMember(Name = "Entries", Description = "SmartMatch Entry", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public List Entries { get; set; } + } + [Authenticated(Roles = "Admin")] public class FileOrganizationService : BaseApiService { @@ -130,5 +157,24 @@ namespace MediaBrowser.Api.Library Task.WaitAll(task); } + + public object Get(GetSmartMatchInfos request) + { + var result = _iFileOrganizationService.GetSmartMatchInfos(new FileOrganizationResultQuery + { + Limit = request.Limit, + StartIndex = request.StartIndex + }); + + return ToOptimizedSerializedResultUsingCache(result); + } + + public void Post(DeleteSmartMatchEntry request) + { + request.Entries.ForEach(entry => + { + _iFileOrganizationService.DeleteSmartMatchEntry(entry.Name, entry.Value); + }); + } } } diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index b7066a36d..896f8c990 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -424,7 +424,7 @@ namespace MediaBrowser.Api.Library public object Get(GetMediaFolders request) { - var items = _libraryManager.GetUserRootFolder().Children.OrderBy(i => i.SortName).ToList(); + var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList(); if (request.IsHidden.HasValue) { @@ -569,7 +569,7 @@ namespace MediaBrowser.Api.Library { throw new ArgumentException("This command cannot be used for remote or virtual items."); } - if (_fileSystem.DirectoryExists(item.Path)) + if (_fileSystem.DirectoryExists(item.Path)) { throw new ArgumentException("This command cannot be used for directories."); } @@ -618,7 +618,7 @@ namespace MediaBrowser.Api.Library var dtoOptions = GetDtoOptions(request); - BaseItem parent = item.Parent; + BaseItem parent = item.GetParent(); while (parent != null) { @@ -629,7 +629,7 @@ namespace MediaBrowser.Api.Library baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user)); - parent = parent.Parent; + parent = parent.GetParent(); } return baseItemDtos.ToList(); @@ -637,7 +637,7 @@ namespace MediaBrowser.Api.Library private BaseItem TranslateParentItem(BaseItem item, User user) { - if (item.Parent is AggregateFolder) + if (item.GetParent() is AggregateFolder) { return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path)); } @@ -685,6 +685,50 @@ namespace MediaBrowser.Api.Library return ToOptimizedSerializedResultUsingCache(counts); } + private IList GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func filter) + { + if (!string.IsNullOrEmpty(parentId)) + { + var folder = (Folder)libraryManager.GetItemById(new Guid(parentId)); + + if (!string.IsNullOrWhiteSpace(userId)) + { + var user = userManager.GetUserById(userId); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return folder + .GetRecursiveChildren(user, filter) + .ToList(); + } + + return folder + .GetRecursiveChildren(filter); + } + if (!string.IsNullOrWhiteSpace(userId)) + { + var user = userManager.GetUserById(userId); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return userManager + .GetUserById(userId) + .RootFolder + .GetRecursiveChildren(user, filter) + .ToList(); + } + + return libraryManager + .RootFolder + .GetRecursiveChildren(filter); + } + private bool FilterItem(BaseItem item, GetItemCounts request, string userId) { if (!string.IsNullOrWhiteSpace(userId)) @@ -745,12 +789,10 @@ namespace MediaBrowser.Api.Library return Task.FromResult(true); } - if (item is ILiveTvRecording) + return item.Delete(new DeleteOptions { - return _liveTv.DeleteRecording(i); - } - - return _libraryManager.DeleteItem(item); + DeleteFileLocation = true + }); }).ToArray(); Task.WaitAll(tasks); @@ -847,9 +889,9 @@ namespace MediaBrowser.Api.Library : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null) + while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null) { - item = item.Parent; + item = item.GetParent(); } var dtoOptions = GetDtoOptions(request); @@ -890,9 +932,9 @@ namespace MediaBrowser.Api.Library : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null) + while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null) { - item = item.Parent; + item = item.GetParent(); } var dtoOptions = GetDtoOptions(request); diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 7e55446ae..f711c69e6 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -80,6 +80,7 @@ + diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index fe8bae1a5..36cbc6ffa 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -117,10 +117,7 @@ namespace MediaBrowser.Api.Movies public async Task Get(GetSimilarMovies request) { var result = await GetSimilarItemsResult( - // Strip out secondary versions - request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, - - SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); return ToOptimizedSerializedResultUsingCache(result); } @@ -128,10 +125,7 @@ namespace MediaBrowser.Api.Movies public async Task Get(GetSimilarTrailers request) { var result = await GetSimilarItemsResult( - // Strip out secondary versions - request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, - - SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); return ToOptimizedSerializedResultUsingCache(result); } @@ -140,8 +134,12 @@ namespace MediaBrowser.Api.Movies { var user = _userManager.GetUserById(request.UserId); - IEnumerable movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie); - + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Movie).Name } + }; + var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId }; + var movies = _libraryManager.GetItems(query, parentIds); movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies); var listEligibleForCategories = new List(); @@ -184,21 +182,27 @@ namespace MediaBrowser.Api.Movies return ToOptimizedResult(result); } - private async Task GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func includeInSearch, Func, List, BaseItem, int> getSimilarityScore) + private async Task GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func, List, BaseItem, int> getSimilarityScore) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) ? (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - - Func filter = i => i.Id != item.Id && includeInSearch(i); - - var inputItems = user == null - ? _libraryManager.RootFolder.GetRecursiveChildren(filter) - : user.RootFolder.GetRecursiveChildren(user, filter); - - var list = inputItems.ToList(); + + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Movie).Name } + }; + var parentIds = new string[] { }; + var list = _libraryManager.GetItems(query, parentIds) + .Where(i => + { + // Strip out secondary versions + var v = i as Video; + return v != null && !v.PrimaryVersionId.HasValue; + }) + .ToList(); if (user != null && user.Configuration.IncludeTrailersInSuggestions) { @@ -379,9 +383,10 @@ namespace MediaBrowser.Api.Movies { foreach (var name in names) { - var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery + var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery(user) { Person = name + }); var items = allMovies diff --git a/MediaBrowser.Api/PackageReviewService.cs b/MediaBrowser.Api/PackageReviewService.cs index b4656b83e..e70d6a713 100644 --- a/MediaBrowser.Api/PackageReviewService.cs +++ b/MediaBrowser.Api/PackageReviewService.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Api { private readonly IHttpClient _httpClient; private readonly IJsonSerializer _serializer; - private const string MbAdminUrl = "http://www.mb3admin.com/admin/"; + private const string MbAdminUrl = "https://www.mb3admin.com/admin/"; private readonly IServerApplicationHost _appHost; public PackageReviewService(IHttpClient httpClient, IJsonSerializer serializer, IServerApplicationHost appHost) diff --git a/MediaBrowser.Api/PinLoginService.cs b/MediaBrowser.Api/PinLoginService.cs new file mode 100644 index 000000000..8b63de10a --- /dev/null +++ b/MediaBrowser.Api/PinLoginService.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Concurrent; +using System.Globalization; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Connect; +using ServiceStack; + +namespace MediaBrowser.Api +{ + [Route("/Auth/Pin", "POST", Summary = "Creates a pin request")] + public class CreatePinRequest : IReturn + { + [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string DeviceId { get; set; } + } + + [Route("/Auth/Pin", "GET", Summary = "Gets pin status")] + public class GetPinStatusRequest : IReturn + { + [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string DeviceId { get; set; } + [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string Pin { get; set; } + } + + [Route("/Auth/Pin/Exchange", "POST", Summary = "Exchanges a pin")] + public class ExchangePinRequest : IReturn + { + [ApiMember(Name = "DeviceId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string DeviceId { get; set; } + [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Pin { get; set; } + } + + [Route("/Auth/Pin/Validate", "POST", Summary = "Validates a pin")] + [Authenticated] + public class ValidatePinRequest : IReturnVoid + { + [ApiMember(Name = "Pin", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Pin { get; set; } + } + + public class PinLoginService : BaseApiService + { + private readonly ConcurrentDictionary _activeRequests = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + public object Post(CreatePinRequest request) + { + var pin = GetNewPin(); + + var value = new MyPinStatus + { + CreationTimeUtc = DateTime.UtcNow, + IsConfirmed = false, + IsExpired = false, + Pin = pin, + DeviceId = request.DeviceId + }; + + _activeRequests.AddOrUpdate(pin, value, (k, v) => value); + + return ToOptimizedResult(new PinCreationResult + { + DeviceId = request.DeviceId, + IsConfirmed = false, + IsExpired = false, + Pin = pin + }); + } + + public object Get(GetPinStatusRequest request) + { + MyPinStatus status; + + if (!_activeRequests.TryGetValue(request.Pin, out status)) + { + throw new ResourceNotFoundException(); + } + + EnsureValid(request.DeviceId, status); + + return ToOptimizedResult(new PinStatusResult + { + Pin = status.Pin, + IsConfirmed = status.IsConfirmed, + IsExpired = status.IsExpired + }); + } + + public object Post(ExchangePinRequest request) + { + MyPinStatus status; + + if (!_activeRequests.TryGetValue(request.Pin, out status)) + { + throw new ResourceNotFoundException(); + } + + EnsureValid(request.DeviceId, status); + + if (!status.IsConfirmed) + { + throw new ResourceNotFoundException(); + } + + return ToOptimizedResult(new PinExchangeResult + { + // TODO: Add access token + UserId = status.UserId + }); + } + + public void Post(ValidatePinRequest request) + { + MyPinStatus status; + + if (!_activeRequests.TryGetValue(request.Pin, out status)) + { + throw new ResourceNotFoundException(); + } + + EnsureValid(status); + + status.IsConfirmed = true; + status.UserId = AuthorizationContext.GetAuthorizationInfo(Request).UserId; + } + + private void EnsureValid(string requestedDeviceId, MyPinStatus status) + { + if (!string.Equals(requestedDeviceId, status.DeviceId, StringComparison.OrdinalIgnoreCase)) + { + throw new ResourceNotFoundException(); + } + + EnsureValid(status); + } + + private void EnsureValid(MyPinStatus status) + { + if ((DateTime.UtcNow - status.CreationTimeUtc).TotalMinutes > 10) + { + status.IsExpired = true; + } + + if (status.IsExpired) + { + throw new ResourceNotFoundException(); + } + } + + private string GetNewPin() + { + var pin = GetNewPinInternal(); + + while (IsPinActive(pin)) + { + pin = GetNewPinInternal(); + } + + return pin; + } + + private string GetNewPinInternal() + { + var length = 5; + var pin = string.Empty; + + while (pin.Length < length) + { + var digit = new Random().Next(0, 9); + pin += digit.ToString(CultureInfo.InvariantCulture); + } + + return pin; + } + + private bool IsPinActive(string pin) + { + MyPinStatus status; + + if (!_activeRequests.TryGetValue(pin, out status)) + { + return true; + } + + if (status.IsExpired) + { + return true; + } + + return false; + } + + public class MyPinStatus : PinStatusResult + { + public DateTime CreationTimeUtc { get; set; } + public string DeviceId { get; set; } + public string UserId { get; set; } + } + } +} diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index bb7f6bb1e..bae8074fd 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -305,9 +305,8 @@ namespace MediaBrowser.Api.Playback /// /// The state. /// The video codec. - /// if set to true [is HLS]. /// System.String. - protected string GetVideoQualityParam(StreamState state, string videoCodec, bool isHls) + protected string GetVideoQualityParam(StreamState state, string videoCodec) { var param = string.Empty; @@ -385,7 +384,7 @@ namespace MediaBrowser.Api.Playback param = "-mbd 2"; } - param += GetVideoBitrateParam(state, videoCodec, isHls); + param += GetVideoBitrateParam(state, videoCodec); var framerate = GetFramerateParam(state); if (framerate.HasValue) @@ -1190,7 +1189,7 @@ namespace MediaBrowser.Api.Playback return bitrate; } - protected string GetVideoBitrateParam(StreamState state, string videoCodec, bool isHls) + protected string GetVideoBitrateParam(StreamState state, string videoCodec) { var bitrate = state.OutputVideoBitrate; @@ -1209,14 +1208,9 @@ namespace MediaBrowser.Api.Playback } // h264 - if (isHls) - { - return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", - bitrate.Value.ToString(UsCulture), - (bitrate.Value * 2).ToString(UsCulture)); - } - - return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); + return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", + bitrate.Value.ToString(UsCulture), + (bitrate.Value * 2).ToString(UsCulture)); } return string.Empty; diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index 9ec16fcc7..defb2eef0 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs @@ -430,7 +430,7 @@ namespace MediaBrowser.Api.Playback.Dash var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg; + args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg; // Add resolution params, if specified if (!hasGraphicalSubs) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index baf662c34..4a83615b4 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -822,7 +822,7 @@ namespace MediaBrowser.Api.Playback.Hls var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg; + args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg; //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index be1db1a4d..22c38009a 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -106,7 +106,7 @@ namespace MediaBrowser.Api.Playback.Hls var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - args += " " + GetVideoQualityParam(state, GetH264Encoder(state), true) + keyFrameArg; + args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg; // Add resolution params, if specified if (!hasGraphicalSubs) diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 19e568ec4..eaf65bd6b 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -166,7 +166,7 @@ namespace MediaBrowser.Api.Playback.Progressive args += GetOutputSizeParam(state, videoCodec); } - var qualityParam = GetVideoQualityParam(state, videoCodec, false); + var qualityParam = GetVideoQualityParam(state, videoCodec); if (!string.IsNullOrEmpty(qualityParam)) { diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 602119d4f..89fc29f3d 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -284,7 +284,7 @@ namespace MediaBrowser.Api private T GetParentWithImage(BaseItem item, ImageType type) where T : BaseItem { - return item.Parents.OfType().FirstOrDefault(i => i.HasImage(type)); + return item.GetParents().OfType().FirstOrDefault(i => i.HasImage(type)); } } } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index b05a1bd20..06db1de74 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -66,12 +66,8 @@ namespace MediaBrowser.Api { _config.Configuration.IsStartupWizardCompleted = true; _config.Configuration.EnableLocalizedGuids = true; - _config.Configuration.EnableLibraryMetadataSubFolder = true; _config.Configuration.EnableCustomPathSubFolders = true; - _config.Configuration.DisableStartupScan = true; - _config.Configuration.EnableUserViews = true; _config.Configuration.EnableDateLastRefresh = true; - _config.Configuration.MergeMetadataAndImagesByName = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 29a4a8bb5..2dad9533f 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string StartItemId { get; set; } - + /// /// Skips over a given number of items within the results. Use for paging. /// @@ -273,29 +273,28 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode); - - var itemsList = _libraryManager - .Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending) - .Cast() - .ToList(); - - var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList(); - var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime(); - var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList(); - previousEpisodes.AddRange(unairedEpisodes); + var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId }; - var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit); + var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { "PremiereDate", "AirTime", "SortName" }, + SortOrder = SortOrder.Ascending, + MinPremiereDate = minPremiereDate, + StartIndex = request.StartIndex, + Limit = request.Limit + + }, parentIds); var options = GetDtoOptions(request); - var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray(); + var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray(); var result = new ItemsResult { - TotalRecordCount = itemsList.Count, + TotalRecordCount = itemsResult.TotalRecordCount, Items = returnItems }; @@ -440,7 +439,7 @@ namespace MediaBrowser.Api } episodes = season.GetEpisodes(user); - } + } else if (request.Season.HasValue) { var series = _libraryManager.GetItemById(request.Id) as Series; @@ -495,7 +494,7 @@ namespace MediaBrowser.Api .ToList(); var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit); - + var dtoOptions = GetDtoOptions(request); var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user) diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 23d4da60c..6867f6308 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -206,6 +206,8 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Genres { get; set; } + public string GenreIds { get; set; } + [ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string OfficialRatings { get; set; } @@ -385,6 +387,11 @@ namespace MediaBrowser.Api.UserLibrary return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); } + public string[] GetGenreIds() + { + return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + public string[] GetPersonTypes() { return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 97d0ad7ab..761d10760 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -112,6 +112,15 @@ namespace MediaBrowser.Api.UserLibrary user == null ? _libraryManager.RootFolder : user.RootFolder : parentItem; + if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) + { + item = user == null ? _libraryManager.RootFolder : user.RootFolder; + } + else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) + { + item = user == null ? _libraryManager.RootFolder : user.RootFolder; + } + // Default list type = children if (!string.IsNullOrEmpty(request.Ids)) @@ -211,6 +220,7 @@ namespace MediaBrowser.Api.UserLibrary Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), + GenreIds = request.GetGenreIds(), Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, @@ -423,15 +433,6 @@ namespace MediaBrowser.Api.UserLibrary return false; } - // Min index number - if (request.MinIndexNumber.HasValue) - { - if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value)) - { - return false; - } - } - // Min official rating if (!string.IsNullOrEmpty(request.MinOfficialRating)) { diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 9d7c38d6f..d29191db4 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -26,6 +26,8 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] public bool? IncludeExternalContent { get; set; } + + public string PresetViews { get; set; } } [Route("/Users/{UserId}/SpecialViewOptions", "GET")] @@ -75,9 +77,24 @@ namespace MediaBrowser.Api.UserLibrary query.IncludeExternalContent = request.IncludeExternalContent.Value; } + if (!string.IsNullOrWhiteSpace(request.PresetViews)) + { + query.PresetViews = request.PresetViews.Split(','); + } + + var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty; + if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1) + { + query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; + } + //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; + var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); var dtoOptions = GetDtoOptions(request); + dtoOptions.Fields = new List(); + dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio); + dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId); var user = _userManager.GetUserById(request.UserId); @@ -123,7 +140,7 @@ namespace MediaBrowser.Api.UserLibrary var views = user.RootFolder .GetChildren(user, true) .OfType() - .Where(i => !UserView.IsExcludedFromGrouping(i)) + .Where(UserView.IsEligibleForGrouping) .ToList(); var list = views @@ -141,9 +158,7 @@ namespace MediaBrowser.Api.UserLibrary private bool IsEligibleForSpecialView(ICollectionFolder view) { - var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos }; - - return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return UserView.IsEligibleForEnhancedView(view.CollectionType); } } diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index 2a93efcde..caf8f54a6 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -453,7 +453,7 @@ namespace MediaBrowser.Common.Implementations RegisterSingleInstance(ApplicationPaths); - TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger, FileSystemManager); + TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager); RegisterSingleInstance(JsonSerializer); RegisterSingleInstance(XmlSerializer); @@ -465,7 +465,7 @@ namespace MediaBrowser.Common.Implementations RegisterSingleInstance(FileSystemManager); - HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, FileSystemManager); + HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager); RegisterSingleInstance(HttpClient); NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager")); @@ -474,7 +474,7 @@ namespace MediaBrowser.Common.Implementations SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager); RegisterSingleInstance(SecurityManager); - InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager); + InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager); RegisterSingleInstance(InstallationManager); ZipClient = new ZipClient(FileSystemManager); diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs index 845c984fb..b5566650c 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs @@ -55,6 +55,25 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks private ILogger Logger { get; set; } private readonly IFileSystem _fileSystem; + private bool _suspendTriggers; + + public bool SuspendTriggers + { + get { return _suspendTriggers; } + set + { + Logger.Info("Setting SuspendTriggers to {0}", value); + var executeQueued = _suspendTriggers && !value; + + _suspendTriggers = value; + + if (executeQueued) + { + ExecuteQueuedTasks(); + } + } + } + /// /// Initializes a new instance of the class. /// @@ -151,6 +170,31 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks QueueScheduledTask(new TaskExecutionOptions()); } + public void Execute() + where T : IScheduledTask + { + var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T)); + + if (scheduledTask == null) + { + Logger.Error("Unable to find scheduled task of type {0} in Execute.", typeof(T).Name); + } + else + { + var type = scheduledTask.ScheduledTask.GetType(); + + Logger.Info("Queueing task {0}", type.Name); + + lock (_taskQueue) + { + if (scheduledTask.State == TaskState.Idle) + { + Execute(scheduledTask, new TaskExecutionOptions()); + } + } + } + } + /// /// Queues the scheduled task. /// @@ -183,7 +227,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks lock (_taskQueue) { - if (task.State == TaskState.Idle) + if (task.State == TaskState.Idle && !SuspendTriggers) { Execute(task, options); return; @@ -273,6 +317,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// private void ExecuteQueuedTasks() { + if (SuspendTriggers) + { + return; + } + + Logger.Info("ExecuteQueuedTasks"); + // Execute queued tasks lock (_taskQueue) { diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs index 8d271859e..c1dc304f6 100644 --- a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs +++ b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs @@ -66,7 +66,12 @@ namespace MediaBrowser.Common.ScheduledTasks void Cancel(IScheduledTaskWorker task); Task Execute(IScheduledTaskWorker task, TaskExecutionOptions options = null); + void Execute() + where T : IScheduledTask; + event EventHandler> TaskExecuting; event EventHandler TaskCompleted; + + bool SuspendTriggers { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 653cec901..17dcf138b 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Channels public List ChannelMediaSources { get; set; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + return UnratedItem.ChannelContent; } protected override string CreateUserDataKey() diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs index 9010470f8..f662020bb 100644 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs @@ -6,6 +6,7 @@ using System; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Channels @@ -20,6 +21,11 @@ namespace MediaBrowser.Controller.Channels return false; } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.ChannelContent; + } + [IgnoreDataMember] public override bool SupportsLocalMetadata { diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index fb545e57a..79ad4b36b 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -42,9 +42,9 @@ namespace MediaBrowser.Controller.Channels return ExternalId; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + return UnratedItem.ChannelContent; } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 77a81d0c8..5f0442f93 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using System; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using System.Collections.Generic; @@ -44,10 +45,10 @@ namespace MediaBrowser.Controller.Dto /// /// Fills the synchronize information. /// - /// The dtos. + /// The tuples. /// The options. /// The user. - void FillSyncInfo(IEnumerable dtos, DtoOptions options, User user); + void FillSyncInfo(IEnumerable> tuples, DtoOptions options, User user); /// /// Gets the base item dto. diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 43b980c20..766f1e5ed 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -27,9 +27,22 @@ namespace MediaBrowser.Controller.Entities.Audio public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } - public List Tags { get; set; } public ExtraType? ExtraType { get; set; } + /// + /// Gets or sets the artist. + /// + /// The artist. + public List Artists { get; set; } + + public List AlbumArtists { get; set; } + + /// + /// Gets or sets the album. + /// + /// The album. + public string Album { get; set; } + [IgnoreDataMember] public bool IsThemeMedia { @@ -43,7 +56,6 @@ namespace MediaBrowser.Controller.Entities.Audio { Artists = new List(); AlbumArtists = new List(); - Tags = new List(); } [IgnoreDataMember] @@ -92,14 +104,6 @@ namespace MediaBrowser.Controller.Entities.Audio locationType != LocationType.Virtual; } - /// - /// Gets or sets the artist. - /// - /// The artist. - public List Artists { get; set; } - - public List AlbumArtists { get; set; } - [IgnoreDataMember] public List AllArtists { @@ -114,12 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio } } - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - [IgnoreDataMember] public MusicAlbum AlbumEntity { @@ -173,9 +171,9 @@ namespace MediaBrowser.Controller.Entities.Audio return base.CreateUserDataKey(); } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Music); + return UnratedItem.Music; } public SongInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 654c9abd3..c5ce6a2f7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities.Audio { get { - return Parents.OfType().FirstOrDefault(); + return GetParents().OfType().FirstOrDefault(); } } @@ -114,13 +114,18 @@ namespace MediaBrowser.Controller.Entities.Audio return config.BlockUnratedItems.Contains(UnratedItem.Music); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + public AlbumInfo GetLookupInfo() { var id = GetItemLookupInfo(); id.AlbumArtists = AlbumArtists; - var artist = Parents.OfType().FirstOrDefault(); + var artist = GetParents().OfType().FirstOrDefault(); if (artist != null) { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index f6d1d32a4..02bcceada 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -138,6 +138,11 @@ namespace MediaBrowser.Controller.Entities.Audio return config.BlockUnratedItems.Contains(UnratedItem.Music); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken) { var items = GetRecursiveChildren().ToList(); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 59e2b87e3..d52e2b37f 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; @@ -24,6 +23,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Controller.Entities { @@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Entities { protected BaseItem() { + Tags = new List(); Genres = new List(); Studios = new List(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities /// /// The supported image extensions /// - public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; + public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" }; public static readonly List SupportedImageExtensionsList = SupportedImageExtensions.ToList(); @@ -103,7 +104,8 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the name. /// /// The name. - public string Name + [IgnoreDataMember] + public virtual string Name { get { @@ -122,14 +124,23 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the id. /// /// The id. + [IgnoreDataMember] public Guid Id { get; set; } /// /// Gets or sets a value indicating whether this instance is hd. /// /// true if this instance is hd; otherwise, false. + [IgnoreDataMember] public bool? IsHD { get; set; } + /// + /// Gets or sets the audio. + /// + /// The audio. + [IgnoreDataMember] + public ProgramAudio? Audio { get; set; } + /// /// Return the id that should be used to key display prefs for this item. /// Default is based on the type for everything except actual generic folders. @@ -149,6 +160,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the path. /// /// The path. + [IgnoreDataMember] public virtual string Path { get; set; } [IgnoreDataMember] @@ -173,7 +185,7 @@ namespace MediaBrowser.Controller.Entities } /// - /// Id of the program. + /// If this content came from an external service, the id of the content on that service /// [IgnoreDataMember] public string ExternalId @@ -201,11 +213,6 @@ namespace MediaBrowser.Controller.Entities } } - public virtual bool IsHiddenFromUser(User user) - { - return false; - } - [IgnoreDataMember] public virtual bool IsOwnedItem { @@ -325,12 +332,14 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the date created. /// /// The date created. + [IgnoreDataMember] public DateTime DateCreated { get; set; } /// /// Gets or sets the date modified. /// /// The date modified. + [IgnoreDataMember] public DateTime DateModified { get; set; } public DateTime DateLastSaved { get; set; } @@ -407,6 +416,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the name of the forced sort. /// /// The name of the forced sort. + [IgnoreDataMember] public string ForcedSortName { get { return _forcedSortName; } @@ -447,10 +457,7 @@ namespace MediaBrowser.Controller.Entities { var idString = Id.ToString("N"); - if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder) - { - basePath = System.IO.Path.Combine(basePath, "library"); - } + basePath = System.IO.Path.Combine(basePath, "library"); return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); } @@ -493,6 +500,7 @@ namespace MediaBrowser.Controller.Entities return sortable; } + [IgnoreDataMember] public Guid ParentId { get; set; } /// @@ -502,15 +510,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public Folder Parent { - get - { - if (ParentId != Guid.Empty) - { - return LibraryManager.GetItemById(ParentId) as Folder; - } - - return null; - } + get { return GetParent() as Folder; } set { @@ -525,16 +525,28 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public IEnumerable Parents { - get + get { return GetParents().OfType(); } + } + + public BaseItem GetParent() + { + if (ParentId != Guid.Empty) { - var parent = Parent; + return LibraryManager.GetItemById(ParentId); + } - while (parent != null) - { - yield return parent; + return null; + } - parent = parent.Parent; - } + public IEnumerable GetParents() + { + var parent = GetParent(); + + while (parent != null) + { + yield return parent; + + parent = parent.GetParent(); } } @@ -546,19 +558,20 @@ namespace MediaBrowser.Controller.Entities public T FindParent() where T : Folder { - return Parents.OfType().FirstOrDefault(); + return GetParents().OfType().FirstOrDefault(); } [IgnoreDataMember] public virtual BaseItem DisplayParent { - get { return Parent; } + get { return GetParent(); } } /// /// When the item first debuted. For movies this could be premiere date, episodes would be first aired /// /// The premiere date. + [IgnoreDataMember] public DateTime? PremiereDate { get; set; } /// @@ -572,31 +585,35 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the display type of the media. /// /// The display type of the media. + [IgnoreDataMember] public string DisplayMediaType { get; set; } /// /// Gets or sets the official rating. /// /// The official rating. + [IgnoreDataMember] public string OfficialRating { get; set; } /// /// Gets or sets the official rating description. /// /// The official rating description. + [IgnoreDataMember] public string OfficialRatingDescription { get; set; } /// /// Gets or sets the custom rating. /// /// The custom rating. - //[IgnoreDataMember] + [IgnoreDataMember] public string CustomRating { get; set; } /// /// Gets or sets the overview. /// /// The overview. + [IgnoreDataMember] public string Overview { get; set; } /// @@ -609,37 +626,48 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the genres. /// /// The genres. + [IgnoreDataMember] public List Genres { get; set; } + /// + /// Gets or sets the tags. + /// + /// The tags. + public List Tags { get; set; } + /// /// Gets or sets the home page URL. /// /// The home page URL. + [IgnoreDataMember] public string HomePageUrl { get; set; } /// /// Gets or sets the community rating. /// /// The community rating. - //[IgnoreDataMember] + [IgnoreDataMember] public float? CommunityRating { get; set; } /// /// Gets or sets the community rating vote count. /// /// The community rating vote count. + [IgnoreDataMember] public int? VoteCount { get; set; } /// /// Gets or sets the run time ticks. /// /// The run time ticks. + [IgnoreDataMember] public long? RunTimeTicks { get; set; } /// /// Gets or sets the production year. /// /// The production year. + [IgnoreDataMember] public int? ProductionYear { get; set; } /// @@ -647,19 +675,34 @@ namespace MediaBrowser.Controller.Entities /// This could be episode number, album track number, etc. /// /// The index number. - //[IgnoreDataMember] + [IgnoreDataMember] public int? IndexNumber { get; set; } /// /// For an episode this could be the season number, or for a song this could be the disc number. /// /// The parent index number. + [IgnoreDataMember] public int? ParentIndexNumber { get; set; } [IgnoreDataMember] - public virtual string OfficialRatingForComparison + public string OfficialRatingForComparison { - get { return OfficialRating; } + get + { + if (!string.IsNullOrWhiteSpace(OfficialRating)) + { + return OfficialRating; + } + + var parent = DisplayParent; + if (parent != null) + { + return parent.OfficialRatingForComparison; + } + + return null; + } } [IgnoreDataMember] @@ -721,21 +764,21 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths(files, directoryService, null) .OfType() .Select(audio => - { - // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio; - - if (dbItem != null) { - audio = dbItem; - } + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio; - audio.ExtraType = ExtraType.ThemeSong; + if (dbItem != null) + { + audio = dbItem; + } - return audio; + audio.ExtraType = ExtraType.ThemeSong; - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); + return audio; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); } /// @@ -751,21 +794,21 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths(files, directoryService, null) .OfType