diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index d4b5be5848..7a14ace777 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -12,6 +11,7 @@ using ServiceStack.Web; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace MediaBrowser.Api { @@ -344,11 +344,7 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder - .GetRecursiveChildren() - .SelectMany(i => i.People) - .Select(i => i.Name) - .DistinctNames() + return libraryManager.GetPeopleNames(new InternalPeopleQuery()) .FirstOrDefault(i => { i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar)); @@ -364,7 +360,8 @@ namespace MediaBrowser.Api var first = pathInfo.GetArgumentValue(0); // backwards compatibility - if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) || + string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase)) { index++; } diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 8f7edabbbd..b8ae9392ae 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -218,6 +218,11 @@ namespace MediaBrowser.Api await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + if (request.People != null) + { + await _libraryManager.UpdatePeople(item, request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList()); + } + if (isLockedChanged && item.IsFolder) { var folder = (Folder)item; @@ -303,11 +308,6 @@ namespace MediaBrowser.Api item.Studios = request.Studios.Select(x => x.Name).ToList(); } - if (request.People != null) - { - item.People = request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList(); - } - if (request.DateCreated.HasValue) { item.DateCreated = NormalizeDateTime(request.DateCreated.Value); diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 0dfd812c3a..e79163d803 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -106,6 +106,7 @@ + diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 513bde871b..97e9aa9c8f 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Api.Movies return ToOptimizedResult(result); } - private async Task GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func includeInSearch, Func getSimilarityScore) + private async Task GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func includeInSearch, Func, List, BaseItem, int> getSimilarityScore) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; @@ -214,7 +214,7 @@ namespace MediaBrowser.Api.Movies } } - var items = SimilarItemsHelper.GetSimilaritems(item, list, getSimilarityScore).ToList(); + var items = SimilarItemsHelper.GetSimilaritems(item, _libraryManager, list, getSimilarityScore).ToList(); IEnumerable returnItems = items; @@ -339,7 +339,7 @@ namespace MediaBrowser.Api.Movies foreach (var director in directors) { var items = allMovies - .Where(i => i.People.Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase))) + .Where(i => _libraryManager.GetPeople(i).Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase))) .Take(itemLimit) .ToList(); @@ -358,12 +358,15 @@ namespace MediaBrowser.Api.Movies private IEnumerable GetWithActor(User user, List allMovies, IEnumerable names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { - var userId = user.Id; - foreach (var name in names) { + var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery + { + Person = name + }); + var items = allMovies - .Where(i => i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))) + .Where(i => itemsWithActor.Contains(i.Id)) .Take(itemLimit) .ToList(); @@ -382,12 +385,10 @@ namespace MediaBrowser.Api.Movies private IEnumerable GetSimilarTo(User user, List allMovies, IEnumerable baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) { - var userId = user.Id; - foreach (var item in baselineItems) { var similar = SimilarItemsHelper - .GetSimilaritems(item, allMovies, SimilarItemsHelper.GetSimiliarityScore) + .GetSimilaritems(item, _libraryManager, allMovies, SimilarItemsHelper.GetSimiliarityScore) .Take(itemLimit) .ToList(); @@ -406,18 +407,37 @@ namespace MediaBrowser.Api.Movies private IEnumerable GetActors(IEnumerable items) { - // Get the two leading actors for all movies - return items - .SelectMany(i => i.People.Where(p => !string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)).Take(2)) + var people = _libraryManager.GetPeople(new InternalPeopleQuery + { + ExcludePersonTypes = new List + { + PersonType.Director + }, + MaxListOrder = 3 + }); + + var itemIds = items.Select(i => i.Id).ToList(); + + return people + .Where(i => itemIds.Contains(i.ItemId)) .Select(i => i.Name) .DistinctNames(); } private IEnumerable GetDirectors(IEnumerable items) { - return items - .Select(i => i.People.FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase))) - .Where(i => i != null) + var people = _libraryManager.GetPeople(new InternalPeopleQuery + { + PersonTypes = new List + { + PersonType.Director + } + }); + + var itemIds = items.Select(i => i.Id).ToList(); + + return people + .Where(i => itemIds.Contains(i.ItemId)) .Select(i => i.Name) .DistinctNames(); } diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index 37f79bf208..ea87c3ad37 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using ServiceStack; using System; +using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Api.Music @@ -68,11 +69,13 @@ namespace MediaBrowser.Api.Music /// Gets the album similarity score. /// /// The item1. + /// The item1 people. + /// All people. /// The item2. /// System.Int32. - private int GetAlbumSimilarityScore(BaseItem item1, BaseItem item2) + private int GetAlbumSimilarityScore(BaseItem item1, List item1People, List allPeople, BaseItem item2) { - var points = SimilarItemsHelper.GetSimiliarityScore(item1, item2); + var points = SimilarItemsHelper.GetSimiliarityScore(item1, item1People, allPeople, item2); var album1 = (MusicAlbum)item1; var album2 = (MusicAlbum)item2; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 41d785a34d..dc5858e863 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -632,7 +632,7 @@ namespace MediaBrowser.Api.Playback { var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); - filters.Add(string.Format("scale=trunc(oh*a*2)/2:min(ih\\,{0})", maxHeightParam)); + filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam)); } if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index ab57e561f5..0a432a5802 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -313,16 +313,17 @@ namespace MediaBrowser.Api.Playback.Hls { var segmentPath = GetSegmentPath(state, playlist, i); - double length; - if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length)) - { - Logger.Debug("Found segment length of {0} for index {1}", length, i); - startSeconds += length; - } - else - { - startSeconds += state.SegmentLength; - } + //double length; + //if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length)) + //{ + // Logger.Debug("Found segment length of {0} for index {1}", length, i); + // startSeconds += length; + //} + //else + //{ + // startSeconds += state.SegmentLength; + //} + startSeconds += state.SegmentLength; } var position = TimeSpan.FromSeconds(startSeconds).Ticks; @@ -441,7 +442,7 @@ namespace MediaBrowser.Api.Playback.Hls CancellationToken cancellationToken) { // If all transcoding has completed, just return immediately - if (transcodingJob != null && transcodingJob.HasExited) + if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath)) { return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob); } @@ -463,7 +464,11 @@ namespace MediaBrowser.Api.Playback.Hls // If it appears in the playlist, it's done if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1) { - return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob); + if (File.Exists(segmentPath)) + { + return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob); + } + break; } } } @@ -564,11 +569,11 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTM3U"); + var isLiveStream = (state.RunTimeTicks ?? 0) == 0; + var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); - var isLiveStream = (state.RunTimeTicks ?? 0) == 0; - // Main stream var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; playlistUrl += queryString; @@ -798,7 +803,7 @@ namespace MediaBrowser.Api.Playback.Hls var audioTranscodeParams = new List(); audioTranscodeParams.Add("-acodec " + codec); - + if (state.OutputAudioBitrate.HasValue) { audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture)); diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 4438876f7f..ebf5183c51 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -779,7 +779,7 @@ namespace MediaBrowser.Api.Reports .Select(p => p == null ? "-1" : p.Name) .ToList(); - if (!(names.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + if (!(names.Any(v => _libraryManager.GetPeople(i).Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) { return false; } @@ -792,7 +792,7 @@ namespace MediaBrowser.Api.Reports if (personTypes.Length == 0) { - if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) + if (!(_libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) { return false; } @@ -802,8 +802,7 @@ namespace MediaBrowser.Api.Reports var types = personTypes; var ok = new[] { i }.Any(item => - item.People != null && - item.People.Any(p => + _libraryManager.GetPeople(i).Any(p => p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); if (!ok) diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs index e297a2a575..541bb92d9f 100644 --- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs +++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs @@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Reports foreach (var item in t) { this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem, - items.SelectMany(x => x.People) + items.SelectMany(x => _libraryManager.GetPeople(x)) .Where(n => n.Type == item) .GroupBy(x => x.Name) .OrderByDescending(x => x.Count()) diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 91e7497783..d114446eeb 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Api /// The include in search. /// The get similarity score. /// ItemsResult. - internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func includeInSearch, Func getSimilarityScore) + internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func includeInSearch, Func, List, BaseItem, int> getSimilarityScore) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null; @@ -82,7 +82,7 @@ namespace MediaBrowser.Api ? libraryManager.RootFolder.GetRecursiveChildren(filter) : user.RootFolder.GetRecursiveChildren(user, filter); - var items = GetSimilaritems(item, inputItems, getSimilarityScore) + var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore) .ToList(); IEnumerable returnItems = items; @@ -106,15 +106,21 @@ namespace MediaBrowser.Api /// Gets the similaritems. /// /// The item. + /// The library manager. /// The input items. /// The get similarity score. /// IEnumerable{BaseItem}. - internal static IEnumerable GetSimilaritems(BaseItem item, IEnumerable inputItems, Func getSimilarityScore) + internal static IEnumerable GetSimilaritems(BaseItem item, ILibraryManager libraryManager, IEnumerable inputItems, Func, List, BaseItem, int> getSimilarityScore) { var itemId = item.Id; inputItems = inputItems.Where(i => i.Id != itemId); + var itemPeople = libraryManager.GetPeople(item); + var allPeople = libraryManager.GetPeople(new InternalPeopleQuery + { + AppearsInItemId = item.Id + }); - return inputItems.Select(i => new Tuple(i, getSimilarityScore(item, i))) + return inputItems.Select(i => new Tuple(i, getSimilarityScore(item, itemPeople, allPeople, i))) .Where(i => i.Item2 > 2) .OrderByDescending(i => i.Item2) .Select(i => i.Item1); @@ -146,9 +152,11 @@ namespace MediaBrowser.Api /// Gets the similiarity score. /// /// The item1. + /// The item1 people. + /// All people. /// The item2. /// System.Int32. - internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2) + internal static int GetSimiliarityScore(BaseItem item1, List item1People, List allPeople, BaseItem item2) { var points = 0; @@ -169,11 +177,13 @@ namespace MediaBrowser.Api // Find common studios points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3); - var item2PeopleNames = item2.People.Select(i => i.Name) + var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id) + .Select(i => i.Name) + .Where(i => !string.IsNullOrWhiteSpace(i)) .DistinctNames() .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - points += item1.People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => + points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => { if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Api/Social/SharingService.cs b/MediaBrowser.Api/Social/SharingService.cs new file mode 100644 index 0000000000..608008455d --- /dev/null +++ b/MediaBrowser.Api/Social/SharingService.cs @@ -0,0 +1,164 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Social; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Social; +using ServiceStack; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.Social +{ + [Route("/Social/Shares/{Id}", "GET", Summary = "Gets a share")] + [Authenticated] + public class GetSocialShareInfo : IReturn + { + [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Social/Shares/Public/{Id}", "GET", Summary = "Gets a share")] + public class GetPublicSocialShareInfo : IReturn + { + [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Social/Shares/Public/{Id}/Image", "GET", Summary = "Gets a share")] + public class GetShareImage + { + [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Social/Shares", "POST", Summary = "Creates a share")] + [Authenticated] + public class CreateShare : IReturn + { + [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string ItemId { get; set; } + + [ApiMember(Name = "UserId", Description = "The user id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string UserId { get; set; } + } + + [Route("/Social/Shares/{Id}", "DELETE", Summary = "Deletes a share")] + [Authenticated] + public class DeleteShare : IReturnVoid + { + [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public string Id { get; set; } + } + + [Route("/Social/Shares/Public/{Id}/Item", "GET", Summary = "Gets a share")] + public class GetSharedLibraryItem + { + [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + public class SharingService : BaseApiService + { + private readonly ISharingManager _sharingManager; + private readonly ILibraryManager _libraryManager; + private readonly IDlnaManager _dlnaManager; + private readonly IDtoService _dtoService; + + public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService) + { + _sharingManager = sharingManager; + _dlnaManager = dlnaManager; + _libraryManager = libraryManager; + _dtoService = dtoService; + } + + public object Get(GetSocialShareInfo request) + { + var info = _sharingManager.GetShareInfo(request.Id); + + return ToOptimizedResult(info); + } + + public object Get(GetSharedLibraryItem request) + { + var info = _sharingManager.GetShareInfo(request.Id); + + if (info.ExpirationDate <= DateTime.UtcNow) + { + throw new ResourceNotFoundException(); + } + + var item = _libraryManager.GetItemById(info.ItemId); + + var dto = _dtoService.GetBaseItemDto(item, new DtoOptions()); + + return ToOptimizedResult(dto); + } + + public object Get(GetPublicSocialShareInfo request) + { + var info = _sharingManager.GetShareInfo(request.Id); + + if (info.ExpirationDate <= DateTime.UtcNow) + { + throw new ResourceNotFoundException(); + } + + return ToOptimizedResult(info); + } + + public async Task Post(CreateShare request) + { + var info = await _sharingManager.CreateShare(request.ItemId, request.UserId).ConfigureAwait(false); + + return ToOptimizedResult(info); + } + + public void Delete(DeleteShare request) + { + var task = _sharingManager.DeleteShare(request.Id); + Task.WaitAll(task); + } + + public object Get(GetShareImage request) + { + var share = _sharingManager.GetShareInfo(request.Id); + + if (share == null) + { + throw new ResourceNotFoundException(); + } + if (share.ExpirationDate <= DateTime.UtcNow) + { + throw new ResourceNotFoundException(); + } + + var item = _libraryManager.GetItemById(share.ItemId); + + var image = item.GetImageInfo(ImageType.Primary, 0); + + if (image != null) + { + return ToStaticFileResult(image.Path); + } + + // Grab a dlna icon if nothing else is available + using (var response = _dlnaManager.GetIcon("logo240.jpg")) + { + using (var ms = new MemoryStream()) + { + response.Stream.CopyTo(ms); + + ms.Position = 0; + var bytes = ms.ToArray(); + return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower()); + } + } + + } + } +} diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index bc2e4699ce..7120f3604e 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -990,7 +990,7 @@ namespace MediaBrowser.Api.UserLibrary .Select(p => p == null ? "-1" : p.Name) .ToList(); - if (!(names.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + if (!(names.Any(v => libraryManager.GetPeople(i).Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) { return false; } @@ -1003,7 +1003,7 @@ namespace MediaBrowser.Api.UserLibrary if (personTypes.Length == 0) { - if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) + if (!(libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) { return false; } @@ -1013,8 +1013,7 @@ namespace MediaBrowser.Api.UserLibrary var types = personTypes; var ok = new[] { i }.Any(item => - item.People != null && - item.People.Any(p => + libraryManager.GetPeople(item).Any(p => p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); if (!ok) diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index b7fb4f542d..bd9898dcdf 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using ServiceStack; -using System; using System.Collections.Generic; using System.Linq; @@ -151,18 +150,16 @@ namespace MediaBrowser.Api.UserLibrary /// The items list. /// The person types. /// IEnumerable{PersonInfo}. - private IEnumerable GetAllPeople(IEnumerable itemsList, string[] personTypes) + private IEnumerable GetAllPeople(IEnumerable itemsList, IEnumerable personTypes) { - var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type)); + var allIds = itemsList.Select(i => i.Id).ToList(); - if (personTypes.Length > 0) + var allPeople = LibraryManager.GetPeople(new InternalPeopleQuery { - people = people.Where(p => - personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) || - personTypes.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase)); - } + PersonTypes = personTypes.ToList() + }); - return people; + return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type); } } } diff --git a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs index 77d9f80f9f..6987928026 100644 --- a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs +++ b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs @@ -85,6 +85,13 @@ namespace MediaBrowser.Common.Implementations.Logging { rule.EnableLoggingForLevel(level); } + foreach (var lev in rule.Levels.ToArray()) + { + if (lev < level) + { + rule.DisableLoggingForLevel(lev); + } + } } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 4185590ab3..f6d1d32a4c 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -216,5 +216,14 @@ namespace MediaBrowser.Controller.Entities.Audio return hasArtist != null && hasArtist.HasAnyArtist(Name); }; } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 971c092361..5f24dbf672 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -71,5 +71,14 @@ namespace MediaBrowser.Controller.Entities.Audio { return i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 014b3ae6a4..41329608e4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -35,7 +35,6 @@ namespace MediaBrowser.Controller.Entities { Genres = new List(); Studios = new List(); - People = new List(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); LockedFields = new List(); ImageInfos = new List(); @@ -413,15 +412,6 @@ namespace MediaBrowser.Controller.Entities } } - public bool ContainsPerson(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - return People.Any(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); - } - public string GetInternalMetadataPath() { var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath; @@ -474,6 +464,8 @@ namespace MediaBrowser.Controller.Entities return sortable; } + public Guid ParentId { get; set; } + /// /// Gets or sets the parent. /// @@ -481,6 +473,12 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public Folder Parent { get; set; } + public void SetParent(Folder parent) + { + Parent = parent; + ParentId = parent == null ? Guid.Empty : parent.Id; + } + [IgnoreDataMember] public IEnumerable Parents { @@ -785,6 +783,12 @@ namespace MediaBrowser.Controller.Entities get { return IsFolder || Parent != null; } } + [IgnoreDataMember] + public virtual bool SupportsPeople + { + get { return true; } + } + /// /// Refreshes owned items such as trailers, theme videos, special features, etc. /// Returns true or false indicating if changes were found. @@ -1248,83 +1252,6 @@ namespace MediaBrowser.Controller.Entities /// public void AddPerson(PersonInfo person) { - if (person == null) - { - throw new ArgumentNullException("person"); - } - - if (string.IsNullOrWhiteSpace(person.Name)) - { - throw new ArgumentNullException(); - } - - // Normalize - if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) - { - person.Type = PersonType.GuestStar; - } - else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) - { - person.Type = PersonType.Director; - } - else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase)) - { - person.Type = PersonType.Producer; - } - else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) - { - person.Type = PersonType.Writer; - } - - // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes - if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) - { - var existing = People.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase)); - - if (existing != null) - { - existing.Type = PersonType.GuestStar; - existing.SortOrder = person.SortOrder ?? existing.SortOrder; - return; - } - } - - if (string.Equals(person.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) - { - // If the actor already exists without a role and we have one, fill it in - var existing = People.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase) || p.Type.Equals(PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))); - if (existing == null) - { - // Wasn't there - add it - People.Add(person); - } - else - { - // Was there, if no role and we have one - fill it in - if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role)) - { - existing.Role = person.Role; - } - - existing.SortOrder = person.SortOrder ?? existing.SortOrder; - } - } - else - { - var existing = People.FirstOrDefault(p => - string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && - string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)); - - // Check for dupes based on the combination of Name and Type - if (existing == null) - { - People.Add(person); - } - else - { - existing.SortOrder = person.SortOrder ?? existing.SortOrder; - } - } } /// diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 1cc5477904..5a1ad6b157 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -1,4 +1,6 @@  +using System.Runtime.Serialization; + namespace MediaBrowser.Controller.Entities { /// @@ -21,5 +23,14 @@ namespace MediaBrowser.Controller.Entities { return true; } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index bcf68263ae..3a610be641 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -194,5 +194,14 @@ namespace MediaBrowser.Controller.Entities .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)) .SelectMany(c => c.Children); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 821e6b5caf..22efb09e15 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.Controller.Entities /// Unable to add + item.Name public async Task AddChild(BaseItem item, CancellationToken cancellationToken) { - item.Parent = this; + item.SetParent(this); if (item.Id == Guid.Empty) { @@ -230,7 +230,7 @@ namespace MediaBrowser.Controller.Entities { RemoveChildrenInternal(new[] { item }); - item.Parent = null; + item.SetParent(null); return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken); } @@ -783,11 +783,11 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.GetOrAddByReferenceItem(item); } - item.Parent = this; + item.SetParent(this); } else { - child.Parent = this; + child.SetParent(this); LibraryManager.RegisterItem(child); item = child; } diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index c91acbe3f1..bf448da0d5 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -62,5 +62,14 @@ namespace MediaBrowser.Controller.Entities { return i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index cf69167638..35f7e33501 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -58,5 +58,14 @@ namespace MediaBrowser.Controller.Entities return id; } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index cb68e5dae9..233e1e0fd1 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -66,5 +66,14 @@ namespace MediaBrowser.Controller.Entities { return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 3643c58b3e..158bcb6d19 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -59,5 +59,11 @@ namespace MediaBrowser.Controller.Entities /// Afters the metadata refresh. /// void AfterMetadataRefresh(); + + /// + /// Gets a value indicating whether [supports people]. + /// + /// true if [supports people]; otherwise, false. + bool SupportsPeople { get; } } } diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs index 14b69b8fde..e6667290c1 100644 --- a/MediaBrowser.Controller/Entities/IItemByName.cs +++ b/MediaBrowser.Controller/Entities/IItemByName.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Controller.Entities /// /// Marker interface /// - public interface IItemByName + public interface IItemByName : IHasMetadata { /// /// Gets the tagged items. diff --git a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs new file mode 100644 index 0000000000..05d23d9866 --- /dev/null +++ b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Entities +{ + public class InternalPeopleQuery + { + public Guid ItemId { get; set; } + public List PersonTypes { get; set; } + public List ExcludePersonTypes { get; set; } + public int? MaxListOrder { get; set; } + public Guid AppearsInItemId { get; set; } + public string NameContains { get; set; } + + public InternalPeopleQuery() + { + PersonTypes = new List(); + ExcludePersonTypes = new List(); + } + } +} diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs new file mode 100644 index 0000000000..3468ca2d58 --- /dev/null +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -0,0 +1,100 @@ +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Controller.Entities +{ + public static class PeopleHelper + { + public static void AddPerson(List people, PersonInfo person) + { + if (person == null) + { + throw new ArgumentNullException("person"); + } + + if (string.IsNullOrWhiteSpace(person.Name)) + { + throw new ArgumentNullException(); + } + + // Normalize + if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + { + person.Type = PersonType.GuestStar; + } + else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) + { + person.Type = PersonType.Director; + } + else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase)) + { + person.Type = PersonType.Producer; + } + else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) + { + person.Type = PersonType.Writer; + } + + // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes + if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + { + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase)); + + if (existing != null) + { + existing.Type = PersonType.GuestStar; + existing.SortOrder = person.SortOrder ?? existing.SortOrder; + return; + } + } + + if (string.Equals(person.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) + { + // If the actor already exists without a role and we have one, fill it in + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase) || p.Type.Equals(PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))); + if (existing == null) + { + // Wasn't there - add it + people.Add(person); + } + else + { + // Was there, if no role and we have one - fill it in + if (string.IsNullOrWhiteSpace(existing.Role) && !string.IsNullOrWhiteSpace(person.Role)) + { + existing.Role = person.Role; + } + + existing.SortOrder = person.SortOrder ?? existing.SortOrder; + } + } + else + { + var existing = people.FirstOrDefault(p => + string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && + string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)); + + // Check for dupes based on the combination of Name and Type + if (existing == null) + { + people.Add(person); + } + else + { + existing.SortOrder = person.SortOrder ?? existing.SortOrder; + } + } + } + + public static bool ContainsPerson(List people, string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + return people.Any(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index ef24d43470..0a62655eef 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -70,13 +70,27 @@ namespace MediaBrowser.Controller.Entities public IEnumerable GetTaggedItems(IEnumerable inputItems) { - return inputItems.Where(GetItemFilter()); + var itemsWithPerson = LibraryManager.GetItemIds(new InternalItemsQuery + { + Person = Name + }); + + return inputItems.Where(i => itemsWithPerson.Contains(i.Id)); } public Func GetItemFilter() { - return i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)); + return i => LibraryManager.GetPeople(i).Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)); + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } } } @@ -85,6 +99,8 @@ namespace MediaBrowser.Controller.Entities /// public class PersonInfo { + public Guid ItemId { get; set; } + /// /// Gets or sets the name. /// diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index b8d359369b..822f305ede 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -72,5 +72,14 @@ namespace MediaBrowser.Controller.Entities { return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 5b70ee5f4e..71e3d1ce03 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -295,5 +295,14 @@ namespace MediaBrowser.Controller.Entities return config.GroupedFolders.Select(i => new Guid(i)).Contains(id); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 9be30273a0..dad6de01a8 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Playlists; +using System.Runtime.Serialization; +using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; @@ -93,5 +94,14 @@ namespace MediaBrowser.Controller.Entities return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 3e4dff0337..62c71d1690 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1699,8 +1699,7 @@ namespace MediaBrowser.Controller.Entities .Select(i => i == null ? "-1" : i.Name) .ToList(); - if (!(names.Any( - v => item.People.Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + if (!(names.Any(v => libraryManager.GetPeople(item).Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) { return false; } @@ -1713,7 +1712,7 @@ namespace MediaBrowser.Controller.Entities if (personTypes.Length == 0) { - if (!(item.People.Any(p => string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase)))) + if (!(libraryManager.GetPeople(item).Any(p => string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase)))) { return false; } @@ -1723,8 +1722,7 @@ namespace MediaBrowser.Controller.Entities var types = personTypes; var ok = new[] { item }.Any(i => - i.People != null && - i.People.Any(p => + libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase)))); if (!ok) diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index a1a1523873..163dcd667c 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -88,5 +88,14 @@ namespace MediaBrowser.Controller.Entities var val = GetYearValue(); return i => i.ProductionYear.HasValue && val.HasValue && i.ProductionYear.Value == val.Value; } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index aa8799fa6e..f0bfaaf667 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -412,5 +412,54 @@ namespace MediaBrowser.Controller.Library /// The item. /// IEnumerable<Folder>. IEnumerable GetCollectionFolders(BaseItem item); + + /// + /// Gets the people. + /// + /// The item. + /// List<PersonInfo>. + List GetPeople(BaseItem item); + + /// + /// Gets the people. + /// + /// The query. + /// List<PersonInfo>. + List GetPeople(InternalPeopleQuery query); + + /// + /// Gets the people items. + /// + /// The query. + /// List<Person>. + List GetPeopleItems(InternalPeopleQuery query); + + /// + /// Gets all people names. + /// + /// List<System.String>. + List GetAllPeople(); + + /// + /// Updates the people. + /// + /// The item. + /// The people. + /// Task. + Task UpdatePeople(BaseItem item, List people); + + /// + /// Gets the item ids. + /// + /// The query. + /// List<Guid>. + List GetItemIds(InternalItemsQuery query); + + /// + /// Gets the people names. + /// + /// The query. + /// List<System.String>. + List GetPeopleNames(InternalPeopleQuery query); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 3da12cd806..2179c5ecd7 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -118,6 +118,11 @@ namespace MediaBrowser.Controller.LiveTv return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N")); } + public override bool CanDelete() + { + return true; + } + public override bool IsAuthorizedToDelete(User user) { return user.Policy.EnableLiveTvManagement; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 179c33d09f..aaaff6bdb9 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -116,6 +116,11 @@ namespace MediaBrowser.Controller.LiveTv return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N")); } + public override bool CanDelete() + { + return true; + } + public override bool IsAuthorizedToDelete(User user) { return user.Policy.EnableLiveTvManagement; diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index bf86c049f2..fcde6d8c0e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -166,6 +166,7 @@ + @@ -173,6 +174,7 @@ + @@ -328,6 +330,7 @@ + diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 7c02a0ea1e..a4b9bf1202 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -147,6 +147,28 @@ namespace MediaBrowser.Controller.Persistence /// The query. /// List<Guid>. List GetItemIdsList(InternalItemsQuery query); + + /// + /// Gets the people. + /// + /// The query. + /// List<PersonInfo>. + List GetPeople(InternalPeopleQuery query); + + /// + /// Updates the people. + /// + /// The item identifier. + /// The people. + /// Task. + Task UpdatePeople(Guid itemId, List people); + + /// + /// Gets the people names. + /// + /// The query. + /// List<System.String>. + List GetPeopleNames(InternalPeopleQuery query); } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index a8a3e88abb..c1a4fa765a 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers /// The metadata file. /// The cancellation token. /// - public void Fetch(T item, string metadataFile, CancellationToken cancellationToken) + public void Fetch(MetadataResult item, string metadataFile, CancellationToken cancellationToken) { if (item == null) { @@ -72,7 +72,7 @@ namespace MediaBrowser.Controller.Providers /// The settings. /// The encoding. /// The cancellation token. - private void Fetch(T item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken) + private void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken) { using (var streamReader = new StreamReader(metadataFile, encoding)) { @@ -101,9 +101,11 @@ namespace MediaBrowser.Controller.Providers /// Fetches metadata from one Xml Element /// /// The reader. - /// The item. - protected virtual void FetchDataFromXmlNode(XmlReader reader, T item) + /// The item result. + protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { + var item = itemResult.Item; + switch (reader.Name) { // DateCreated @@ -490,7 +492,7 @@ namespace MediaBrowser.Controller.Providers { continue; } - item.AddPerson(p); + PeopleHelper.AddPerson(itemResult.People, p); } break; } @@ -502,7 +504,7 @@ namespace MediaBrowser.Controller.Providers { continue; } - item.AddPerson(p); + PeopleHelper.AddPerson(itemResult.People, p); } break; } @@ -516,7 +518,7 @@ namespace MediaBrowser.Controller.Providers { // This is one of the mis-named "Actors" full nodes created by MB2 // Create a reader and pass it to the persons node processor - FetchDataFromPersonsNode(new XmlTextReader(new StringReader("" + actors + "")), item); + FetchDataFromPersonsNode(new XmlTextReader(new StringReader("" + actors + "")), itemResult); } else { @@ -527,7 +529,7 @@ namespace MediaBrowser.Controller.Providers { continue; } - item.AddPerson(p); + PeopleHelper.AddPerson(itemResult.People, p); } } break; @@ -541,7 +543,7 @@ namespace MediaBrowser.Controller.Providers { continue; } - item.AddPerson(p); + PeopleHelper.AddPerson(itemResult.People, p); } break; } @@ -833,7 +835,7 @@ namespace MediaBrowser.Controller.Providers { using (var subtree = reader.ReadSubtree()) { - FetchDataFromPersonsNode(subtree, item); + FetchDataFromPersonsNode(subtree, itemResult); } break; } @@ -1133,7 +1135,7 @@ namespace MediaBrowser.Controller.Providers /// /// The reader. /// The item. - private void FetchDataFromPersonsNode(XmlReader reader, T item) + private void FetchDataFromPersonsNode(XmlReader reader, MetadataResult item) { reader.MoveToContent(); @@ -1154,7 +1156,7 @@ namespace MediaBrowser.Controller.Providers { continue; } - item.AddPerson(person); + PeopleHelper.AddPerson(item.People, person); } } break; diff --git a/MediaBrowser.Controller/Providers/LocalMetadataResult.cs b/MediaBrowser.Controller/Providers/LocalMetadataResult.cs index 8be3ee7aac..76b7a31360 100644 --- a/MediaBrowser.Controller/Providers/LocalMetadataResult.cs +++ b/MediaBrowser.Controller/Providers/LocalMetadataResult.cs @@ -1,23 +1,17 @@ -using System.Collections.Generic; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; +using System.Collections.Generic; namespace MediaBrowser.Controller.Providers { - public class LocalMetadataResult + public class LocalMetadataResult : MetadataResult where T : IHasMetadata { - public bool HasMetadata { get; set; } - public T Item { get; set; } - public List Images { get; set; } - public List Chapters { get; set; } public List UserDataLIst { get; set; } public LocalMetadataResult() { Images = new List(); - Chapters = new List(); UserDataLIst = new List(); } } diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 756458cfaf..a18dd83e83 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -1,8 +1,18 @@ +using MediaBrowser.Controller.Entities; +using System.Collections.Generic; + namespace MediaBrowser.Controller.Providers { public class MetadataResult { + public List People { get; set; } + public bool HasMetadata { get; set; } public T Item { get; set; } + + public MetadataResult() + { + People = new List(); + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Social/ISharingManager.cs b/MediaBrowser.Controller/Social/ISharingManager.cs new file mode 100644 index 0000000000..ded37771af --- /dev/null +++ b/MediaBrowser.Controller/Social/ISharingManager.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Model.Social; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Social +{ + public interface ISharingManager + { + /// + /// Creates the share. + /// + /// The item identifier. + /// The user identifier. + /// Task<SocialShareInfo>. + Task CreateShare(string itemId, string userId); + /// + /// Gets the share information. + /// + /// The identifier. + /// SocialShareInfo. + SocialShareInfo GetShareInfo(string id); + /// + /// Deletes the share. + /// + /// The identifier. + /// Task. + Task DeleteShare(string id); + } +} diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 2635a4cbf9..860c736ead 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -49,4 +49,18 @@ namespace MediaBrowser.Controller.Sync /// Task<QueryResult<FileMetadata>>. Task> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken); } + + public interface ISupportsDirectCopy + { + /// + /// Sends the file. + /// + /// The path. + /// The path parts. + /// The target. + /// The progress. + /// The cancellation token. + /// Task<SyncedFileInfo>. + Task SendFile(string path, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 246c234625..72040c8aee 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.ContentDirectory _profile = profile; _config = config; - _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger); + _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager); } protected override IEnumerable> GetResult(string methodName, Headers methodParams) @@ -410,18 +410,11 @@ namespace MediaBrowser.Dlna.ContentDirectory { if (stubType.Value == StubType.People) { - var items = item.People.Select(i => + var items = _libraryManager.GetPeopleItems(new InternalPeopleQuery { - try - { - return _libraryManager.GetPerson(i.Name); - } - catch - { - return null; - } + ItemId = item.Id - }).Where(i => i != null).ToArray(); + }).ToArray(); var result = new QueryResult { @@ -443,7 +436,7 @@ namespace MediaBrowser.Dlna.ContentDirectory var person = item as Person; if (person != null) { - return await GetItemsFromPerson(person, user, startIndex, limit).ConfigureAwait(false); + return GetItemsFromPerson(person, user, startIndex, limit); } return ApplyPaging(new QueryResult(), startIndex, limit); @@ -486,38 +479,19 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private async Task> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit) + private QueryResult GetItemsFromPerson(Person person, User user, int? startIndex, int? limit) { - var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Series && i.ContainsPerson(person.Name)) - .ToList(); - - var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery + var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery { - ContentTypes = new[] { ChannelMediaContentType.MovieExtra }, - ExtraTypes = new[] { ExtraType.Trailer }, - UserId = user.Id.ToString("N") + Person = person.Name - }, CancellationToken.None).ConfigureAwait(false); + }).Items; - var currentIds = items.Select(i => i.GetProviderId(MetadataProviders.Imdb)) + var items = itemsWithPerson + .Where(i => i is Movie || i is Series || i is IChannelItem) + .Where(i => i.IsVisibleStandalone(user)) .ToList(); - var trailersToAdd = trailerResult.Items - .Where(i => i.ContainsPerson(person.Name)) - .Where(i => - { - // Try to filter out dupes using imdb id - var imdb = i.GetProviderId(MetadataProviders.Imdb); - if (!string.IsNullOrWhiteSpace(imdb) && - currentIds.Contains(imdb, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - return true; - }); - - items.AddRange(trailersToAdd); - items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) .Skip(startIndex ?? 0) .Take(limit ?? int.MaxValue) @@ -569,7 +543,11 @@ namespace MediaBrowser.Dlna.ContentDirectory private bool EnablePeopleDisplay(BaseItem item) { - if (item.People.Count > 0) + if (_libraryManager.GetPeopleNames(new InternalPeopleQuery + { + ItemId = item.Id + + }).Count > 0) { return item is Movie; } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 08f7707355..50a6f3ba61 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -40,8 +40,9 @@ namespace MediaBrowser.Dlna.Didl private readonly ILocalizationManager _localization; private readonly IMediaSourceManager _mediaSourceManager; private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; - public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger) + public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager) { _profile = profile; _imageProcessor = imageProcessor; @@ -50,6 +51,7 @@ namespace MediaBrowser.Dlna.Didl _localization = localization; _mediaSourceManager = mediaSourceManager; _logger = logger; + _libraryManager = libraryManager; _accessToken = accessToken; _user = user; } @@ -654,7 +656,9 @@ namespace MediaBrowser.Dlna.Didl { var types = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer, PersonType.Composer, "Creator" }; - foreach (var actor in item.People) + var people = _libraryManager.GetPeople(item); + + foreach (var actor in people) { var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) ?? PersonType.Actor; diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 2d2525919e..9ce62034b4 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -525,6 +525,7 @@ namespace MediaBrowser.Dlna new Xbox360Profile(), new XboxOneProfile(), new SonyPs3Profile(), + new SonyPs4Profile(), new SonyBravia2010Profile(), new SonyBravia2011Profile(), new SonyBravia2012Profile(), diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 204872e7b6..06aaff734e 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -82,6 +82,7 @@ + @@ -210,6 +211,9 @@ + + +