diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-10-07 21:37:45 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-10-07 21:37:45 -0400 |
| commit | 4999f1948533f513f43966b7af03b3db8881645d (patch) | |
| tree | 0428d7b7040bbc9a00235f01a0eed4521654b6a0 | |
| parent | 28d4c54de8c6402b44da81314435bb70dac63635 (diff) | |
resolve moviedb issues
21 files changed, 1339 insertions, 812 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 734b6a939..ba1f73ff2 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1718,7 +1718,7 @@ namespace MediaBrowser.Api.Playback string mediaSourceId, CancellationToken cancellationToken) { - var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(id, cancellationToken) + var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(id, true, cancellationToken) .ConfigureAwait(false); var list = channelMediaSources.ToList(); diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index f3df6c7b0..f4ea7ef94 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -344,7 +344,7 @@ namespace MediaBrowser.Api // This must be the last filter if (!string.IsNullOrEmpty(request.AdjacentTo)) { - seasons = ItemsService.FilterForAdjacency(seasons, request.AdjacentTo) + seasons = UserViewBuilder.FilterForAdjacency(seasons, request.AdjacentTo) .Cast<Season>(); } @@ -434,7 +434,7 @@ namespace MediaBrowser.Api // This must be the last filter if (!string.IsNullOrEmpty(request.AdjacentTo)) { - episodes = ItemsService.FilterForAdjacency(episodes, request.AdjacentTo) + episodes = UserViewBuilder.FilterForAdjacency(episodes, request.AdjacentTo) .Cast<Episode>(); } diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 15a2d5c33..458cf8bae 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -188,7 +188,7 @@ namespace MediaBrowser.Api.UserLibrary var imageTypes = request.GetImageTypes().ToList(); if (imageTypes.Count > 0) { - items = items.Where(item => imageTypes.Any(imageType => ItemsService.HasImage(item, imageType))); + items = items.Where(item => imageTypes.Any(item.HasImage)); } var filters = request.GetFilters().ToList(); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 011864d35..897a68a65 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -243,6 +243,43 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? CollapseBoxSetItems { get; set; } + + public string[] GetAllGenres() + { + return (AllGenres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetGenres() + { + return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetStudios() + { + return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetPersonTypes() + { + return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + public int[] GetYears() + { + return (Years ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); + } + + public IEnumerable<VideoType> GetVideoTypes() + { + var val = VideoTypes; + + if (string.IsNullOrEmpty(val)) + { + return new VideoType[] { }; + } + + return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)); + } } /// <summary> @@ -331,21 +368,21 @@ namespace MediaBrowser.Api.UserLibrary items = ApplyFilter(items, filter, user, _userDataRepository); } - items = FilterVirtualEpisodes(request, items, user); + items = UserViewBuilder.FilterVirtualEpisodes(items, + request.IsMissing, + request.IsVirtualUnaired, + request.IsUnaired); - if (CollapseBoxSetItems(request, parentItem, user)) - { - items = _collectionManager.CollapseItemsWithinBoxSets(items, user); - } + var internalQuery = GetItemsQuery(request, user); - items = ApplyPostCollectionCollapseFilters(request, items, user); + items = UserViewBuilder.CollapseBoxSetItemsIfNeeded(items, internalQuery, parentItem, user); items = _libraryManager.Sort(items, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending); // This must be the last filter if (!string.IsNullOrEmpty(request.AdjacentTo)) { - items = FilterForAdjacency(items, request.AdjacentTo); + items = UserViewBuilder.FilterForAdjacency(items, request.AdjacentTo); } var itemsArray = items.ToList(); @@ -363,33 +400,6 @@ namespace MediaBrowser.Api.UserLibrary }; } - private bool CollapseBoxSetItems(GetItems request, BaseItem parentItem, User user) - { - // Could end up stuck in a loop like this - if (parentItem is BoxSet) - { - return false; - } - - var param = request.CollapseBoxSetItems; - - if (!param.HasValue) - { - if (user != null && !user.Configuration.GroupMoviesIntoBoxSets) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.IncludeItemTypes) && - request.IncludeItemTypes.Split(',').Contains("Movie", StringComparer.OrdinalIgnoreCase)) - { - param = true; - } - } - - return param.HasValue && param.Value && AllowBoxSetCollapsing(request); - } - /// <summary> /// Gets the items to serialize. /// </summary> @@ -486,9 +496,48 @@ namespace MediaBrowser.Api.UserLibrary Filter = (i, u) => ApplyAdditionalFilters(request, i, u, true), Limit = request.Limit, - StartIndex = request.StartIndex + 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, + Genres = request.GetGenres(), + AllGenres = request.GetAllGenres(), + Studios = request.GetStudios(), + Person = request.Person, + 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) @@ -610,156 +659,376 @@ namespace MediaBrowser.Api.UserLibrary return items; } - private IEnumerable<BaseItem> FilterVirtualEpisodes(GetItems request, IEnumerable<BaseItem> items, User user) + private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, bool isPreFiltered) { - items = FilterVirtualSeasons(request, items, user); - - if (request.IsMissing.HasValue) + if (!isPreFiltered) { - var val = request.IsMissing.Value; - items = items.Where(i => + var mediaTypes = request.GetMediaTypes(); + if (mediaTypes.Length > 0) { - var e = i as Episode; - if (e != null) + if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) { - return e.IsMissingEpisode == val; + return false; } - return true; - }); - } + } - if (request.IsUnaired.HasValue) - { - var val = request.IsUnaired.Value; - items = items.Where(i => + if (request.IsPlayed.HasValue) { - var e = i as Episode; - if (e != null) + var val = request.IsPlayed.Value; + if (i.IsPlayed(user) != val) { - return e.IsUnaired == val; + return false; } - return true; - }); - } + } - if (request.IsVirtualUnaired.HasValue) - { - var val = request.IsVirtualUnaired.Value; - items = items.Where(i => + // 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 e = i as Episode; - if (e != null) + var val = request.IsInBoxSet.Value; + if (i.Parents.OfType<BoxSet>().Any() != val) { - return e.IsVirtualUnaired == val; + return false; } - return true; - }); - } + } - return items; - } + // Filter by Video3DFormat + if (request.Is3D.HasValue) + { + var val = request.Is3D.Value; + var video = i as Video; - private IEnumerable<BaseItem> FilterVirtualSeasons(GetItems request, IEnumerable<BaseItem> items, User user) - { - if (request.IsMissing.HasValue && request.IsVirtualUnaired.HasValue) - { - var isMissing = request.IsMissing.Value; - var isVirtualUnaired = request.IsVirtualUnaired.Value; + if (video == null || val != video.Video3DFormat.HasValue) + { + return false; + } + } + + if (request.IsHD.HasValue) + { + var val = request.IsHD.Value; + var video = i as Video; + + 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 (!isMissing && !isVirtualUnaired) + if (request.HasImdbId.HasValue) { - return items.Where(i => + 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) { - var e = i as Season; - if (e != null) + 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) != 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 !e.IsMissingOrVirtualUnaired; + return false; } - return true; - }); + } + else + { + return false; + } } - } - if (request.IsMissing.HasValue) - { - var val = request.IsMissing.Value; - items = items.Where(i => + if (request.HasSubtitles.HasValue) { - var e = i as Season; - if (e != null) + var val = request.HasSubtitles.Value; + + var video = i as Video; + + if (video == null || val != video.HasSubtitles) { - return e.IsMissingSeason == val; + return false; } - return true; - }); - } + } - if (request.IsUnaired.HasValue) - { - var val = request.IsUnaired.Value; - items = items.Where(i => + if (request.HasParentalRating.HasValue) { - var e = i as Season; - if (e != null) + var val = request.HasParentalRating.Value; + + var rating = i.CustomRating; + + if (string.IsNullOrEmpty(rating)) { - return e.IsUnaired == val; + rating = i.OfficialRating; } - return true; - }); - } - if (request.IsVirtualUnaired.HasValue) - { - var val = request.IsVirtualUnaired.Value; - items = items.Where(i => + if (val) + { + if (string.IsNullOrEmpty(rating)) + { + return false; + } + } + else + { + if (!string.IsNullOrEmpty(rating)) + { + return false; + } + } + } + + if (request.HasTrailer.HasValue) { - var e = i as Season; - if (e != null) + var val = request.HasTrailer.Value; + var trailerCount = 0; + + var hasTrailers = i as IHasTrailers; + if (hasTrailers != null) { - return e.IsVirtualUnaired == val; + trailerCount = hasTrailers.LocalTrailerIds.Count; } - return true; - }); - } - return items; - } + var ok = val ? trailerCount > 0 : trailerCount == 0; - private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, bool isPreFiltered) - { - if (!isPreFiltered) - { - var mediaTypes = request.GetMediaTypes(); - if (mediaTypes.Length > 0) + if (!ok) + { + return false; + } + } + + if (request.HasThemeSong.HasValue) { - if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase))) + 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.IsPlayed.HasValue) + if (request.HasThemeVideo.HasValue) { - var val = request.IsPlayed.Value; - if (i.IsPlayed(user) != val) + 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; } } - // Exclude item types - var excluteItemTypes = request.GetExcludeItemTypes(); - if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + // Apply genre filter + var genres = request.GetGenres(); + if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) { return false; } - // Include item types - var includeItemTypes = request.GetIncludeItemTypes(); - if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) + // Apply genre filter + var allGenres = request.GetAllGenres(); + if (allGenres.Length > 0 && !allGenres.All(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))) { return false; } + + // Filter by VideoType + if (!string.IsNullOrEmpty(request.VideoTypes)) + { + var types = request.VideoTypes.Split(','); + + var video = i as Video; + if (video == null || !types.Contains(video.VideoType.ToString(), StringComparer.OrdinalIgnoreCase)) + { + 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 year filter + var years = request.GetYears(); + if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) + { + 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) @@ -960,164 +1229,6 @@ namespace MediaBrowser.Api.UserLibrary } } - // Filter by Video3DFormat - if (request.Is3D.HasValue) - { - var val = request.Is3D.Value; - var video = i as Video; - - if (video == null || val != video.Video3DFormat.HasValue) - { - return false; - } - } - - // Filter by VideoType - if (!string.IsNullOrEmpty(request.VideoTypes)) - { - var types = request.VideoTypes.Split(','); - - var video = i as Video; - if (video == null || !types.Contains(video.VideoType.ToString(), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - - var imageTypes = request.GetImageTypes().ToList(); - if (imageTypes.Count > 0) - { - if (!(imageTypes.Any(imageType => HasImage(i, imageType)))) - { - return false; - } - } - - // Apply genre filter - if (!string.IsNullOrEmpty(request.Genres)) - { - var vals = request.Genres.Split('|'); - if (!(vals.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) - { - return false; - } - } - - // Apply genre filter - if (!string.IsNullOrEmpty(request.AllGenres)) - { - var vals = request.AllGenres.Split('|'); - if (!vals.All(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - } - - // Apply studio filter - if (!string.IsNullOrEmpty(request.Studios)) - { - var vals = request.Studios.Split('|'); - if (!vals.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - } - - // Apply year filter - if (!string.IsNullOrEmpty(request.Years)) - { - var vals = request.Years.Split(',').Select(int.Parse).ToList(); - if (!(i.ProductionYear.HasValue && vals.Contains(i.ProductionYear.Value))) - { - return false; - } - } - - // Apply person filter - if (!string.IsNullOrEmpty(request.Person)) - { - var personTypes = request.PersonTypes; - - if (string.IsNullOrEmpty(personTypes)) - { - if (!(i.People.Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase)))) - { - return false; - } - } - else - { - var types = personTypes.Split(','); - - 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.HasTrailer.HasValue) - { - var val = request.HasTrailer.Value; - var trailerCount = 0; - - var hasTrailers = i as IHasTrailers; - if (hasTrailers != null) - { - trailerCount = hasTrailers.LocalTrailerIds.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; - } - } - if (request.MinPlayers.HasValue) { var filterValue = request.MinPlayers.Value; @@ -1164,106 +1275,6 @@ namespace MediaBrowser.Api.UserLibrary } } - 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; - - var video = i as Video; - - 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.IsHD.HasValue) - { - var val = request.IsHD.Value; - var video = i as Video; - - if (video == null || val != video.IsHD) - { - return false; - } - } - - if (request.IsInBoxSet.HasValue) - { - var val = request.IsHD.Value; - if (i.Parents.OfType<BoxSet>().Any() != val) - { - 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.ParentIndexNumber.HasValue) { var filterValue = request.ParentIndexNumber.Value; @@ -1324,321 +1335,9 @@ namespace MediaBrowser.Api.UserLibrary } } - 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 (IsYearMismatched(i) != 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; - } - } - return true; } - private IEnumerable<BaseItem> ApplyPostCollectionCollapseFilters(GetItems request, IEnumerable<BaseItem> items, User user) - { - if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) - { - items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); - } - if (!string.IsNullOrEmpty(request.NameStartsWith)) - { - items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0); - } - - if (!string.IsNullOrEmpty(request.NameLessThan)) - { - items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1); - } - - return items; - } - - private bool IsYearMismatched(BaseItem item) - { - if (item.ProductionYear.HasValue) - { - var path = item.Path; - - if (!string.IsNullOrEmpty(path)) - { - int? yearInName; - string name; - NameParser.ParseName(Path.GetFileName(path), out name, out yearInName); - - // Go up a level if we didn't get a year - if (!yearInName.HasValue) - { - NameParser.ParseName(Path.GetFileName(Path.GetDirectoryName(path)), out name, out yearInName); - } - - if (yearInName.HasValue) - { - return yearInName.Value != item.ProductionYear.Value; - } - } - } - - return false; - } - - private bool AllowBoxSetCollapsing(GetItems request) - { - if (!string.IsNullOrWhiteSpace(request.Filters)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.AllGenres)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.Genres)) - { - return false; - } - - if (request.HasImdbId.HasValue) - { - return false; - } - - if (request.HasOfficialRating.HasValue) - { - return false; - } - - if (request.HasOverview.HasValue) - { - return false; - } - - if (request.HasParentalRating.HasValue) - { - return false; - } - - if (request.HasSpecialFeature.HasValue) - { - return false; - } - - if (request.HasSubtitles.HasValue) - { - return false; - } - - if (request.HasThemeSong.HasValue) - { - return false; - } - - if (request.HasThemeVideo.HasValue) - { - return false; - } - - if (request.HasTmdbId.HasValue) - { - return false; - } - - if (request.HasTrailer.HasValue) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.Ids)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.ImageTypes)) - { - return false; - } - - if (request.Is3D.HasValue) - { - return false; - } - - if (request.IsHD.HasValue) - { - return false; - } - - if (request.IsInBoxSet.HasValue) - { - return false; - } - - if (request.IsLocked.HasValue) - { - return false; - } - - if (request.IsPlaceHolder.HasValue) - { - return false; - } - - if (request.IsPlayed.HasValue) - { - return false; - } - - if (request.IsUnidentified.HasValue) - { - return false; - } - - if (request.IsYearMismatched.HasValue) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.Person)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.Studios)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.VideoTypes)) - { - return false; - } - - if (!string.IsNullOrWhiteSpace(request.Years)) - { - return false; - } - - return true; - } - - internal static IEnumerable<BaseItem> FilterForAdjacency(IEnumerable<BaseItem> items, string adjacentToId) - { - var list = items.ToList(); - - var adjacentToIdGuid = new Guid(adjacentToId); - var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid); - - var index = list.IndexOf(adjacentToItem); - - var previousId = Guid.Empty; - var nextId = Guid.Empty; - - if (index > 0) - { - previousId = list[index - 1].Id; - } - - if (index < list.Count - 1) - { - nextId = list[index + 1].Id; - } - - return list.Where(i => i.Id == previousId || i.Id == nextId || i.Id == adjacentToIdGuid); - } - - /// <summary> - /// Determines whether the specified item has image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="imageType">Type of the image.</param> - /// <returns><c>true</c> if the specified item has image; otherwise, <c>false</c>.</returns> - internal static bool HasImage(BaseItem item, ImageType imageType) - { - return item.HasImage(imageType); - } - /// <summary> /// Applies the paging. /// </summary> diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 6d505aecb..ede366dab 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -1,9 +1,11 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Linq; +using System.Threading; namespace MediaBrowser.Controller.Channels { @@ -69,5 +71,22 @@ namespace MediaBrowser.Controller.Channels { return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N")); } + + public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) + { + var list = base.GetMediaSources(enablePathSubstitution).ToList(); + + var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None) + .Result.ToList(); + + if (sources.Count > 0) + { + return sources; + } + + list.InsertRange(0, sources); + + return list; + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index e6fa45972..72e2b110a 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading; namespace MediaBrowser.Controller.Channels { @@ -90,7 +91,15 @@ namespace MediaBrowser.Controller.Channels { var list = base.GetMediaSources(enablePathSubstitution).ToList(); - list.InsertRange(0, ChannelManager.GetCachedChannelItemMediaSources(Id.ToString("N"))); + var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None) + .Result.ToList(); + + if (sources.Count > 0) + { + return sources; + } + + list.InsertRange(0, sources); return list; } diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index f4e985c7a..05015da37 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -110,19 +110,13 @@ namespace MediaBrowser.Controller.Channels Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, IProgress<double> progress, CancellationToken cancellationToken); /// <summary> - /// Gets the cached channel item media sources. - /// </summary> - /// <param name="id">The identifier.</param> - /// <returns>IEnumerable{MediaSourceInfo}.</returns> - IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(string id); - - /// <summary> /// Gets the channel item media sources. /// </summary> /// <param name="id">The identifier.</param> + /// <param name="includeDynamicSources">if set to <c>true</c> [include dynamic sources].</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns> - Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, CancellationToken cancellationToken); + Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken); /// <summary> /// Gets the channel folder. diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 4a4ff17a0..93b71ae36 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -251,6 +252,7 @@ namespace MediaBrowser.Controller.Entities public static IUserDataManager UserDataManager { get; set; } public static ILiveTvManager LiveTvManager { get; set; } public static IChannelManager ChannelManager { get; set; } + public static ICollectionManager CollectionManager { get; set; } /// <summary> /// Returns a <see cref="System.String" /> that represents this instance. diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bfec0c26e..4614f2f8a 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -785,7 +785,7 @@ namespace MediaBrowser.Controller.Entities protected QueryResult<BaseItem> SortAndFilter(IEnumerable<BaseItem> items, InternalItemsQuery query) { - return UserViewBuilder.SortAndFilter(items, null, query, LibraryManager, UserDataManager); + return UserViewBuilder.SortAndFilter(items, this, null, query, LibraryManager, UserDataManager); } /// <summary> diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 91cb43291..1faa6c391 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -29,6 +29,46 @@ namespace MediaBrowser.Controller.Entities public string[] MediaTypes { get; set; } public string[] IncludeItemTypes { get; set; } public string[] ExcludeItemTypes { get; set; } + public string[] Genres { get; set; } + public string[] AllGenres { get; set; } + + public bool? IsMissing { get; set; } + public bool? IsUnaired { get; set; } + public bool? IsVirtualUnaired { get; set; } + public bool? CollapseBoxSetItems { get; set; } + + public string NameStartsWithOrGreater { get; set; } + public string NameStartsWith { get; set; } + public string NameLessThan { get; set; } + + public string Person { get; set; } + public string AdjacentTo { get; set; } + public string[] PersonTypes { get; set; } + + public bool? Is3D { get; set; } + public bool? IsHD { get; set; } + public bool? IsInBoxSet { get; set; } + public bool? IsLocked { get; set; } + public bool? IsUnidentified { get; set; } + public bool? IsPlaceHolder { get; set; } + public bool? IsYearMismatched { get; set; } + + public bool? HasImdbId { get; set; } + public bool? HasOverview { get; set; } + public bool? HasTmdbId { get; set; } + public bool? HasOfficialRating { get; set; } + public bool? HasTvdbId { get; set; } + public bool? HasThemeSong { get; set; } + public bool? HasThemeVideo { get; set; } + public bool? HasSubtitles { get; set; } + public bool? HasSpecialFeature { get; set; } + public bool? HasTrailer { get; set; } + public bool? HasParentalRating { get; set; } + + public string[] Studios { get; set; } + public ImageType[] ImageTypes { get; set; } + public VideoType[] VideoTypes { get; set; } + public int[] Years { get; set; } public InternalItemsQuery() { @@ -36,6 +76,13 @@ namespace MediaBrowser.Controller.Entities MediaTypes = new string[] { }; IncludeItemTypes = new string[] { }; ExcludeItemTypes = new string[] { }; + AllGenres = new string[] { }; + Genres = new string[] { }; + Studios = new string[] { }; + ImageTypes = new ImageType[] { }; + VideoTypes = new VideoType[] { }; + Years = new int[] { }; + PersonTypes = new string[] { }; } } } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 043c69e27..bcbaa967a 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) { - return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager) + return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, CollectionManager) .GetUserItems(this, ViewType, query); } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 8efa1b6bb..bf6cac87e 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1,9 +1,12 @@ -using MediaBrowser.Controller.Channels; +using System.IO; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.TV; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; @@ -27,8 +30,9 @@ namespace MediaBrowser.Controller.Entities private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; private readonly ITVSeriesManager _tvSeriesManager; + private readonly ICollectionManager _collectionManager; - public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager) + public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, ICollectionManager collectionManager) { _userViewManager = userViewManager; _liveTvManager = liveTvManager; @@ -37,6 +41,7 @@ namespace MediaBrowser.Controller.Entities _logger = logger; _userDataManager = userDataManager; _tvSeriesManager = tvSeriesManager; + _collectionManager = collectionManager; } public async Task<QueryResult<BaseItem>> GetUserItems(Folder parent, string viewType, InternalItemsQuery query) @@ -102,17 +107,17 @@ namespace MediaBrowser.Controller.Entities { var result = await GetLiveTvFolders(user).ConfigureAwait(false); - return GetResult(result, query); + return GetResult(result, parent, query); } case CollectionType.Folders: - return GetResult(user.RootFolder.GetChildren(user, true), query); + return GetResult(user.RootFolder.GetChildren(user, true), parent, query); case CollectionType.Games: return await GetGameView(user, parent, query).ConfigureAwait(false); case CollectionType.BoxSets: - return GetResult(GetMediaFolders(user).SelectMany(i => i.GetRecursiveChildren(user)).OfType<BoxSet>(), query); + return GetResult(GetMediaFolders(user).SelectMany(i => i.GetRecursiveChildren(user)).OfType<BoxSet>(), parent, query); case CollectionType.TvShows: return await GetTvView(parent, user, query).ConfigureAwait(false); @@ -205,7 +210,7 @@ namespace MediaBrowser.Controller.Entities return GetFavoriteSongs(parent, user, query); default: - return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), query); + return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), parent, query); } } @@ -218,7 +223,7 @@ namespace MediaBrowser.Controller.Entities { if (query.Recursive) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }), parent, query); } var list = new List<BaseItem>(); @@ -233,7 +238,7 @@ namespace MediaBrowser.Controller.Entities //list.Add(await GetUserView(CollectionType.MusicGenres, user, "5", parent).ConfigureAwait(false)); list.Add(await GetUserView(category, CollectionType.MusicFavorites, user, "6", parent).ConfigureAwait(false)); - return GetResult(list, query); + return GetResult(list, parent, query); } private async Task<QueryResult<BaseItem>> GetMusicFavorites(Folder parent, User user, InternalItemsQuery query) @@ -246,7 +251,7 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(category, CollectionType.MusicFavoriteArtists, user, "1", parent).ConfigureAwait(false)); list.Add(await GetUserView(category, CollectionType.MusicFavoriteSongs, user, "2", parent).ConfigureAwait(false)); - return GetResult(list, query); + return GetResult(list, parent, query); } private QueryResult<BaseItem> GetMusicAlbumArtists(Folder parent, User user, InternalItemsQuery query) @@ -270,7 +275,7 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null); - return GetResult(artists, query); + return GetResult(artists, parent, query); } private QueryResult<BaseItem> GetMusicArtists(Folder parent, User user, InternalItemsQuery query) @@ -294,7 +299,7 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null); - return GetResult(artists, query); + return GetResult(artists, parent, query); } private QueryResult<BaseItem> GetFavoriteArtists(Folder parent, User user, InternalItemsQuery query) @@ -318,17 +323,17 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null && _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite); - return GetResult(artists, query); + return GetResult(artists, parent, query); } private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicAlbum), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicAlbum), parent, query); } private QueryResult<BaseItem> GetMusicSongs(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is Audio.Audio), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is Audio.Audio), parent, query); } private QueryResult<BaseItem> GetMusicLatest(Folder parent, User user, InternalItemsQuery query) @@ -336,14 +341,14 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicVideo || i is Audio.Audio), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicVideo || i is Audio.Audio), parent, GetSpecialItemsLimit(), query); } private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query) { if (query.Recursive) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie || i is BoxSet), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie || i is BoxSet), parent, query); } var list = new List<BaseItem>(); @@ -357,52 +362,52 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(category, CollectionType.MovieFavorites, user, "4", parent).ConfigureAwait(false)); //list.Add(await GetUserView(CollectionType.MovieGenres, user, "5", parent).ConfigureAwait(false)); - return GetResult(list, query); + return GetResult(list, parent, query); } private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, query); } private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series), parent, query); } private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Episode), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Episode), parent, query); } private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is Audio.Audio), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is Audio.Audio), parent, query); } private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is MusicAlbum), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is MusicAlbum), parent, query); } private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, query); } private QueryResult<BaseItem> GetMovieCollections(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is BoxSet), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is BoxSet), parent, query); } private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query) @@ -410,7 +415,7 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query) @@ -419,7 +424,7 @@ namespace MediaBrowser.Controller.Entities query.SortOrder = SortOrder.Descending; query.IsResumable = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query) @@ -444,14 +449,14 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null); - return GetResult(genres, query); + return GetResult(genres, parent, query); } private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query) { if (query.Recursive) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series || i is Season || i is Episode), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series || i is Season || i is Episode), parent, query); } var list = new List<BaseItem>(); @@ -466,14 +471,14 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(category, CollectionType.TvFavoriteEpisodes, user, "5", parent).ConfigureAwait(false)); //list.Add(await GetUserView(CollectionType.TvGenres, user, "5", parent).ConfigureAwait(false)); - return GetResult(list, query); + return GetResult(list, parent, query); } private async Task<QueryResult<BaseItem>> GetGameView(User user, Folder parent, InternalItemsQuery query) { if (query.Recursive) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }), parent, query); } var list = new List<BaseItem>(); @@ -486,7 +491,7 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(category, CollectionType.GameSystems, user, "3", parent).ConfigureAwait(false)); //list.Add(await GetUserView(CollectionType.GameGenres, user, "4", parent).ConfigureAwait(false)); - return GetResult(list, query); + return GetResult(list, parent, query); } private QueryResult<BaseItem> GetLatestGames(Folder parent, User user, InternalItemsQuery query) @@ -494,7 +499,7 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query) @@ -503,14 +508,14 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetFavoriteGames(Folder parent, User user, InternalItemsQuery query) { query.IsFavorite = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<Game>(), parent, query); } private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query) @@ -518,7 +523,7 @@ namespace MediaBrowser.Controller.Entities query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; query.SortOrder = SortOrder.Descending; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Episode>(), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Episode>(), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query) @@ -542,12 +547,12 @@ namespace MediaBrowser.Controller.Entities query.SortOrder = SortOrder.Descending; query.IsResumable = true; - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Episode>(), GetSpecialItemsLimit(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Episode>(), parent, GetSpecialItemsLimit(), query); } private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Series>(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Series>(), parent, query); } private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query) @@ -572,12 +577,12 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null); - return GetResult(genres, query); + return GetResult(genres, parent, query); } private QueryResult<BaseItem> GetGameSystems(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<GameSystem>(), query); + return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType<GameSystem>(), parent, query); } private QueryResult<BaseItem> GetGameGenres(Folder parent, User user, InternalItemsQuery query) @@ -602,7 +607,7 @@ namespace MediaBrowser.Controller.Entities }) .Where(i => i != null); - return GetResult(genres, query); + return GetResult(genres, parent, query); } private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result) @@ -616,21 +621,24 @@ namespace MediaBrowser.Controller.Entities } private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items, + BaseItem parentItem, InternalItemsQuery query) where T : BaseItem { - return GetResult(items, null, query); + return GetResult(items, parentItem, null, query); } private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items, + BaseItem parentItem, int? totalRecordLimit, InternalItemsQuery query) where T : BaseItem { - return SortAndFilter(items, totalRecordLimit, query, _libraryManager, _userDataManager); + return SortAndFilter(items, parentItem, totalRecordLimit, query, _libraryManager, _userDataManager); } public static QueryResult<BaseItem> SortAndFilter(IEnumerable<BaseItem> items, + BaseItem parentItem, int? totalRecordLimit, InternalItemsQuery query, ILibraryManager libraryManager, @@ -640,9 +648,361 @@ namespace MediaBrowser.Controller.Entities items = items.Where(i => Filter(i, user, query, userDataManager)); + items = FilterVirtualEpisodes(items, + query.IsMissing, + query.IsVirtualUnaired, + query.IsUnaired); + + items = CollapseBoxSetItemsIfNeeded(items, query, parentItem, user); + + // This must be the last filter + if (!string.IsNullOrEmpty(query.AdjacentTo)) + { + items = FilterForAdjacency(items, query.AdjacentTo); + } + return Sort(items, totalRecordLimit, query, libraryManager); } + public static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items, + InternalItemsQuery query, + BaseItem parentItem, + User user) + { + if (CollapseBoxSetItems(query, parentItem, user)) + { + items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user); + } + + items = ApplyPostCollectionCollapseFilters(query, items, user); + + return items; + } + + private static IEnumerable<BaseItem> ApplyPostCollectionCollapseFilters(InternalItemsQuery request, + IEnumerable<BaseItem> items, + User user) + { + if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) + { + items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); + } + if (!string.IsNullOrEmpty(request.NameStartsWith)) + { + items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0); + } + + if (!string.IsNullOrEmpty(request.NameLessThan)) + { + items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1); + } + + return items; + } + + private static bool CollapseBoxSetItems(InternalItemsQuery query, + BaseItem parentItem, + User user) + { + // Could end up stuck in a loop like this + if (parentItem is BoxSet) + { + return false; + } + + var param = query.CollapseBoxSetItems; + + if (!param.HasValue) + { + if (user != null && !user.Configuration.GroupMoviesIntoBoxSets) + { + return false; + } + + if (query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase)) + { + param = true; + } + } + + return param.HasValue && param.Value && AllowBoxSetCollapsing(query); + } + + private static bool AllowBoxSetCollapsing(InternalItemsQuery request) + { + if (request.IsFavorite.HasValue) + { + return false; + } + if (request.IsFavoriteOrLiked.HasValue) + { + return false; + } + if (request.IsLiked.HasValue) + { + return false; + } + if (request.IsPlayed.HasValue) + { + return false; + } + if (request.IsResumable.HasValue) + { + return false; + } + if (request.IsFolder.HasValue) + { + return false; + } + + if (request.AllGenres.Length > 0) + { + return false; + } + + if (request.Genres.Length > 0) + { + return false; + } + + if (request.HasImdbId.HasValue) + { + return false; + } + + if (request.HasOfficialRating.HasValue) + { + return false; + } + + if (request.HasOverview.HasValue) + { + return false; + } + + if (request.HasParentalRating.HasValue) + { + return false; + } + + if (request.HasSpecialFeature.HasValue) + { + return false; + } + + if (request.HasSubtitles.HasValue) + { + return false; + } + + if (request.HasThemeSong.HasValue) + { + return false; + } + + if (request.HasThemeVideo.HasValue) + { + return false; + } + + if (request.HasTmdbId.HasValue) + { + return false; + } + + if (request.HasTrailer.HasValue) + { + return false; + } + + if (request.ImageTypes.Length > 0) + { + return false; + } + + if (request.Is3D.HasValue) + { + return false; + } + + if (request.IsHD.HasValue) + { + return false; + } + + if (request.IsInBoxSet.HasValue) + { + return false; + } + + if (request.IsLocked.HasValue) + { + return false; + } + + if (request.IsPlaceHolder.HasValue) + { + return false; + } + + if (request.IsPlayed.HasValue) + { + return false; + } + + if (request.IsUnidentified.HasValue) + { + return false; + } + + if (request.IsYearMismatched.HasValue) + { + return false; + } + + if (!string.IsNullOrWhiteSpace(request.Person)) + { + return false; + } + + if (request.Studios.Length > 0) + { + return false; + } + + if (request.VideoTypes.Length > 0) + { + return false; + } + + if (request.Years.Length > 0) + { + return false; + } + + return true; + } + + public static IEnumerable<BaseItem> FilterVirtualEpisodes( + IEnumerable<BaseItem> items, + bool? isMissing, + bool? isVirtualUnaired, + bool? isUnaired) + { + items = FilterVirtualSeasons(items, isMissing, isVirtualUnaired, isUnaired); + + if (isMissing.HasValue) + { + var val = isMissing.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsMissingEpisode == val; + } + return true; + }); + } + + if (isUnaired.HasValue) + { + var val = isUnaired.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsUnaired == val; + } + return true; + }); + } + + if (isVirtualUnaired.HasValue) + { + var val = isVirtualUnaired.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsVirtualUnaired == val; + } + return true; + }); + } + + return items; + } + + private static IEnumerable<BaseItem> FilterVirtualSeasons( + IEnumerable<BaseItem> items, + bool? isMissing, + bool? isVirtualUnaired, + bool? isUnaired) + { + if (isMissing.HasValue && isVirtualUnaired.HasValue) + { + if (!isMissing.Value && !isVirtualUnaired.Value) + { + return items.Where(i => + { + var e = i as Season; + if (e != null) + { + return !e.IsMissingOrVirtualUnaired; + } + return true; + }); + } + } + + if (isMissing.HasValue) + { + var val = isMissing.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsMissingSeason == val; + } + return true; + }); + } + + if (isUnaired.HasValue) + { + var val = isUnaired.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsUnaired == val; + } + return true; + }); + } + + if (isVirtualUnaired.HasValue) + { + var val = isVirtualUnaired.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsVirtualUnaired == val; + } + return true; + }); + } + + return items; + } + public static QueryResult<BaseItem> Sort(IEnumerable<BaseItem> items, int? totalRecordLimit, InternalItemsQuery query, @@ -692,7 +1052,7 @@ namespace MediaBrowser.Controller.Entities { return false; } - + if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder) { return false; @@ -755,6 +1115,334 @@ namespace MediaBrowser.Controller.Entities } } + if (query.IsInBoxSet.HasValue) + { + var val = query.IsInBoxSet.Value; + if (item.Parents.OfType<BoxSet>().Any() != val) + { + return false; + } + } + + // Filter by Video3DFormat + if (query.Is3D.HasValue) + { + var val = query.Is3D.Value; + var video = item as Video; + + if (video == null || val != video.Video3DFormat.HasValue) + { + return false; + } + } + + if (query.IsHD.HasValue) + { + var val = query.IsHD.Value; + var video = item as Video; + + if (video == null || val != video.IsHD) + { + return false; + } + } + + if (query.IsUnidentified.HasValue) + { + var val = query.IsUnidentified.Value; + if (item.IsUnidentified != val) + { + return false; + } + } + + if (query.IsLocked.HasValue) + { + var val = query.IsLocked.Value; + if (item.IsLocked != val) + { + return false; + } + } + + if (query.HasOverview.HasValue) + { + var filterValue = query.HasOverview.Value; + + var hasValue = !string.IsNullOrEmpty(item.Overview); + + if (hasValue != filterValue) + { + return false; + } + } + + if (query.HasImdbId.HasValue) + { + var filterValue = query.HasImdbId.Value; + + var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Imdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (query.HasTmdbId.HasValue) + { + var filterValue = query.HasTmdbId.Value; + + var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (query.HasTvdbId.HasValue) + { + var filterValue = query.HasTvdbId.Value; + + var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tvdb)); + + if (hasValue != filterValue) + { + return false; + } + } + + if (query.IsYearMismatched.HasValue) + { + var filterValue = query.IsYearMismatched.Value; + + if (IsYearMismatched(item) != filterValue) + { + return false; + } + } + + if (query.HasOfficialRating.HasValue) + { + var filterValue = query.HasOfficialRating.Value; + + var hasValue = !string.IsNullOrEmpty(item.OfficialRating); + + if (hasValue != filterValue) + { + return false; + } + } + + if (query.IsPlaceHolder.HasValue) + { + var filterValue = query.IsPlaceHolder.Value; + + var isPlaceHolder = false; + + var hasPlaceHolder = item as ISupportsPlaceHolders; + + if (hasPlaceHolder != null) + { + isPlaceHolder = hasPlaceHolder.IsPlaceHolder; + } + + if (isPlaceHolder != filterValue) + { + return false; + } + } + + if (query.HasSpecialFeature.HasValue) + { + var filterValue = query.HasSpecialFeature.Value; + + var movie = item as IHasSpecialFeatures; + + if (movie != null) + { + var ok = filterValue + ? movie.SpecialFeatureIds.Count > 0 + : movie.SpecialFeatureIds.Count == 0; + + if (!ok) + { + return false; + } + } + else + { + return false; + } + } + + if (query.HasSubtitles.HasValue) + { + var val = query.HasSubtitles.Value; + + var video = item as Video; + + if (video == null || val != video.HasSubtitles) + { + return false; + } + } + + if (query.HasParentalRating.HasValue) + { + var val = query.HasParentalRating.Value; + + var rating = item.CustomRating; + + if (string.IsNullOrEmpty(rating)) + { + rating = item.OfficialRating; + } + + if (val) + { + if (string.IsNullOrEmpty(rating)) + { + return false; + } + } + else + { + if (!string.IsNullOrEmpty(rating)) + { + return false; + } + } + } + + if (query.HasTrailer.HasValue) + { + var val = query.HasTrailer.Value; + var trailerCount = 0; + + var hasTrailers = item as IHasTrailers; + if (hasTrailers != null) + { + trailerCount = hasTrailers.LocalTrailerIds.Count; + } + + var ok = val ? trailerCount > 0 : trailerCount == 0; + + if (!ok) + { + return false; + } + } + + if (query.HasThemeSong.HasValue) + { + var filterValue = query.HasThemeSong.Value; + + var themeCount = 0; + var iHasThemeMedia = item as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeSongIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + if (query.HasThemeVideo.HasValue) + { + var filterValue = query.HasThemeVideo.Value; + + var themeCount = 0; + var iHasThemeMedia = item as IHasThemeMedia; + + if (iHasThemeMedia != null) + { + themeCount = iHasThemeMedia.ThemeVideoIds.Count; + } + var ok = filterValue ? themeCount > 0 : themeCount == 0; + + if (!ok) + { + return false; + } + } + + // Apply genre filter + if (query.Genres.Length > 0 && !(query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))) + { + return false; + } + + // Apply genre filter + if (query.AllGenres.Length > 0 && !query.AllGenres.All(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + + // Filter by VideoType + if (query.VideoTypes.Length > 0) + { + var video = item as Video; + if (video == null || !query.VideoTypes.Contains(video.VideoType)) + { + return false; + } + } + + if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage)) + { + return false; + } + + // Apply studio filter + if (query.Studios.Length > 0 && !query.Studios.Any(v => item.Studios.Contains(v, StringComparer.OrdinalIgnoreCase))) + { + return false; + } + + // Apply year filter + if (query.Years.Length > 0) + { + if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value))) + { + return false; + } + } + + // Apply person filter + if (!string.IsNullOrEmpty(query.Person)) + { + var personTypes = query.PersonTypes; + + if (personTypes.Length == 0) + { + if (!(item.People.Any(p => string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase)))) + { + return false; + } + } + else + { + var types = personTypes; + + var ok = new[] { item }.Any(i => + i.People != null && + i.People.Any(p => + p.Name.Equals(query.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase)))); + + if (!ok) + { + return false; + } + } + } + return true; } @@ -768,7 +1456,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !excludeFolderIds.Contains(i.Id) && !UserView.IsExcludedFromGrouping(i)); } - private IEnumerable<Folder> GetMediaFolders(User user, string[] viewTypes) + private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes) { return GetMediaFolders(user) .Where(i => @@ -824,5 +1512,58 @@ namespace MediaBrowser.Controller.Entities return view; } + + public static bool IsYearMismatched(BaseItem item) + { + if (item.ProductionYear.HasValue) + { + var path = item.Path; + + if (!string.IsNullOrEmpty(path)) + { + int? yearInName; + string name; + NameParser.ParseName(Path.GetFileName(path), out name, out yearInName); + + // Go up a level if we didn't get a year + if (!yearInName.HasValue) + { + NameParser.ParseName(Path.GetFileName(Path.GetDirectoryName(path)), out name, out yearInName); + } + + if (yearInName.HasValue) + { + return yearInName.Value != item.ProductionYear.Value; + } + } + } + + return false; + } + + public static IEnumerable<BaseItem> FilterForAdjacency(IEnumerable<BaseItem> items, string adjacentToId) + { + var list = items.ToList(); + + var adjacentToIdGuid = new Guid(adjacentToId); + var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid); + + var index = list.IndexOf(adjacentToItem); + + var previousId = Guid.Empty; + var nextId = Guid.Empty; + + if (index > 0) + { + previousId = list[index - 1].Id; + } + + if (index < list.Count - 1) + { + nextId = list[index + 1].Id; + } + + return list.Where(i => i.Id == previousId || i.Id == nextId || i.Id == adjacentToIdGuid); + } } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 550bb3cfb..4d6b301d1 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -91,6 +91,13 @@ namespace MediaBrowser.Model.Dlna throw new ArgumentNullException(baseUrl); } + if (IsDirectStream && MediaSource != null && MediaSource.Protocol == MediaProtocol.Http) + { + if (MediaSource.RequiredHttpHeaders.Count == 0) + { + } + } + string dlnaCommand = BuildDlnaParam(this); string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 40eb38669..cd0f14200 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -273,13 +273,17 @@ namespace MediaBrowser.Providers.Movies languages.Add("en"); } + var firstLetter = string.IsNullOrWhiteSpace(preferredLanguage) + ? string.Empty + : preferredLanguage.Substring(0, 1); + var allLanguages = localization.GetCultures() .Select(i => i.TwoLetterISOLanguageName) .Distinct(StringComparer.OrdinalIgnoreCase) - .Where(i => !languages.Contains(i, StringComparer.OrdinalIgnoreCase)) + .Where(i => !languages.Contains(i, StringComparer.OrdinalIgnoreCase) && i.StartsWith(firstLetter, StringComparison.OrdinalIgnoreCase)) .ToList(); - languages.AddRange(allLanguages); + //languages.AddRange(allLanguages); return string.Join(",", languages.ToArray()); } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs index c8aa90b99..af5248608 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs @@ -212,12 +212,10 @@ namespace MediaBrowser.Server.Implementations.Channels } var itemId = item.Id.ToString("N"); - var sources = await _manager.GetChannelItemMediaSources(itemId, cancellationToken) + var sources = await _manager.GetChannelItemMediaSources(itemId, false, cancellationToken) .ConfigureAwait(false); - var list = sources.ToList(); - - var cachedVersions = list.Where(i => i.Protocol == MediaProtocol.File).ToList(); + var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList(); if (cachedVersions.Count > 0) { @@ -225,13 +223,6 @@ namespace MediaBrowser.Server.Implementations.Channels return; } - var source = list.FirstOrDefault(i => i.Protocol == MediaProtocol.Http); - - if (source == null) - { - return; - } - var channelItem = (IChannelMediaItem)item; var destination = Path.Combine(path, channelItem.ChannelId, itemId); diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index 8fb6bb434..b9d218215 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -244,7 +244,7 @@ namespace MediaBrowser.Server.Implementations.Channels return item; } - public async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, CancellationToken cancellationToken) + public async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken) { var item = (IChannelMediaItem)_libraryManager.GetItemById(id); @@ -255,7 +255,7 @@ namespace MediaBrowser.Server.Implementations.Channels IEnumerable<ChannelMediaInfo> results; - if (requiresCallback != null) + if (requiresCallback != null && includeDynamicSources) { results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) .ConfigureAwait(false); @@ -374,6 +374,18 @@ namespace MediaBrowser.Server.Implementations.Channels Id = id }; + var bitrate = (info.AudioBitrate ?? 0) + (info.VideoBitrate ?? 0); + + if (bitrate > 0) + { + source.Bitrate = bitrate; + } + + if (item is ChannelVideoItem && info.Protocol != MediaProtocol.Rtmp) + { + + } + return source; } @@ -1447,7 +1459,7 @@ namespace MediaBrowser.Server.Implementations.Channels IProgress<double> progress, CancellationToken cancellationToken) { var itemId = item.Id.ToString("N"); - var sources = await GetChannelItemMediaSources(itemId, cancellationToken) + var sources = await GetChannelItemMediaSources(itemId, true, cancellationToken) .ConfigureAwait(false); var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList(); diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json index 15f4099ec..070d7a966 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json @@ -519,7 +519,7 @@ "MediaInfoLongitude": "L\u00e4nge", "MediaInfoShutterSpeed": "Verschlusszeit", "MediaInfoSoftware": "Software", - "HeaderIfYouLikeCheckTheseOut": "Wenn du {0} magst, schau dir einmal diese an...", + "HeaderIfYouLikeCheckTheseOut": "Wenn du {0} magst, schau dir einmal das an...", "HeaderPlotKeywords": "Handlungsstichworte", "HeaderMovies": "Filme", "HeaderAlbums": "Alben", diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json index 5384b76cf..e3e3b4187 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json @@ -469,7 +469,7 @@ "LabelResumePoint": "Point de reprise", "ValueOneMovie": "1 Film", "ValueMovieCount": "{0} films", - "ValueOneTrailer": "1 Bande Annonce", + "ValueOneTrailer": "1 bande-annonce", "ValueTrailerCount": "{0} bandes-annonces", "ValueOneSeries": "1 S\u00e9rie", "ValueSeriesCount": "{0} series", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/fr.json b/MediaBrowser.Server.Implementations/Localization/Server/fr.json index f40f8acc9..4ae3a5d0d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json @@ -1020,7 +1020,7 @@ "LabelChannelDownloadPathHelp": "Sp\u00e9cifiez un chemin de t\u00e9l\u00e9chargements personnalis\u00e9 si besoin. Laissez vide pour t\u00e9l\u00e9charger dans un r\u00e9pertoire interne du programme.", "LabelChannelDownloadAge": "Supprimer le contenu apr\u00e8s : (jours)", "LabelChannelDownloadAgeHelp": "Le contenu t\u00e9l\u00e9charg\u00e9 plus vieux sera supprim\u00e9. Par contre, il sera toujours disponible par flux Internet (en ligne).", - "ChannelSettingsFormHelp": "Installer des cha\u00eenes comme \"Trailers\" and \"Vimeo\" par le catalogue de Plugins.", + "ChannelSettingsFormHelp": "Installer des cha\u00eenes comme \"Trailers\" et \"Vimeo\" dans le catalogue des plugins.", "LabelSelectCollection": "S\u00e9lectionner la collection :", "ButtonOptions": "Options", "ViewTypeMovies": "Films", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/hr.json b/MediaBrowser.Server.Implementations/Localization/Server/hr.json index 7481a1964..949e2a5e7 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/hr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/hr.json @@ -845,43 +845,43 @@ "OptionSpecialFeatures": "Specijalne opcije", "HeaderCollections": "Kolekcije", "LabelProfileCodecsHelp": "Odvojeno sa to\u010dka-zrezom. Ovo mo\u017ee ostaviti prazno kao bi bilo postavljeno za sve codecs.", - "LabelProfileContainersHelp": "Separated by comma. This can be left empty to apply to all containers.", - "HeaderResponseProfile": "Response Profile", - "LabelType": "Type:", + "LabelProfileContainersHelp": "Odvojeno sa to\u010dka-zrezom. Ovo mo\u017ee ostaviti prazno kao bi bilo postavljeno za sve spremnike.", + "HeaderResponseProfile": "Profil odziva", + "LabelType": "Tip:", "LabelPersonRole": "Role:", "LabelPersonRoleHelp": "Role is generally only applicable to actors.", - "LabelProfileContainer": "Container:", - "LabelProfileVideoCodecs": "Video codecs:", - "LabelProfileAudioCodecs": "Audio codecs:", - "LabelProfileCodecs": "Codecs:", - "HeaderDirectPlayProfile": "Direct Play Profile", - "HeaderTranscodingProfile": "Transcoding Profile", - "HeaderCodecProfile": "Codec Profile", - "HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct play.", - "HeaderContainerProfile": "Container Profile", - "HeaderContainerProfileHelp": "Container profiles indicate the limitations of a device when playing specific formats. If a limitation applies then the media will be transcoded, even if the format is configured for direct play.", + "LabelProfileContainer": "Spremnik:", + "LabelProfileVideoCodecs": "Video kodek:", + "LabelProfileAudioCodecs": "Audio kodek:", + "LabelProfileCodecs": "Kodeki:", + "HeaderDirectPlayProfile": "Profil za direktnu reprodukciju", + "HeaderTranscodingProfile": "Profil transkodiranja", + "HeaderCodecProfile": "Profil kodeka", + "HeaderCodecProfileHelp": "Profili kodeka definiraju ograni\u010denja kada ure\u0111aji izvode sadr\u017eaj u specifi\u010dnom kodeku. Ako se ograni\u010denja podudaraju tada \u0107e sadr\u017eaj biti transkodiran, iako je kodek konfiguriran za direktno izvo\u0111enje.", + "HeaderContainerProfile": "Profil spremnika", + "HeaderContainerProfileHelp": "Profil spremnika definira ograni\u010denja za ure\u0111aje kada izvode specifi\u010dne formate. Ako se ograni\u010denja podudaraju tada \u0107e sadr\u017eaj biti transkodiran, iako je format konfiguriran za direktno izvo\u0111enje.", "OptionProfileVideo": "Video", "OptionProfileAudio": "Audio", "OptionProfileVideoAudio": "Video Audio", - "OptionProfilePhoto": "Photo", - "LabelUserLibrary": "User library:", - "LabelUserLibraryHelp": "Select which user library to display to the device. Leave empty to inherit the default setting.", - "OptionPlainStorageFolders": "Display all folders as plain storage folders", - "OptionPlainStorageFoldersHelp": "If enabled, all folders are represented in DIDL as \"object.container.storageFolder\" instead of a more specific type, such as \"object.container.person.musicArtist\".", - "OptionPlainVideoItems": "Display all videos as plain video items", - "OptionPlainVideoItemsHelp": "If enabled, all videos are represented in DIDL as \"object.item.videoItem\" instead of a more specific type, such as \"object.item.videoItem.movie\".", - "LabelSupportedMediaTypes": "Supported Media Types:", - "TabIdentification": "Identification", + "OptionProfilePhoto": "Slika", + "LabelUserLibrary": "Korisni\u010dka biblioteka:", + "LabelUserLibraryHelp": "Odaberite koju korisni\u010dku biblioteku \u0107e te prikazati ure\u0111aju. Ostavite prazno ako \u017eelite preuzeti definirane postavke.", + "OptionPlainStorageFolders": "Prika\u017ei sve mape kako jednostavne mape za skladi\u0161tenje", + "OptionPlainStorageFoldersHelp": "Ako je omogu\u0107eno, sve mape se prezentiraju u DIDL-u kao \"objekt.spremnik.skladi\u0161naMapa\" umjesto vi\u0161e specijaliziranog tipa kao \"objekt.spremnik.osoba.glazbaIzvo\u0111a\u010d\".", + "OptionPlainVideoItems": "Prika\u017ei sav video kao jednostavne video stavke.", + "OptionPlainVideoItemsHelp": "Ako je omogu\u0107eno, sav video se prezentira u DIDL-u kao \"objekt.stavka.videoStavka\" umjesto vi\u0161e specijaliziranog tipa kao \"objekt.stavka.videoStavka.film\".", + "LabelSupportedMediaTypes": "Podr\u017eani tipovi medija:", + "TabIdentification": "Identifikacija", "HeaderIdentification": "Identification", - "TabDirectPlay": "Direct Play", - "TabContainers": "Containers", - "TabCodecs": "Codecs", - "TabResponses": "Responses", - "HeaderProfileInformation": "Profile Information", - "LabelEmbedAlbumArtDidl": "Embed album art in Didl", - "LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for obtaining album art. Others may fail to play with this option enabled.", - "LabelAlbumArtPN": "Album art PN:", - "LabelAlbumArtHelp": "PN used for album art, within the dlna:profileID attribute on upnp:albumArtURI. Some clients require a specific value, regardless of the size of the image.", + "TabDirectPlay": "Direktna reprodukcija", + "TabContainers": "Spremnik", + "TabCodecs": "Kodek", + "TabResponses": "Odazivi", + "HeaderProfileInformation": "Informacija profila", + "LabelEmbedAlbumArtDidl": "Ugradi grafike albuma u Didl", + "LabelEmbedAlbumArtDidlHelp": "Neki ure\u0111aji podr\u017eavaju ovu metodu za prikaz grafike albuma. Drugi bi mogli imati problema sa ovom opcijom uklju\u010denom.", + "LabelAlbumArtPN": "Grafika albuma PN:", + "LabelAlbumArtHelp": "PN se koristi za grafiku albuma sa dlna:profilID atributom na upnp:albumGrafikaURI. Neki klijenti zahtijevaju specifi\u010dnu vrijednost bez obzira na veli\u010dinu slike.", "LabelAlbumArtMaxWidth": "Album art max width:", "LabelAlbumArtMaxWidthHelp": "Max resolution of album art exposed via upnp:albumArtURI.", "LabelAlbumArtMaxHeight": "Album art max height:", diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index f08a7771b..17c5b108a 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -218,6 +218,7 @@ namespace MediaBrowser.ServerApplication private IAuthenticationRepository AuthenticationRepository { get; set; } private ISyncRepository SyncRepository { get; set; } private ITVSeriesManager TVSeriesManager { get; set; } + private ICollectionManager CollectionManager { get; set; } private readonly StartupOptions _startupOptions; private readonly string _remotePackageName; @@ -494,8 +495,8 @@ namespace MediaBrowser.ServerApplication var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient); RegisterSingleInstance<IConnectionManager>(connectionManager); - var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager")); - RegisterSingleInstance<ICollectionManager>(collectionManager); + CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager")); + RegisterSingleInstance(CollectionManager); var playlistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager); RegisterSingleInstance<IPlaylistManager>(playlistManager); @@ -700,6 +701,7 @@ namespace MediaBrowser.ServerApplication BaseItem.LiveTvManager = LiveTvManager; Folder.UserViewManager = UserViewManager; UserView.TVSeriesManager = TVSeriesManager; + BaseItem.CollectionManager = CollectionManager; } /// <summary> |
