diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 435bca0b8c..2e9323e034 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -316,14 +316,14 @@ namespace MediaBrowser.Api /// The delete files. /// Task. /// deviceId - internal Task KillTranscodingJobs(string deviceId, Func deleteFiles) + internal void KillTranscodingJobs(string deviceId, Func deleteFiles) { if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } - return KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles); + KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles); } /// @@ -332,7 +332,7 @@ namespace MediaBrowser.Api /// The kill job. /// The delete files. /// Task. - internal async Task KillTranscodingJobs(Func killJob, Func deleteFiles) + internal void KillTranscodingJobs(Func killJob, Func deleteFiles) { var jobs = new List(); @@ -348,18 +348,9 @@ namespace MediaBrowser.Api return; } - await TranscodingStartLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try + foreach (var job in jobs) { - foreach (var job in jobs) - { - KillTranscodingJob(job, deleteFiles); - } - } - finally - { - TranscodingStartLock.Release(); + KillTranscodingJob(job, deleteFiles); } } @@ -501,7 +492,7 @@ namespace MediaBrowser.Api } catch (FileNotFoundException) { - + } catch (IOException ex) { @@ -563,7 +554,7 @@ namespace MediaBrowser.Api public long? BytesDownloaded { get; set; } public long? BytesTranscoded { get; set; } - + public long? TranscodingPositionTicks { get; set; } public long? DownloadPositionTicks { get; set; } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 46b4c06722..ef86a46e8a 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -126,8 +126,14 @@ namespace MediaBrowser.Api.Movies movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies); + var listEligibleForCategories = new List(); + var listEligibleForSuggestion = new List (); + var list = movies.ToList(); + listEligibleForCategories.AddRange(list); + listEligibleForSuggestion.AddRange(list); + if (user.Configuration.IncludeTrailersInSuggestions) { var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery @@ -138,17 +144,20 @@ namespace MediaBrowser.Api.Movies }, CancellationToken.None).ConfigureAwait(false); - var newTrailers = trailerResult.Items; - - list.AddRange(newTrailers); - - list = list - .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) - .ToList(); + listEligibleForSuggestion.AddRange(trailerResult.Items); } - var result = GetRecommendationCategories(user, list, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList()); + listEligibleForCategories = listEligibleForCategories + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) + .ToList(); + + listEligibleForSuggestion = listEligibleForSuggestion + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase) + .ToList(); + + var result = GetRecommendationCategories(user, listEligibleForCategories, listEligibleForSuggestion, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList()); return ToOptimizedResult(result); } @@ -210,11 +219,11 @@ namespace MediaBrowser.Api.Movies return result; } - private IEnumerable GetRecommendationCategories(User user, List allMovies, int categoryLimit, int itemLimit, List fields) + private IEnumerable GetRecommendationCategories(User user, List allMoviesForCategories, List allMovies, int categoryLimit, int itemLimit, List fields) { var categories = new List(); - var recentlyPlayedMovies = allMovies + var recentlyPlayedMovies = allMoviesForCategories .Select(i => { var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey()); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index cb95049ce1..734b6a9394 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1645,6 +1645,17 @@ namespace MediaBrowser.Api.Playback { state.OutputVideoCodec = GetVideoCodec(videoRequest); state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream); + + if (state.OutputVideoBitrate.HasValue) + { + var resolution = ResolutionNormalizer.Normalize(state.OutputVideoBitrate.Value, + state.OutputVideoCodec, + videoRequest.MaxWidth, + videoRequest.MaxHeight); + + videoRequest.MaxWidth = resolution.MaxWidth; + videoRequest.MaxHeight = resolution.MaxHeight; + } } ApplyDeviceProfileSettings(state); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 2035aff164..b99657c30f 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls // If the playlist doesn't already exist, startup ffmpeg try { - await ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)).ConfigureAwait(false); + ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)); if (currentTranscodingIndex.HasValue) { @@ -157,7 +157,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); + await WaitForMinimumSegmentCount(playlistPath, 2, cancellationTokenSource.Token).ConfigureAwait(false); } } } @@ -402,8 +402,10 @@ namespace MediaBrowser.Api.Playback.Hls var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); + var isLiveStream = (state.RunTimeTicks ?? 0) == 0; + // Main stream - var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; + var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; playlistUrl += queryString; var request = (GetMasterHlsVideoStream)state.Request; @@ -418,7 +420,7 @@ namespace MediaBrowser.Api.Playback.Hls AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup); - if (EnableAdaptiveBitrateStreaming(state)) + if (EnableAdaptiveBitrateStreaming(state, isLiveStream)) { var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value; @@ -482,7 +484,7 @@ namespace MediaBrowser.Api.Playback.Hls } } - private bool EnableAdaptiveBitrateStreaming(StreamState state) + private bool EnableAdaptiveBitrateStreaming(StreamState state, bool isLiveStream) { // Within the local network this will likely do more harm than good. if (Request.IsLocal || NetworkManager.IsInLocalNetwork(Request.RemoteIp)) @@ -491,13 +493,12 @@ namespace MediaBrowser.Api.Playback.Hls } var request = state.Request as GetMasterHlsVideoStream; - if (request != null && !request.EnableAdaptiveBitrateStreaming) { return false; } - if (string.IsNullOrWhiteSpace(state.MediaPath)) + if (isLiveStream || string.IsNullOrWhiteSpace(state.MediaPath)) { // Opening live streams is so slow it's not even worth it return false; diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index a8d4c6b86f..2263a2b371 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -72,9 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls public void Delete(StopEncodingProcess request) { - var task = ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true); - - Task.WaitAll(task); + ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true); } /// diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index c2b8069cb2..15a2d5c337 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack; using System; @@ -101,7 +102,7 @@ namespace MediaBrowser.Api.UserLibrary filteredItems = FilterByLibraryItems(request, filteredItems, user, libraryItems); - filteredItems = ItemsService.ApplySortOrder(request, filteredItems, user, LibraryManager).Cast(); + filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending).Cast(); var ibnItemsArray = filteredItems.ToList(); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index f236100147..3dcd4efbda 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -96,6 +96,21 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsPlayed", Description = "Optional filter by items that are played, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsPlayed { get; set; } + public string[] GetMediaTypes() + { + return (MediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetIncludeItemTypes() + { + return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetExcludeItemTypes() + { + return (ExcludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + /// /// Gets the filters. /// @@ -132,7 +147,7 @@ namespace MediaBrowser.Api.UserLibrary /// Gets the order by. /// /// IEnumerable{ItemSortBy}. - public IEnumerable GetOrderBy() + public string[] GetOrderBy() { var val = SortBy; diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 2ac4f5e636..011864d35e 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Threading.Tasks; namespace MediaBrowser.Api.UserLibrary { @@ -289,9 +290,9 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// System.Object. - public object Get(GetItems request) + public async Task Get(GetItems request) { - var result = GetItems(request); + var result = await GetItems(request).ConfigureAwait(false); return ToOptimizedSerializedResultUsingCache(result); } @@ -301,16 +302,27 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// Task{ItemsResult}. - private ItemsResult GetItems(GetItems request) + private async Task GetItems(GetItems request) { var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId); var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; - var items = GetItemsToSerialize(request, user, parentItem); + var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false); - items = items.AsParallel(); + var isFiltered = result.Item2; - items = ApplyAdditionalFilters(request, items, user); + if (isFiltered) + { + var currentFields = request.GetItemFields().ToList(); + + return new ItemsResult + { + TotalRecordCount = result.Item1.TotalRecordCount, + Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, currentFields, user)).ToArray() + }; + } + + var items = result.Item1.Items.Where(i => ApplyAdditionalFilters(request, i, user, false)); // Apply filters // Run them starting with the ones that are likely to reduce the list the most @@ -321,16 +333,14 @@ namespace MediaBrowser.Api.UserLibrary items = FilterVirtualEpisodes(request, items, user); - items = items.AsEnumerable(); - if (CollapseBoxSetItems(request, parentItem, user)) { items = _collectionManager.CollapseItemsWithinBoxSets(items, user); } items = ApplyPostCollectionCollapseFilters(request, items, user); - - items = ApplySortOrder(request, items, user, _libraryManager); + + items = _libraryManager.Sort(items, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending); // This must be the last filter if (!string.IsNullOrEmpty(request.AdjacentTo)) @@ -387,8 +397,7 @@ namespace MediaBrowser.Api.UserLibrary /// The user. /// The parent item. /// IEnumerable{BaseItem}. - /// - private IEnumerable GetItemsToSerialize(GetItems request, User user, BaseItem parentItem) + private async Task, bool>> GetItemsToSerialize(GetItems request, User user, BaseItem parentItem) { var item = string.IsNullOrEmpty(request.ParentId) ? user == null ? _libraryManager.RootFolder : user.RootFolder : @@ -406,19 +415,40 @@ namespace MediaBrowser.Api.UserLibrary else if (request.Recursive) { - items = user == null ? - ((Folder)item).RecursiveChildren : - ((Folder)item).GetRecursiveChildren(user); + if (user == null) + { + items = ((Folder)item).RecursiveChildren; - items = _libraryManager.ReplaceVideosWithPrimaryVersions(items); + items = _libraryManager.ReplaceVideosWithPrimaryVersions(items); + } + else + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)); + + return new Tuple, bool>(result, true); + } } else { - items = user == null ? - ((Folder)item).Children : - ((Folder)item).GetChildren(user, true); + if (user == null) + { + items = ((Folder)item).Children; - items = _libraryManager.ReplaceVideosWithPrimaryVersions(items); + items = _libraryManager.ReplaceVideosWithPrimaryVersions(items); + } + else + { + var userRoot = item as UserRootFolder; + + if (userRoot == null) + { + var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)); + + return new Tuple, bool>(result, true); + } + + items = ((Folder)item).GetChildren(user, true); + } } if (request.IncludeIndexContainers) @@ -430,25 +460,72 @@ namespace MediaBrowser.Api.UserLibrary list.AddRange(containers); - return list.Distinct(); + items = list.Distinct(); } - return items; + return new Tuple, bool>(new QueryResult + { + Items = items.ToArray() + + }, false); } - /// - /// Applies sort order - /// - /// The request. - /// The items. - /// The user. - /// The library manager. - /// IEnumerable{BaseItem}. - internal static IEnumerable ApplySortOrder(BaseItemsRequest request, IEnumerable items, User user, ILibraryManager libraryManager) + private InternalItemsQuery GetItemsQuery(GetItems request, User user) { - var orderBy = request.GetOrderBy().ToList(); + var query = new InternalItemsQuery + { + User = user, + IsPlayed = request.IsPlayed, + MediaTypes = request.GetMediaTypes(), + IncludeItemTypes = request.GetIncludeItemTypes(), + ExcludeItemTypes = request.GetExcludeItemTypes(), + Recursive = request.Recursive, + SortBy = request.GetOrderBy(), + SortOrder = request.SortOrder ?? SortOrder.Ascending, - return orderBy.Count == 0 ? items : libraryManager.Sort(items, user, orderBy, request.SortOrder ?? SortOrder.Ascending); + Filter = (i, u) => ApplyAdditionalFilters(request, i, u, true), + + Limit = request.Limit, + StartIndex = request.StartIndex + }; + + 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; + } + } + + return query; } /// @@ -648,36 +725,70 @@ namespace MediaBrowser.Api.UserLibrary return items; } - /// - /// Applies the additional filters. - /// - /// The request. - /// The items. - /// IEnumerable{BaseItem}. - private IEnumerable ApplyAdditionalFilters(GetItems request, IEnumerable items, User user) + private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, bool isPreFiltered) { + 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.MinCommunityRating.HasValue) { var val = request.MinCommunityRating.Value; - items = items.Where(i => i.CommunityRating.HasValue && i.CommunityRating >= val); + if (!(i.CommunityRating.HasValue && i.CommunityRating >= val)) + { + return false; + } } if (request.MinCriticRating.HasValue) { var val = request.MinCriticRating.Value; - items = items.Where(i => + var hasCriticRating = i as IHasCriticRating; + + if (hasCriticRating != null) { - var hasCriticRating = i as IHasCriticRating; - - if (hasCriticRating != null) + if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) { - return hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val; + return false; } - + } + else + { return false; - }); + } } // Artists @@ -685,12 +796,12 @@ namespace MediaBrowser.Api.UserLibrary { var artists = request.Artists.Split('|'); - items = items.Where(i => - { - var audio = i as IHasArtist; + var audio = i as IHasArtist; - return audio != null && artists.Any(audio.HasArtist); - }); + if (!(audio != null && artists.Any(audio.HasArtist))) + { + return false; + } } // Albums @@ -698,37 +809,46 @@ namespace MediaBrowser.Api.UserLibrary { var albums = request.Albums.Split('|'); - items = items.Where(i => + var audio = i as Audio; + + if (audio != null) { - var audio = i as Audio; - - if (audio != null) + if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase))) { - return albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)); + return false; } + } - var album = i as MusicAlbum; + var album = i as MusicAlbum; - if (album != null) + if (album != null) + { + if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase))) { - return albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)); + return false; } + } - var musicVideo = i as MusicVideo; + var musicVideo = i as MusicVideo; - if (musicVideo != null) + if (musicVideo != null) + { + if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase))) { - return albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)); + return false; } + } - return false; - }); + return false; } // Min index number if (request.MinIndexNumber.HasValue) { - items = items.Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value); + if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value)) + { + return false; + } } // Min official rating @@ -738,24 +858,22 @@ namespace MediaBrowser.Api.UserLibrary if (level.HasValue) { - items = items.Where(i => + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (string.IsNullOrEmpty(rating)) - { - return true; - } + rating = i.OfficialRating; + } + if (!string.IsNullOrEmpty(rating)) + { var itemLevel = _localization.GetRatingLevel(rating); - return !itemLevel.HasValue || itemLevel.Value >= level.Value; - }); + if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value)) + { + return false; + } + } } } @@ -766,60 +884,54 @@ namespace MediaBrowser.Api.UserLibrary if (level.HasValue) { - items = items.Where(i => + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) { - var rating = i.CustomRating; - - if (string.IsNullOrEmpty(rating)) - { - rating = i.OfficialRating; - } - - if (string.IsNullOrEmpty(rating)) - { - return true; - } + rating = i.OfficialRating; + } + if (!string.IsNullOrEmpty(rating)) + { var itemLevel = _localization.GetRatingLevel(rating); - return !itemLevel.HasValue || itemLevel.Value <= level.Value; - }); + if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value)) + { + return false; + } + } } } - // Exclude item types - if (!string.IsNullOrEmpty(request.ExcludeItemTypes)) - { - var vals = request.ExcludeItemTypes.Split(','); - items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); - } - - // Include item types - if (!string.IsNullOrEmpty(request.IncludeItemTypes)) - { - var vals = request.IncludeItemTypes.Split(','); - items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); - } - // LocationTypes if (!string.IsNullOrEmpty(request.LocationTypes)) { var vals = request.LocationTypes.Split(','); - items = items.Where(f => vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); + if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } } // ExcludeLocationTypes if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) { var vals = request.ExcludeLocationTypes.Split(','); - items = items.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); + if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + return false; + } } if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater)) { - items = items.OfType() - .Where(i => string.Compare(request.AlbumArtistStartsWithOrGreater, i.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1) - .Cast(); + 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 @@ -827,7 +939,12 @@ namespace MediaBrowser.Api.UserLibrary { var vals = request.SeriesStatus.Split(','); - items = items.OfType().Where(i => i.Status.HasValue && vals.Contains(i.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase)); + 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 @@ -835,13 +952,24 @@ namespace MediaBrowser.Api.UserLibrary { var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)); - items = items.OfType().Where(i => i.AirDays != null && days.Any(d => i.AirDays.Contains(d))); + var ok = new[] { i }.OfType().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d))); + + if (!ok) + { + return false; + } } // Filter by Video3DFormat if (request.Is3D.HasValue) { - items = items.OfType