diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 8aba4263c..ab53f7550 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -69,7 +69,6 @@ - diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index 78633472b..6ff40d1cf 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -1,11 +1,13 @@ -using System.Linq; +using System.Threading.Tasks; using ImageMagickSharp; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Logging; using System; using System.IO; +using System.Linq; namespace Emby.Drawing.ImageMagick { @@ -13,11 +15,13 @@ namespace Emby.Drawing.ImageMagick { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; + private readonly IHttpClient _httpClient; - public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths) + public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient) { _logger = logger; _appPaths = appPaths; + _httpClient = httpClient; LogImageMagickVersion(); } @@ -177,7 +181,8 @@ namespace Emby.Drawing.ImageMagick { var currentImageSize = new ImageSize(imageWidth, imageHeight); - new PlayedIndicatorDrawer(_appPaths).DrawPlayedIndicator(wand, currentImageSize); + var task = new PlayedIndicatorDrawer(_appPaths, _httpClient).DrawPlayedIndicator(wand, currentImageSize); + Task.WaitAll(task); } else if (options.UnplayedCount.HasValue) { diff --git a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs b/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs index 5eeb15771..1c751de1f 100644 --- a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs +++ b/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs @@ -1,8 +1,10 @@ using ImageMagickSharp; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Model.Drawing; using System; using System.IO; +using System.Threading.Tasks; namespace Emby.Drawing.ImageMagick { @@ -12,13 +14,15 @@ namespace Emby.Drawing.ImageMagick private const int OffsetFromTopRightCorner = 38; private readonly IApplicationPaths _appPaths; + private readonly IHttpClient _iHttpClient; - public PlayedIndicatorDrawer(IApplicationPaths appPaths) + public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient) { _appPaths = appPaths; + _iHttpClient = iHttpClient; } - public void DrawPlayedIndicator(MagickWand wand, ImageSize imageSize) + public async Task DrawPlayedIndicator(MagickWand wand, ImageSize imageSize) { var x = imageSize.Width - OffsetFromTopRightCorner; @@ -34,7 +38,7 @@ namespace Emby.Drawing.ImageMagick pixel.Opacity = 0; pixel.Color = "white"; draw.FillColor = pixel; - draw.Font = ExtractFont("webdings.ttf", _appPaths); + draw.Font = await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf", _appPaths, _iHttpClient).ConfigureAwait(false); draw.FontSize = FontSize; draw.FontStyle = FontStyleType.NormalStyle; draw.TextAlignment = TextAlignType.CenterAlign; @@ -77,7 +81,37 @@ namespace Emby.Drawing.ImageMagick } catch (IOException) { - + + } + + return tempPath; + } + + internal static async Task DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient) + { + var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name); + + if (File.Exists(filePath)) + { + return filePath; + } + + var tempPath = await httpClient.GetTempFile(new HttpRequestOptions + { + Url = url, + Progress = new Progress() + + }).ConfigureAwait(false); + + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + + try + { + File.Copy(tempPath, filePath, false); + } + catch (IOException) + { + } return tempPath; diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index d4b5be584..564cfa93a 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,9 +344,7 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder - .GetRecursiveChildren() - .SelectMany(i => i.People) + return libraryManager.GetAllPeople() .Select(i => i.Name) .DistinctNames() .FirstOrDefault(i => @@ -364,7 +362,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 8f7edabbb..b8ae9392a 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/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 513bde871..17eb448bc 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 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(); @@ -363,7 +363,7 @@ namespace MediaBrowser.Api.Movies foreach (var name in names) { var items = allMovies - .Where(i => i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))) + .Where(i => _libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))) .Take(itemLimit) .ToList(); @@ -387,7 +387,7 @@ namespace MediaBrowser.Api.Movies foreach (var item in baselineItems) { var similar = SimilarItemsHelper - .GetSimilaritems(item, allMovies, SimilarItemsHelper.GetSimiliarityScore) + .GetSimilaritems(item, _libraryManager, allMovies, SimilarItemsHelper.GetSimiliarityScore) .Take(itemLimit) .ToList(); @@ -408,7 +408,7 @@ namespace MediaBrowser.Api.Movies { // 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)) + .SelectMany(i => _libraryManager.GetPeople(i).Where(p => !string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)).Take(2)) .Select(i => i.Name) .DistinctNames(); } @@ -416,7 +416,7 @@ namespace MediaBrowser.Api.Movies private IEnumerable GetDirectors(IEnumerable items) { return items - .Select(i => i.People.FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase))) + .Select(i => _libraryManager.GetPeople(i).FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase))) .Where(i => i != null) .Select(i => i.Name) .DistinctNames(); diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index 37f79bf20..3dc459bd2 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -69,10 +69,11 @@ namespace MediaBrowser.Api.Music /// /// The item1. /// The item2. + /// The library manager. /// System.Int32. - private int GetAlbumSimilarityScore(BaseItem item1, BaseItem item2) + private int GetAlbumSimilarityScore(BaseItem item1, BaseItem item2, ILibraryManager libraryManager) { - var points = SimilarItemsHelper.GetSimiliarityScore(item1, item2); + var points = SimilarItemsHelper.GetSimiliarityScore(item1, item2, libraryManager); var album1 = (MusicAlbum)item1; var album2 = (MusicAlbum)item2; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 71ef689a7..41d785a34 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -772,6 +772,11 @@ namespace MediaBrowser.Api.Playback ? null : audioStream.Channels; + if (inputChannels <= 0) + { + inputChannels = null; + } + var codec = outputAudioCodec ?? string.Empty; if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index ccbf182fe..ab57e561f 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -294,6 +294,10 @@ namespace MediaBrowser.Api.Playback.Hls } } } + } + catch (DirectoryNotFoundException) + { + } catch (FileNotFoundException) { diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 2c0728719..ebf5183c5 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -24,1219 +24,1139 @@ using System.Text; namespace MediaBrowser.Api.Reports { - /// The reports service. - /// - public class ReportsService : BaseApiService - { - #region [Constructors] + /// The reports service. + /// + public class ReportsService : BaseApiService + { - /// - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. - /// Manager for user. - /// Manager for library. - /// The localization. - /// Manager for activity. - public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IActivityManager activityManager, IActivityRepository repo) - { - _userManager = userManager; - _libraryManager = libraryManager; - _localization = localization; - _activityManager = activityManager; - _repo = repo; - } - #endregion + /// Manager for user. + private readonly IUserManager _userManager; - #region [Private Fields] + /// Manager for library. + private readonly ILibraryManager _libraryManager; + /// The localization. + private readonly ILocalizationManager _localization; - private readonly IActivityManager _activityManager; ///< Manager for activity + /// + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportsService class. + /// Manager for user. + /// Manager for library. + /// The localization. + public ReportsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization) + { + _userManager = userManager; + _libraryManager = libraryManager; + _localization = localization; + } - /// Manager for library. - private readonly ILibraryManager _libraryManager; ///< Manager for library - /// The localization. + /// Gets the given request. + /// The request. + /// A Task<object> + public async Task Get(GetReportHeaders request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; - private readonly ILocalizationManager _localization; ///< The localization + ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); + var reportResult = reportBuilder.GetReportHeaders(reportRowType, request); - private readonly IActivityRepository _repo; + return ToOptimizedResult(reportResult); - /// Manager for user. - private readonly IUserManager _userManager; ///< Manager for user + } - #endregion + /// Gets the given request. + /// The request. + /// A Task<object> + public async Task Get(GetItemReport request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; - #region [Public Methods] + var reportResult = await GetReportResult(request); - /// Gets the given request. - /// The request. - /// A Task<object> - public async Task Get(GetActivityLogs request) - { - ReportResult result = await GetReportActivities(request).ConfigureAwait(false); - return ToOptimizedResult(result); - } + return ToOptimizedResult(reportResult); + } - /// Gets the given request. - /// The request. - /// A Task<object> - public async Task Get(GetReportHeaders request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; + /// Gets the given request. + /// The request. + /// A Task<object> + public async Task Get(GetReportDownload request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; - ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView); - ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + var headers = new Dictionary(); + string fileExtension = "csv"; + string contentType = "text/plain;charset='utf-8'"; - List result = new List(); - switch (reportViewType) - { - case ReportViewType.ReportData: - ReportBuilder dataBuilder = new ReportBuilder(_libraryManager); - result = dataBuilder.GetHeaders(request); - break; - case ReportViewType.ReportStatistics: - break; - case ReportViewType.ReportActivities: - ReportActivitiesBuilder activityBuilder = new ReportActivitiesBuilder(_libraryManager, _userManager); - result = activityBuilder.GetHeaders(request); - break; - } + switch (request.ExportType) + { + case ReportExportType.CSV: + break; + case ReportExportType.Excel: + contentType = "application/vnd.ms-excel"; + fileExtension = "xls"; + break; + } - return ToOptimizedResult(result); + var filename = "ReportExport." + fileExtension; + headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename); + headers["Content-Encoding"] = "UTF-8"; - } + ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); + QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); + ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request); - /// Gets the given request. - /// The request. - /// A Task<object> - public async Task Get(GetItemReport request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; + reportResult.TotalRecordCount = queryResult.TotalRecordCount; - var reportResult = await GetReportResult(request); + string result = string.Empty; + switch (request.ExportType) + { + case ReportExportType.CSV: + result = new ReportExport().ExportToCsv(reportResult); + break; + case ReportExportType.Excel: + result = new ReportExport().ExportToExcel(reportResult); + break; + } - return ToOptimizedResult(reportResult); - } + object ro = ResultFactory.GetResult(result, contentType, headers); + return ro; + } - /// Gets the given request. - /// The request. - /// A Task<object> - public async Task Get(GetReportStatistics request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; - var reportResult = await GetReportStatistic(request); + /// Gets the given request. + /// The request. + /// A Task<object> + public async Task Get(GetReportStatistics request) + { + if (string.IsNullOrEmpty(request.IncludeItemTypes)) + return null; + var reportResult = await GetReportStatistic(request); - return ToOptimizedResult(reportResult); - } + return ToOptimizedResult(reportResult); + } - /// Gets the given request. - /// The request. - /// A Task<object> - public async Task Get(GetReportDownload request) - { - if (string.IsNullOrEmpty(request.IncludeItemTypes)) - return null; + /// Gets report statistic. + /// The request. + /// The report statistic. + private async Task GetReportStatistic(GetReportStatistics request) + { + ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); - ReportViewType reportViewType = ReportHelper.GetReportViewType(request.ReportView); - var headers = new Dictionary(); - string fileExtension = "csv"; - string contentType = "text/plain;charset='utf-8'"; + ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager); + ReportStatResult reportResult = reportBuilder.GetReportStatResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5); + reportResult.TotalRecordCount = reportResult.Groups.Count(); + return reportResult; + } - switch (request.ExportType) - { - case ReportExportType.CSV: - break; - case ReportExportType.Excel: - contentType = "application/vnd.ms-excel"; - fileExtension = "xls"; - break; - } + /// Gets report result. + /// The request. + /// The report result. + private async Task GetReportResult(GetItemReport request) + { - var filename = "ReportExport." + fileExtension; - headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", filename); - headers["Content-Encoding"] = "UTF-8"; + ReportViewType reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); + ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); + QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); + ReportResult reportResult = reportBuilder.GetReportResult(queryResult.Items, reportRowType, request); + reportResult.TotalRecordCount = queryResult.TotalRecordCount; - ReportResult result = null; - switch (reportViewType) - { - case ReportViewType.ReportStatistics: - case ReportViewType.ReportData: - ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - ReportBuilder dataBuilder = new ReportBuilder(_libraryManager); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); - result = dataBuilder.GetResult(queryResult.Items, request); - result.TotalRecordCount = queryResult.TotalRecordCount; - break; - case ReportViewType.ReportActivities: - result = await GetReportActivities(request).ConfigureAwait(false); - break; - } + return reportResult; + } - string returnResult = string.Empty; - switch (request.ExportType) - { - case ReportExportType.CSV: - returnResult = new ReportExport().ExportToCsv(result); - break; - case ReportExportType.Excel: - returnResult = new ReportExport().ExportToExcel(result); - break; - } + /// Gets query result. + /// The request. + /// The query result. + private async Task> GetQueryResult(BaseReportRequest request) + { + // Placeholder in case needed later + request.Recursive = true; + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts"; - object ro = ResultFactory.GetResult(returnResult, contentType, headers); - return ro; - } + var parentItem = string.IsNullOrEmpty(request.ParentId) ? + (user == null ? _libraryManager.RootFolder : user.RootFolder) : + _libraryManager.GetItemById(request.ParentId); - #endregion + var item = string.IsNullOrEmpty(request.ParentId) ? + user == null ? _libraryManager.RootFolder : user.RootFolder : + parentItem; - #region [Internal Methods] + IEnumerable items; - /// Applies filtering. - /// The items. - /// The filter. - /// The user. - /// The repository. - /// IEnumerable{BaseItem}. - internal static IEnumerable ApplyFilter(IEnumerable items, ItemFilter filter, User user, IUserDataManager repository) - { - // Avoid implicitly captured closure - var currentUser = user; + if (request.Recursive) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); + return result; + } + else + { + if (user == null) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); + return result; + } - switch (filter) - { - case ItemFilter.IsFavoriteOrLikes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + var userRoot = item as UserRootFolder; - if (userdata == null) - { - return false; - } + if (userRoot == null) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - var likes = userdata.Likes ?? false; - var favorite = userdata.IsFavorite; + return result; + } - return likes || favorite; - }); + items = ((Folder)item).GetChildren(user, true); + } - case ItemFilter.Likes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + return new QueryResult { Items = items.ToArray() }; - return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; - }); + } - case ItemFilter.Dislikes: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + /// Gets items query. + /// The request. + /// The user. + /// The items query. + private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) + { + var query = new InternalItemsQuery + { + User = user, + IsPlayed = request.IsPlayed, + MediaTypes = request.GetMediaTypes(), + IncludeItemTypes = request.GetIncludeItemTypes(), + ExcludeItemTypes = request.GetExcludeItemTypes(), + Recursive = true, + SortBy = request.GetOrderBy(), + SortOrder = request.SortOrder ?? SortOrder.Ascending, - return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; - }); + Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager), + StartIndex = request.StartIndex, + IsMissing = request.IsMissing, + IsVirtualUnaired = request.IsVirtualUnaired, + IsUnaired = request.IsUnaired, + CollapseBoxSetItems = request.CollapseBoxSetItems, + NameLessThan = request.NameLessThan, + NameStartsWith = request.NameStartsWith, + NameStartsWithOrGreater = request.NameStartsWithOrGreater, + HasImdbId = request.HasImdbId, + IsYearMismatched = request.IsYearMismatched, + IsUnidentified = request.IsUnidentified, + IsPlaceHolder = request.IsPlaceHolder, + IsLocked = request.IsLocked, + IsInBoxSet = request.IsInBoxSet, + IsHD = request.IsHD, + Is3D = request.Is3D, + HasTvdbId = request.HasTvdbId, + HasTmdbId = request.HasTmdbId, + HasOverview = request.HasOverview, + HasOfficialRating = request.HasOfficialRating, + HasParentalRating = request.HasParentalRating, + HasSpecialFeature = request.HasSpecialFeature, + HasSubtitles = request.HasSubtitles, + HasThemeSong = request.HasThemeSong, + HasThemeVideo = request.HasThemeVideo, + HasTrailer = request.HasTrailer, + Tags = request.GetTags(), + OfficialRatings = request.GetOfficialRatings(), + Genres = request.GetGenres(), + Studios = request.GetStudios(), + StudioIds = request.GetStudioIds(), + Person = request.Person, + PersonIds = request.GetPersonIds(), + PersonTypes = request.GetPersonTypes(), + Years = request.GetYears(), + ImageTypes = request.GetImageTypes().ToArray(), + VideoTypes = request.GetVideoTypes().ToArray(), + AdjacentTo = request.AdjacentTo + }; - case ItemFilter.IsFavorite: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + if (!string.IsNullOrWhiteSpace(request.Ids)) + { + query.CollapseBoxSetItems = false; + } - return userdata != null && userdata.IsFavorite; - }); + foreach (var filter in request.GetFilters()) + { + switch (filter) + { + case ItemFilter.Dislikes: + query.IsLiked = false; + break; + case ItemFilter.IsFavorite: + query.IsFavorite = true; + break; + case ItemFilter.IsFavoriteOrLikes: + query.IsFavoriteOrLiked = true; + break; + case ItemFilter.IsFolder: + query.IsFolder = true; + break; + case ItemFilter.IsNotFolder: + query.IsFolder = false; + break; + case ItemFilter.IsPlayed: + query.IsPlayed = true; + break; + case ItemFilter.IsRecentlyAdded: + break; + case ItemFilter.IsResumable: + query.IsResumable = true; + break; + case ItemFilter.IsUnplayed: + query.IsPlayed = false; + break; + case ItemFilter.Likes: + query.IsLiked = true; + break; + } + } - case ItemFilter.IsResumable: - return items.Where(item => - { - var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + if (request.HasQueryLimit) + query.Limit = request.Limit; + return query; + } - return userdata != null && userdata.PlaybackPositionTicks > 0; - }); + /// Applies filtering. + /// The items. + /// The filter. + /// The user. + /// The repository. + /// IEnumerable{BaseItem}. + internal static IEnumerable ApplyFilter(IEnumerable items, ItemFilter filter, User user, IUserDataManager repository) + { + // Avoid implicitly captured closure + var currentUser = user; - case ItemFilter.IsPlayed: - return items.Where(item => item.IsPlayed(currentUser)); + switch (filter) + { + case ItemFilter.IsFavoriteOrLikes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - case ItemFilter.IsUnplayed: - return items.Where(item => item.IsUnplayed(currentUser)); + if (userdata == null) + { + return false; + } - case ItemFilter.IsFolder: - return items.Where(item => item.IsFolder); + var likes = userdata.Likes ?? false; + var favorite = userdata.IsFavorite; - case ItemFilter.IsNotFolder: - return items.Where(item => !item.IsFolder); + return likes || favorite; + }); - case ItemFilter.IsRecentlyAdded: - return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10); - } + case ItemFilter.Likes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - return items; - } + return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; + }); - #endregion + case ItemFilter.Dislikes: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); - #region [Private Methods] + return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; + }); - /// Applies the additional filters. - /// The request. - /// Zero-based index of the. - /// The user. - /// true if this object is pre filtered. - /// Manager for library. - /// true if it succeeds, false if it fails. + case ItemFilter.IsFavorite: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.IsFavorite; + }); + + case ItemFilter.IsResumable: + return items.Where(item => + { + var userdata = repository.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.PlaybackPositionTicks > 0; + }); + + case ItemFilter.IsPlayed: + return items.Where(item => item.IsPlayed(currentUser)); + + case ItemFilter.IsUnplayed: + return items.Where(item => item.IsUnplayed(currentUser)); + + case ItemFilter.IsFolder: + return items.Where(item => item.IsFolder); + + case ItemFilter.IsNotFolder: + return items.Where(item => !item.IsFolder); + + case ItemFilter.IsRecentlyAdded: + return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10); + } + + return items; + } + + /// Applies the additional filters. + /// The request. + /// Zero-based index of the. + /// The user. + /// true if this object is pre filtered. + /// Manager for library. + /// true if it succeeds, false if it fails. private bool ApplyAdditionalFilters(BaseReportRequest request, BaseItem i, User user, bool isPreFiltered, ILibraryManager libraryManager) - { - var video = i as Video; - - if (!isPreFiltered) - { - var mediaTypes = request.GetMediaTypes(); - if (mediaTypes.Length > 0) - { - if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - } - - if (request.IsPlayed.HasValue) - { - var val = request.IsPlayed.Value; - if (i.IsPlayed(user) != val) - { - return false; - } - } - - // Exclude item types - var excluteItemTypes = request.GetExcludeItemTypes(); - if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - // Include item types - var includeItemTypes = request.GetIncludeItemTypes(); - if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - if (request.IsInBoxSet.HasValue) - { - var val = request.IsInBoxSet.Value; - if (i.Parents.OfType().Any() != val) - { - return false; - } - } - - // Filter by Video3DFormat - if (request.Is3D.HasValue) - { - var val = request.Is3D.Value; - - if (video == null || val != video.Video3DFormat.HasValue) - { - return false; - } - } - - if (request.IsHD.HasValue) - { - var val = request.IsHD.Value; - - if (video == null || val != video.IsHD) - { - return false; - } - } - - if (request.IsUnidentified.HasValue) - { - var val = request.IsUnidentified.Value; - if (i.IsUnidentified != val) - { - return false; - } - } - - if (request.IsLocked.HasValue) - { - var val = request.IsLocked.Value; - if (i.IsLocked != val) - { - return false; - } - } - - if (request.HasOverview.HasValue) - { - var filterValue = request.HasOverview.Value; - - var hasValue = !string.IsNullOrEmpty(i.Overview); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasImdbId.HasValue) - { - var filterValue = request.HasImdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasTmdbId.HasValue) - { - var filterValue = request.HasTmdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.HasTvdbId.HasValue) - { - var filterValue = request.HasTvdbId.Value; - - var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.IsYearMismatched.HasValue) - { - var filterValue = request.IsYearMismatched.Value; - - if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue) - { - return false; - } - } - - if (request.HasOfficialRating.HasValue) - { - var filterValue = request.HasOfficialRating.Value; - - var hasValue = !string.IsNullOrEmpty(i.OfficialRating); - - if (hasValue != filterValue) - { - return false; - } - } - - if (request.IsPlaceHolder.HasValue) - { - var filterValue = request.IsPlaceHolder.Value; - - var isPlaceHolder = false; - - var hasPlaceHolder = i as ISupportsPlaceHolders; - - if (hasPlaceHolder != null) - { - isPlaceHolder = hasPlaceHolder.IsPlaceHolder; - } - - if (isPlaceHolder != filterValue) - { - return false; - } - } - - if (request.HasSpecialFeature.HasValue) - { - var filterValue = request.HasSpecialFeature.Value; - - var movie = i as IHasSpecialFeatures; - - if (movie != null) - { - var ok = filterValue - ? movie.SpecialFeatureIds.Count > 0 - : movie.SpecialFeatureIds.Count == 0; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.HasSubtitles.HasValue) - { - var val = request.HasSubtitles.Value; - - if (video == null || val != video.HasSubtitles) - { - return false; - } - } - - if (request.HasParentalRating.HasValue) - { - var val = request.HasParentalRating.Value; - - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (val) - { - if (string.IsNullOrEmpty(rating)) - { - return false; - } - } - else - { - if (!string.IsNullOrEmpty(rating)) - { - return false; - } - } - } - - if (request.HasTrailer.HasValue) - { - var val = request.HasTrailer.Value; - var trailerCount = 0; - - var hasTrailers = i as IHasTrailers; - if (hasTrailers != null) - { - trailerCount = hasTrailers.GetTrailerIds().Count; - } - - var ok = val ? trailerCount > 0 : trailerCount == 0; - - if (!ok) - { - return false; - } - } - - if (request.HasThemeSong.HasValue) - { - var filterValue = request.HasThemeSong.Value; - - var themeCount = 0; - var iHasThemeMedia = i as IHasThemeMedia; - - if (iHasThemeMedia != null) - { - themeCount = iHasThemeMedia.ThemeSongIds.Count; - } - var ok = filterValue ? themeCount > 0 : themeCount == 0; - - if (!ok) - { - return false; - } - } - - if (request.HasThemeVideo.HasValue) - { - var filterValue = request.HasThemeVideo.Value; - - var themeCount = 0; - var iHasThemeMedia = i as IHasThemeMedia; - - if (iHasThemeMedia != null) - { - themeCount = iHasThemeMedia.ThemeVideoIds.Count; - } - var ok = filterValue ? themeCount > 0 : themeCount == 0; - - if (!ok) - { - return false; - } - } - - // Apply tag filter - var tags = request.GetTags(); - if (tags.Length > 0) - { - var hasTags = i as IHasTags; - if (hasTags == null) - { - return false; - } - if (!(tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - } - - // Apply official rating filter - var officialRatings = request.GetOfficialRatings(); - if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty)) - { - return false; - } - - // Apply genre filter - var genres = request.GetGenres(); - if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - - // Filter by VideoType - var videoTypes = request.GetVideoTypes(); - if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType))) - { - return false; - } - - var imageTypes = request.GetImageTypes().ToList(); - if (imageTypes.Count > 0) - { - if (!(imageTypes.Any(i.HasImage))) - { - return false; - } - } - - // Apply studio filter - var studios = request.GetStudios(); - if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - - // Apply studio filter - var studioIds = request.GetStudioIds(); - if (studioIds.Length > 0 && !studioIds.Any(id => - { - var studioItem = libraryManager.GetItemById(id); - return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase); - })) - { - return false; - } - - // Apply year filter - var years = request.GetYears(); - if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) - { - return false; - } - - // Apply person filter - var personIds = request.GetPersonIds(); - if (personIds.Length > 0) - { - var names = personIds - .Select(libraryManager.GetItemById) - .Select(p => p == null ? "-1" : p.Name) - .ToList(); - - if (!(names.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - } - - // Apply person filter - if (!string.IsNullOrEmpty(request.Person)) - { - var personTypes = request.GetPersonTypes(); - - if (personTypes.Length == 0) - { - if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) - { - return false; - } - } - else - { - var types = personTypes; - - var ok = new[] { i }.Any(item => - item.People != null && - item.People.Any(p => - p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); - - if (!ok) - { - return false; - } - } - } - } - - if (request.MinCommunityRating.HasValue) - { - var val = request.MinCommunityRating.Value; - - if (!(i.CommunityRating.HasValue && i.CommunityRating >= val)) - { - return false; - } - } - - if (request.MinCriticRating.HasValue) - { - var val = request.MinCriticRating.Value; - - var hasCriticRating = i as IHasCriticRating; - - if (hasCriticRating != null) - { - if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) - { - return false; - } - } - else - { - return false; - } - } - - // Artists - if (!string.IsNullOrEmpty(request.ArtistIds)) - { - var artistIds = request.ArtistIds.Split('|'); - - var audio = i as IHasArtist; - - if (!(audio != null && artistIds.Any(id => - { - var artistItem = libraryManager.GetItemById(id); - return artistItem != null && audio.HasAnyArtist(artistItem.Name); - }))) - { - return false; - } - } - - // Artists - if (!string.IsNullOrEmpty(request.Artists)) - { - var artists = request.Artists.Split('|'); - - var audio = i as IHasArtist; - - if (!(audio != null && artists.Any(audio.HasAnyArtist))) - { - return false; - } - } - - // Albums - if (!string.IsNullOrEmpty(request.Albums)) - { - var albums = request.Albums.Split('|'); - - var audio = i as Audio; - - if (audio != null) - { - if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - var album = i as MusicAlbum; - - if (album != null) - { - if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - var musicVideo = i as MusicVideo; - - if (musicVideo != null) - { - if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - } - - 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)) - { - var level = _localization.GetRatingLevel(request.MinOfficialRating); - - if (level.HasValue) - { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (!string.IsNullOrEmpty(rating)) - { - var itemLevel = _localization.GetRatingLevel(rating); - - if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value)) - { - return false; - } - } - } - } - - // Max official rating - if (!string.IsNullOrEmpty(request.MaxOfficialRating)) - { - var level = _localization.GetRatingLevel(request.MaxOfficialRating); - - if (level.HasValue) - { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (!string.IsNullOrEmpty(rating)) - { - var itemLevel = _localization.GetRatingLevel(rating); - - if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value)) - { - return false; - } - } - } - } - - // LocationTypes - if (!string.IsNullOrEmpty(request.LocationTypes)) - { - var vals = request.LocationTypes.Split(','); - if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) - { - var vals = request.ExcludeLocationTypes.Split(','); - if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater)) - { - var ok = new[] { i }.OfType() - .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1); - - if (!ok) - { - return false; - } - } - - // Filter by Series Status - if (!string.IsNullOrEmpty(request.SeriesStatus)) - { - var vals = request.SeriesStatus.Split(','); - - var ok = new[] { i }.OfType().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase)); - - if (!ok) - { - return false; - } - } - - // Filter by Series AirDays - if (!string.IsNullOrEmpty(request.AirDays)) - { - var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)); - - var ok = new[] { i }.OfType().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d))); - - if (!ok) - { - return false; - } - } - - if (request.MinPlayers.HasValue) - { - var filterValue = request.MinPlayers.Value; - - var game = i as Game; - - if (game != null) - { - var players = game.PlayersSupported ?? 1; - - var ok = players >= filterValue; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.MaxPlayers.HasValue) - { - var filterValue = request.MaxPlayers.Value; - - var game = i as Game; - - if (game != null) - { - var players = game.PlayersSupported ?? 1; - - var ok = players <= filterValue; - - if (!ok) - { - return false; - } - } - else - { - return false; - } - } - - if (request.ParentIndexNumber.HasValue) - { - var filterValue = request.ParentIndexNumber.Value; - - var episode = i as Episode; - - if (episode != null) - { - if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue) - { - return false; - } - } - - var song = i as Audio; - - if (song != null) - { - if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue) - { - return false; - } - } - } - - if (request.AiredDuringSeason.HasValue) - { - var episode = i as Episode; - - if (episode == null) - { - return false; - } - - if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any()) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.MinPremiereDate)) - { - var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date)) - { - return false; - } - } - - if (!string.IsNullOrEmpty(request.MaxPremiereDate)) - { - var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - - if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date)) - { - return false; - } - } - - return true; - } - - /// Applies the paging. - /// The request. - /// The items. - /// IEnumerable{BaseItem}. - private IEnumerable ApplyPaging(GetItems request, IEnumerable items) - { - // Start at - if (request.StartIndex.HasValue) - { - items = items.Skip(request.StartIndex.Value); - } - - // Return limit - if (request.Limit.HasValue) - { - items = items.Take(request.Limit.Value); - } - - return items; - } - - /// Gets items query. - /// The request. - /// The user. - /// The items query. - private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user) - { - var query = new InternalItemsQuery - { - User = user, - IsPlayed = request.IsPlayed, - MediaTypes = request.GetMediaTypes(), - IncludeItemTypes = request.GetIncludeItemTypes(), - ExcludeItemTypes = request.GetExcludeItemTypes(), - Recursive = true, - SortBy = request.GetOrderBy(), - SortOrder = request.SortOrder ?? SortOrder.Ascending, - - Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager), - StartIndex = request.StartIndex, - IsMissing = request.IsMissing, - IsVirtualUnaired = request.IsVirtualUnaired, - IsUnaired = request.IsUnaired, - CollapseBoxSetItems = request.CollapseBoxSetItems, - NameLessThan = request.NameLessThan, - NameStartsWith = request.NameStartsWith, - NameStartsWithOrGreater = request.NameStartsWithOrGreater, - HasImdbId = request.HasImdbId, - IsYearMismatched = request.IsYearMismatched, - IsUnidentified = request.IsUnidentified, - IsPlaceHolder = request.IsPlaceHolder, - IsLocked = request.IsLocked, - IsInBoxSet = request.IsInBoxSet, - IsHD = request.IsHD, - Is3D = request.Is3D, - HasTvdbId = request.HasTvdbId, - HasTmdbId = request.HasTmdbId, - HasOverview = request.HasOverview, - HasOfficialRating = request.HasOfficialRating, - HasParentalRating = request.HasParentalRating, - HasSpecialFeature = request.HasSpecialFeature, - HasSubtitles = request.HasSubtitles, - HasThemeSong = request.HasThemeSong, - HasThemeVideo = request.HasThemeVideo, - HasTrailer = request.HasTrailer, - Tags = request.GetTags(), - OfficialRatings = request.GetOfficialRatings(), - Genres = request.GetGenres(), - Studios = request.GetStudios(), - StudioIds = request.GetStudioIds(), - Person = request.Person, - PersonIds = request.GetPersonIds(), - PersonTypes = request.GetPersonTypes(), - Years = request.GetYears(), - ImageTypes = request.GetImageTypes().ToArray(), - VideoTypes = request.GetVideoTypes().ToArray(), - AdjacentTo = request.AdjacentTo - }; - - if (!string.IsNullOrWhiteSpace(request.Ids)) - { - query.CollapseBoxSetItems = false; - } - - foreach (var filter in request.GetFilters()) - { - switch (filter) - { - case ItemFilter.Dislikes: - query.IsLiked = false; - break; - case ItemFilter.IsFavorite: - query.IsFavorite = true; - break; - case ItemFilter.IsFavoriteOrLikes: - query.IsFavoriteOrLiked = true; - break; - case ItemFilter.IsFolder: - query.IsFolder = true; - break; - case ItemFilter.IsNotFolder: - query.IsFolder = false; - break; - case ItemFilter.IsPlayed: - query.IsPlayed = true; - break; - case ItemFilter.IsRecentlyAdded: - break; - case ItemFilter.IsResumable: - query.IsResumable = true; - break; - case ItemFilter.IsUnplayed: - query.IsPlayed = false; - break; - case ItemFilter.Likes: - query.IsLiked = true; - break; - } - } - - if (request.HasQueryLimit) - query.Limit = request.Limit; - return query; - } - - /// Gets query result. - /// The request. - /// The query result. - private async Task> GetQueryResult(BaseReportRequest request) - { - // Placeholder in case needed later - request.Recursive = true; - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts"; - - var parentItem = string.IsNullOrEmpty(request.ParentId) ? - (user == null ? _libraryManager.RootFolder : user.RootFolder) : - _libraryManager.GetItemById(request.ParentId); - - var item = string.IsNullOrEmpty(request.ParentId) ? - user == null ? _libraryManager.RootFolder : user.RootFolder : - parentItem; - - IEnumerable items; - - if (request.Recursive) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - return result; - } - else - { - if (user == null) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false); - return result; - } - - var userRoot = item as UserRootFolder; - - if (userRoot == null) - { - var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false); - - return result; - } - - items = ((Folder)item).GetChildren(user, true); - } - - return new QueryResult { Items = items.ToArray() }; - - } - - /// Gets report activities. - /// The request. - /// The report activities. - private Task GetReportActivities(IReportsDownload request) - { - return Task.Run(() => - { - DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? - (DateTime?)null : - DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - var queryResult = _repo.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); - //var queryResult = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); - - ReportActivitiesBuilder builder = new ReportActivitiesBuilder(_libraryManager, _userManager); - var result = builder.GetResult(queryResult, request); - result.TotalRecordCount = queryResult.TotalRecordCount; - return result; - - }); - - } - - /// Gets report result. - /// The request. - /// The report result. - private async Task GetReportResult(GetItemReport request) - { - ReportBuilder reportBuilder = new ReportBuilder(_libraryManager); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); - ReportResult reportResult = reportBuilder.GetResult(queryResult.Items, request); - reportResult.TotalRecordCount = queryResult.TotalRecordCount; - - return reportResult; - } - - /// Gets report statistic. - /// The request. - /// The report statistic. - private async Task GetReportStatistic(GetReportStatistics request) - { - ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes); - QueryResult queryResult = await GetQueryResult(request).ConfigureAwait(false); - - ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager); - ReportStatResult reportResult = reportBuilder.GetResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5); - reportResult.TotalRecordCount = reportResult.Groups.Count(); - return reportResult; - } - - #endregion - - } + { + var video = i as Video; + + if (!isPreFiltered) + { + var mediaTypes = request.GetMediaTypes(); + if (mediaTypes.Length > 0) + { + if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + } + + if (request.IsPlayed.HasValue) + { + var val = request.IsPlayed.Value; + if (i.IsPlayed(user) != val) + { + return false; + } + } + + // Exclude item types + var excluteItemTypes = request.GetExcludeItemTypes(); + if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + // Include item types + var includeItemTypes = request.GetIncludeItemTypes(); + if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + if (request.IsInBoxSet.HasValue) + { + var val = request.IsInBoxSet.Value; + if (i.Parents.OfType().Any() != val) + { + return false; + } + } + + // Filter by Video3DFormat + if (request.Is3D.HasValue) + { + var val = request.Is3D.Value; + + if (video == null || val != video.Video3DFormat.HasValue) + { + return false; + } + } + + if (request.IsHD.HasValue) + { + var val = request.IsHD.Value; + + if (video == null || val != video.IsHD) + { + return false; + } + } + + if (request.IsUnidentified.HasValue) + { + var val = request.IsUnidentified.Value; + if (i.IsUnidentified != val) + { + return false; + } + } + + if (request.IsLocked.HasValue) + { + var val = request.IsLocked.Value; + if (i.IsLocked != val) + { + return false; + } + } + + if (request.HasOverview.HasValue) + { + var filterValue = request.HasOverview.Value; + + var hasValue = !string.IsNullOrEmpty(i.Overview); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasImdbId.HasValue) + { + var filterValue = request.HasImdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasTmdbId.HasValue) + { + var filterValue = request.HasTmdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.HasTvdbId.HasValue) + { + var filterValue = request.HasTvdbId.Value; + + var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.IsYearMismatched.HasValue) + { + var filterValue = request.IsYearMismatched.Value; + + if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue) + { + return false; + } + } + + if (request.HasOfficialRating.HasValue) + { + var filterValue = request.HasOfficialRating.Value; + + var hasValue = !string.IsNullOrEmpty(i.OfficialRating); + + if (hasValue != filterValue) + { + return false; + } + } + + if (request.IsPlaceHolder.HasValue) + { + var filterValue = request.IsPlaceHolder.Value; + + var isPlaceHolder = false; + + var hasPlaceHolder = i as ISupportsPlaceHolders; + + if (hasPlaceHolder != null) + { + isPlaceHolder = hasPlaceHolder.IsPlaceHolder; + } + + if (isPlaceHolder != filterValue) + { + return false; + } + } + + if (request.HasSpecialFeature.HasValue) + { + var filterValue = request.HasSpecialFeature.Value; + + var movie = i as IHasSpecialFeatures; + + if (movie != null) + { + var ok = filterValue + ? movie.SpecialFeatureIds.Count > 0 + : movie.SpecialFeatureIds.Count == 0; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.HasSubtitles.HasValue) + { + var val = request.HasSubtitles.Value; + + if (video == null || val != video.HasSubtitles) + { + return false; + } + } + + if (request.HasParentalRating.HasValue) + { + var val = request.HasParentalRating.Value; + + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (val) + { + if (string.IsNullOrEmpty(rating)) + { + return false; + } + } + else + { + if (!string.IsNullOrEmpty(rating)) + { + return false; + } + } + } + + if (request.HasTrailer.HasValue) + { + var val = request.HasTrailer.Value; + var trailerCount = 0; + + var hasTrailers = i as IHasTrailers; + if (hasTrailers != null) + { + trailerCount = hasTrailers.GetTrailerIds().Count; + } + + var ok = val ? trailerCount > 0 : trailerCount == 0; + + if (!ok) + { + return false; + } + } + + if (request.HasThemeSong.HasValue) + { + var filterValue = request.HasThemeSong.Value; + + var themeCount = 0; + var iHasThemeMedia = i as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeSongIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + if (request.HasThemeVideo.HasValue) + { + var filterValue = request.HasThemeVideo.Value; + + var themeCount = 0; + var iHasThemeMedia = i as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeVideoIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + // Apply tag filter + var tags = request.GetTags(); + if (tags.Length > 0) + { + var hasTags = i as IHasTags; + if (hasTags == null) + { + return false; + } + if (!(tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + } + + // Apply official rating filter + var officialRatings = request.GetOfficialRatings(); + if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty)) + { + return false; + } + + // Apply genre filter + var genres = request.GetGenres(); + if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + + // Filter by VideoType + var videoTypes = request.GetVideoTypes(); + if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType))) + { + return false; + } + + var imageTypes = request.GetImageTypes().ToList(); + if (imageTypes.Count > 0) + { + if (!(imageTypes.Any(i.HasImage))) + { + return false; + } + } + + // Apply studio filter + var studios = request.GetStudios(); + if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + + // Apply studio filter + var studioIds = request.GetStudioIds(); + if (studioIds.Length > 0 && !studioIds.Any(id => + { + var studioItem = libraryManager.GetItemById(id); + return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase); + })) + { + return false; + } + + // Apply year filter + var years = request.GetYears(); + if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) + { + return false; + } + + // Apply person filter + var personIds = request.GetPersonIds(); + if (personIds.Length > 0) + { + var names = personIds + .Select(libraryManager.GetItemById) + .Select(p => p == null ? "-1" : p.Name) + .ToList(); + + if (!(names.Any(v => _libraryManager.GetPeople(i).Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + } + + // Apply person filter + if (!string.IsNullOrEmpty(request.Person)) + { + var personTypes = request.GetPersonTypes(); + + if (personTypes.Length == 0) + { + if (!(_libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) + { + return false; + } + } + else + { + var types = personTypes; + + var ok = new[] { i }.Any(item => + _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) + { + return false; + } + } + } + } + + if (request.MinCommunityRating.HasValue) + { + var val = request.MinCommunityRating.Value; + + if (!(i.CommunityRating.HasValue && i.CommunityRating >= val)) + { + return false; + } + } + + if (request.MinCriticRating.HasValue) + { + var val = request.MinCriticRating.Value; + + var hasCriticRating = i as IHasCriticRating; + + if (hasCriticRating != null) + { + if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) + { + return false; + } + } + else + { + return false; + } + } + + // Artists + if (!string.IsNullOrEmpty(request.ArtistIds)) + { + var artistIds = request.ArtistIds.Split('|'); + + var audio = i as IHasArtist; + + if (!(audio != null && artistIds.Any(id => + { + var artistItem = libraryManager.GetItemById(id); + return artistItem != null && audio.HasAnyArtist(artistItem.Name); + }))) + { + return false; + } + } + + // Artists + if (!string.IsNullOrEmpty(request.Artists)) + { + var artists = request.Artists.Split('|'); + + var audio = i as IHasArtist; + + if (!(audio != null && artists.Any(audio.HasAnyArtist))) + { + return false; + } + } + + // Albums + if (!string.IsNullOrEmpty(request.Albums)) + { + var albums = request.Albums.Split('|'); + + var audio = i as Audio; + + if (audio != null) + { + if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + var album = i as MusicAlbum; + + if (album != null) + { + if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + var musicVideo = i as MusicVideo; + + if (musicVideo != null) + { + if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + } + + 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)) + { + var level = _localization.GetRatingLevel(request.MinOfficialRating); + + if (level.HasValue) + { + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (!string.IsNullOrEmpty(rating)) + { + var itemLevel = _localization.GetRatingLevel(rating); + + if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value)) + { + return false; + } + } + } + } + + // Max official rating + if (!string.IsNullOrEmpty(request.MaxOfficialRating)) + { + var level = _localization.GetRatingLevel(request.MaxOfficialRating); + + if (level.HasValue) + { + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = i.OfficialRating; + } + + if (!string.IsNullOrEmpty(rating)) + { + var itemLevel = _localization.GetRatingLevel(rating); + + if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value)) + { + return false; + } + } + } + } + + // LocationTypes + if (!string.IsNullOrEmpty(request.LocationTypes)) + { + var vals = request.LocationTypes.Split(','); + if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } + } + + // ExcludeLocationTypes + if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + { + var vals = request.ExcludeLocationTypes.Split(','); + if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater)) + { + var ok = new[] { i }.OfType() + .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1); + + if (!ok) + { + return false; + } + } + + // Filter by Series Status + if (!string.IsNullOrEmpty(request.SeriesStatus)) + { + var vals = request.SeriesStatus.Split(','); + + var ok = new[] { i }.OfType().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase)); + + if (!ok) + { + return false; + } + } + + // Filter by Series AirDays + if (!string.IsNullOrEmpty(request.AirDays)) + { + var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)); + + var ok = new[] { i }.OfType().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d))); + + if (!ok) + { + return false; + } + } + + if (request.MinPlayers.HasValue) + { + var filterValue = request.MinPlayers.Value; + + var game = i as Game; + + if (game != null) + { + var players = game.PlayersSupported ?? 1; + + var ok = players >= filterValue; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.MaxPlayers.HasValue) + { + var filterValue = request.MaxPlayers.Value; + + var game = i as Game; + + if (game != null) + { + var players = game.PlayersSupported ?? 1; + + var ok = players <= filterValue; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (request.ParentIndexNumber.HasValue) + { + var filterValue = request.ParentIndexNumber.Value; + + var episode = i as Episode; + + if (episode != null) + { + if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue) + { + return false; + } + } + + var song = i as Audio; + + if (song != null) + { + if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue) + { + return false; + } + } + } + + if (request.AiredDuringSeason.HasValue) + { + var episode = i as Episode; + + if (episode == null) + { + return false; + } + + if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any()) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.MinPremiereDate)) + { + var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + + if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date)) + { + return false; + } + } + + if (!string.IsNullOrEmpty(request.MaxPremiereDate)) + { + var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + + if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date)) + { + return false; + } + } + + return true; + } + + /// Applies the paging. + /// The request. + /// The items. + /// IEnumerable{BaseItem}. + private IEnumerable ApplyPaging(GetItems request, IEnumerable items) + { + // Start at + if (request.StartIndex.HasValue) + { + items = items.Skip(request.StartIndex.Value); + } + + // Return limit + if (request.Limit.HasValue) + { + items = items.Take(request.Limit.Value); + } + + return items; + } + + } } diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs index e2169c9c3..541bb92d9 100644 --- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs +++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs @@ -9,265 +9,206 @@ using System.Threading.Tasks; namespace MediaBrowser.Api.Reports { - /// A report stat builder. - /// - public class ReportStatBuilder : ReportBuilderBase - { - #region [Constructors] + /// A report stat builder. + /// + public class ReportStatBuilder : ReportBuilderBase + { + /// + /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. + /// Manager for library. + public ReportStatBuilder(ILibraryManager libraryManager) + : base(libraryManager) + { + } - /// - /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. - /// Manager for library. - public ReportStatBuilder(ILibraryManager libraryManager) - : base(libraryManager) - { - } + /// Gets report stat result. + /// The items. + /// Type of the report row. + /// The top item. + /// The report stat result. + public ReportStatResult GetReportStatResult(BaseItem[] items, ReportViewType reportRowType, int topItem = 5) + { + ReportStatResult result = new ReportStatResult(); + result = this.GetResultGenres(result, items, topItem); + result = this.GetResultStudios(result, items, topItem); + result = this.GetResultPersons(result, items, topItem); + result = this.GetResultProductionYears(result, items, topItem); + result = this.GetResulProductionLocations(result, items, topItem); + result = this.GetResultCommunityRatings(result, items, topItem); + result = this.GetResultParentalRatings(result, items, topItem); - #endregion + switch (reportRowType) + { + case ReportViewType.Season: + case ReportViewType.Series: + case ReportViewType.MusicAlbum: + case ReportViewType.MusicArtist: + case ReportViewType.Game: + break; + case ReportViewType.Movie: + case ReportViewType.BoxSet: - #region [Public Methods] - - /// Gets report stat result. - /// The items. - /// List of types of the report include items. - /// The top item. - /// The report stat result. - public ReportStatResult GetResult(BaseItem[] items, ReportIncludeItemTypes reportIncludeItemTypes, int topItem = 5) - { - ReportStatResult result = new ReportStatResult(); - result = this.GetResultGenres(result, items, topItem); - result = this.GetResultStudios(result, items, topItem); - result = this.GetResultPersons(result, items, topItem); - result = this.GetResultProductionYears(result, items, topItem); - result = this.GetResulProductionLocations(result, items, topItem); - result = this.GetResultCommunityRatings(result, items, topItem); - result = this.GetResultParentalRatings(result, items, topItem); + break; + case ReportViewType.Book: + case ReportViewType.Episode: + case ReportViewType.Video: + case ReportViewType.MusicVideo: + case ReportViewType.Trailer: + case ReportViewType.Audio: + case ReportViewType.BaseItem: + default: + break; + } - switch (reportIncludeItemTypes) - { - case ReportIncludeItemTypes.Season: - case ReportIncludeItemTypes.Series: - case ReportIncludeItemTypes.MusicAlbum: - case ReportIncludeItemTypes.MusicArtist: - case ReportIncludeItemTypes.Game: - break; - case ReportIncludeItemTypes.Movie: - case ReportIncludeItemTypes.BoxSet: + result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList(); - break; - case ReportIncludeItemTypes.Book: - case ReportIncludeItemTypes.Episode: - case ReportIncludeItemTypes.Video: - case ReportIncludeItemTypes.MusicVideo: - case ReportIncludeItemTypes.Trailer: - case ReportIncludeItemTypes.Audio: - case ReportIncludeItemTypes.BaseItem: - default: - break; - } + return result; + } - result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList(); + private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderGenres"), topItem, + items.SelectMany(x => x.Genres) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetGenreID(x.Key) + })); + return result; - return result; - } + } - #endregion + private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderStudios"), topItem, + items.SelectMany(x => x.Studios) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetStudioID(x.Key) + }) + ); - #region [Protected Internal Methods] - /// Gets the headers. - /// Type of the header. - /// The request. - /// The headers. - /// - protected internal override List GetHeaders(H request) - { - throw new NotImplementedException(); - } + return result; - #endregion + } - #region [Private Methods] + private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + List t = new List { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "AlbumArtist" }; + foreach (var item in t) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem, + items.SelectMany(x => _libraryManager.GetPeople(x)) + .Where(n => n.Type == item) + .GroupBy(x => x.Name) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key, + Value = x.Count().ToString(), + Id = GetPersonID(x.Key) + }) + ); + } - /// Gets the groups. - /// The result. - /// The header. - /// The top item. - /// The top. - private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable top) - { - if (top.Count() > 0) - { - var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) }; - group.Items.AddRange(top); - result.Groups.Add(group); - } - } + return result; + } - /// Gets resul production locations. - /// The result. - /// The items. - /// The top item. - /// The resul production locations. - private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem, - items.OfType() - .Where(x => x.ProductionLocations != null) - .SelectMany(x => x.ProductionLocations) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); + private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("LabelCommunityRating"), topItem, + items.Where(x => x.CommunityRating != null && x.CommunityRating > 0) + .GroupBy(x => x.CommunityRating) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); - return result; - } + return result; + } - /// Gets result community ratings. - /// The result. - /// The items. - /// The top item. - /// The result community ratings. - private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("LabelCommunityRating"), topItem, - items.Where(x => x.CommunityRating != null && x.CommunityRating > 0) - .GroupBy(x => x.CommunityRating) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); + private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderParentalRatings"), topItem, + items.Where(x => x.OfficialRating != null) + .GroupBy(x => x.OfficialRating) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); - return result; - } + return result; + } - /// Gets result genres. - /// The result. - /// The items. - /// The top item. - /// The result genres. - private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderGenres"), topItem, - items.SelectMany(x => x.Genres) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetGenreID(x.Key) - })); - return result; - } + private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderYears"), topItem, + items.Where(x => x.ProductionYear != null && x.ProductionYear > 0) + .GroupBy(x => x.ProductionYear) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); - /// Gets result parental ratings. - /// The result. - /// The items. - /// The top item. - /// The result parental ratings. - private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderParentalRatings"), topItem, - items.Where(x => x.OfficialRating != null) - .GroupBy(x => x.OfficialRating) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); + return result; + } - return result; - } + private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5) + { + this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderCountries"), topItem, + items.OfType() + .Where(x => x.ProductionLocations != null) + .SelectMany(x => x.ProductionLocations) + .GroupBy(x => x) + .OrderByDescending(x => x.Count()) + .Take(topItem) + .Select(x => new ReportStatItem + { + Name = x.Key.ToString(), + Value = x.Count().ToString() + }) + ); - /// Gets result persons. - /// The result. - /// The items. - /// The top item. - /// The result persons. - private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - List t = new List { PersonType.Actor, PersonType.Composer, PersonType.Director, PersonType.GuestStar, PersonType.Producer, PersonType.Writer, "Artist", "AlbumArtist" }; - foreach (var item in t) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("Option" + item), topItem, - items.SelectMany(x => x.People) - .Where(n => n.Type == item) - .GroupBy(x => x.Name) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetPersonID(x.Key) - }) - ); - } + return result; + } - return result; - } - /// Gets result production years. - /// The result. - /// The items. - /// The top item. - /// The result production years. - private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderYears"), topItem, - items.Where(x => x.ProductionYear != null && x.ProductionYear > 0) - .GroupBy(x => x.ProductionYear) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key.ToString(), - Value = x.Count().ToString() - }) - ); - - return result; - } - - /// Gets result studios. - /// The result. - /// The items. - /// The top item. - /// The result studios. - private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5) - { - this.GetGroups(result, ReportHelper.GetServerLocalizedString("HeaderStudios"), topItem, - items.SelectMany(x => x.Studios) - .GroupBy(x => x) - .OrderByDescending(x => x.Count()) - .Take(topItem) - .Select(x => new ReportStatItem - { - Name = x.Key, - Value = x.Count().ToString(), - Id = GetStudioID(x.Key) - }) - ); - - return result; - - } - - #endregion - - } + /// Gets the groups. + /// The result. + /// The header. + /// The top item. + /// The top. + private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable top) + { + if (top.Count() > 0) + { + var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) }; + group.Items.AddRange(top); + result.Groups.Add(group); + } + } + } } diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 91e749778..1e9b365db 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 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,16 @@ 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 getSimilarityScore) { var itemId = item.Id; inputItems = inputItems.Where(i => i.Id != itemId); - return inputItems.Select(i => new Tuple(i, getSimilarityScore(item, i))) + return inputItems.Select(i => new Tuple(i, getSimilarityScore(item, i, libraryManager))) .Where(i => i.Item2 > 2) .OrderByDescending(i => i.Item2) .Select(i => i.Item1); @@ -148,7 +149,7 @@ namespace MediaBrowser.Api /// The item1. /// The item2. /// System.Int32. - internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2) + internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2, ILibraryManager libraryManager) { var points = 0; @@ -169,11 +170,11 @@ 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 = libraryManager.GetPeople(item2).Select(i => i.Name) .DistinctNames() .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - points += item1.People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => + points += libraryManager.GetPeople(item1).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/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index bc2e4699c..7120f3604 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 b7fb4f542..f95beb27e 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -153,7 +153,7 @@ namespace MediaBrowser.Api.UserLibrary /// IEnumerable{PersonInfo}. private IEnumerable GetAllPeople(IEnumerable itemsList, string[] personTypes) { - var people = itemsList.SelectMany(i => i.People.OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type)); + var people = itemsList.SelectMany(i => LibraryManager.GetPeople(i).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type)); if (personTypes.Length > 0) { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 4185590ab..f6d1d32a4 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 971c09236..5f24dbf67 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 014b3ae6a..5d7c02f48 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; @@ -785,6 +775,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 +1244,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 1cc547790..5a1ad6b15 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 bcf68263a..3a610be64 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/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index c91acbe3f..bf448da0d 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 cf6916763..35f7e3350 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 cb68e5dae..233e1e0fd 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 3643c58b3..158bcb6d1 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/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs new file mode 100644 index 000000000..3468ca2d5 --- /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 ef24d4347..390fcaf80 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -76,7 +76,16 @@ namespace MediaBrowser.Controller.Entities 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; + } } } diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index b8d359369..822f305ed 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 5b70ee5f4..71e3d1ce0 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 9be30273a..dad6de01a 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 3e4dff033..41e5406e1 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1698,9 +1698,9 @@ namespace MediaBrowser.Controller.Entities .Select(libraryManager.GetItemById) .Select(i => i == null ? "-1" : i.Name) .ToList(); - + if (!(names.Any( - v => item.People.Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) + v => libraryManager.GetPeople(item).Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase)))) { return false; } @@ -1713,7 +1713,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 +1723,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 a1a152387..163dcd667 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 aa8799fa6..92028cc1a 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -412,5 +412,26 @@ namespace MediaBrowser.Controller.Library /// The item. /// IEnumerable<Folder>. IEnumerable GetCollectionFolders(BaseItem item); + + /// + /// Gets the people. + /// + /// The item. + /// List<PersonInfo>. + List GetPeople(BaseItem item); + + /// + /// Gets all people names. + /// + /// List<System.String>. + List GetAllPeople(); + + /// + /// Updates the people. + /// + /// The item. + /// The people. + /// Task. + Task UpdatePeople(BaseItem item, List people); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 3da12cd80..2179c5ecd 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 179c33d09..aaaff6bdb 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 bf86c049f..62578e675 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -173,6 +173,7 @@ + diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 7c02a0ea1..fba5f4c03 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -147,6 +147,21 @@ namespace MediaBrowser.Controller.Persistence /// The query. /// List<Guid>. List GetItemIdsList(InternalItemsQuery query); + + /// + /// Gets the people. + /// + /// The item identifier. + /// List<PersonInfo>. + List GetPeople(Guid itemId); + + /// + /// Updates the people. + /// + /// The item identifier. + /// The people. + /// Task. + Task UpdatePeople(Guid itemId, List people); } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index a8a3e88ab..c1a4fa765 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 8be3ee7aa..76b7a3136 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 756458cfa..a18dd83e8 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/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs index dceea0cc6..5cb106fec 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Providers; using System.Collections.Generic; using System.Threading; @@ -35,5 +36,12 @@ namespace MediaBrowser.Controller.Subtitles /// The cancellation token. /// Task{SubtitleResponse}. Task GetSubtitles(string id, CancellationToken cancellationToken); + + /// + /// Gets the supported languages. + /// + /// The cancellation token. + /// Task<IEnumerable<NameIdPair>>. + Task> GetSupportedLanguages(CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 246c23462..6771fd1ab 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,7 +410,7 @@ namespace MediaBrowser.Dlna.ContentDirectory { if (stubType.Value == StubType.People) { - var items = item.People.Select(i => + var items = _libraryManager.GetPeople(item).Select(i => { try { @@ -488,7 +488,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private async Task> 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)) + var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Series && PeopleHelper.ContainsPerson(_libraryManager.GetPeople(i), person.Name)) .ToList(); var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery @@ -503,7 +503,7 @@ namespace MediaBrowser.Dlna.ContentDirectory .ToList(); var trailersToAdd = trailerResult.Items - .Where(i => i.ContainsPerson(person.Name)) + .Where(i => PeopleHelper.ContainsPerson(_libraryManager.GetPeople(i), person.Name)) .Where(i => { // Try to filter out dupes using imdb id @@ -569,7 +569,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private bool EnablePeopleDisplay(BaseItem item) { - if (item.People.Count > 0) + if (_libraryManager.GetPeople(item).Count > 0) { return item is Movie; } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 08f770735..50a6f3ba6 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 2d2525919..9ce62034b 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 204872e7b..06aaff734 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -82,6 +82,7 @@ + @@ -210,6 +211,9 @@ + + +