Browse Source

sync updates

Luke Pulverenti 10 years ago
parent
commit
a1a56557ec
67 changed files with 568 additions and 432 deletions
  1. 23 11
      MediaBrowser.Api/BaseApiService.cs
  2. 8 7
      MediaBrowser.Api/GamesService.cs
  3. 1 1
      MediaBrowser.Api/ItemRefreshService.cs
  4. 1 1
      MediaBrowser.Api/ItemUpdateService.cs
  5. 41 40
      MediaBrowser.Api/Library/LibraryService.cs
  6. 5 6
      MediaBrowser.Api/Movies/MoviesService.cs
  7. 4 4
      MediaBrowser.Api/Music/AlbumsService.cs
  8. 5 3
      MediaBrowser.Api/SearchService.cs
  9. 5 5
      MediaBrowser.Api/SimilarItemsHelper.cs
  10. 10 9
      MediaBrowser.Api/TvShowsService.cs
  11. 40 26
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  12. 2 2
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  13. 37 26
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  14. 3 2
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  15. 18 7
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  16. 7 1
      MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
  17. 3 2
      MediaBrowser.Controller/Entities/BaseItem.cs
  18. 53 38
      MediaBrowser.Controller/Entities/Folder.cs
  19. 7 1
      MediaBrowser.Controller/Entities/GameGenre.cs
  20. 6 1
      MediaBrowser.Controller/Entities/Genre.cs
  21. 4 1
      MediaBrowser.Controller/Entities/IItemByName.cs
  22. 1 1
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  23. 3 11
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  24. 7 1
      MediaBrowser.Controller/Entities/Person.cs
  25. 7 1
      MediaBrowser.Controller/Entities/Studio.cs
  26. 3 3
      MediaBrowser.Controller/Entities/TV/Season.cs
  27. 4 12
      MediaBrowser.Controller/Entities/TV/Series.cs
  28. 6 3
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  29. 3 2
      MediaBrowser.Controller/Entities/UserView.cs
  30. 50 22
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  31. 8 1
      MediaBrowser.Controller/Entities/Year.cs
  32. 32 28
      MediaBrowser.Controller/Playlists/Playlist.cs
  33. 2 4
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  34. 7 3
      MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
  35. 2 3
      MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs
  36. 2 1
      MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs
  37. 3 1
      MediaBrowser.Providers/Music/AlbumMetadataService.cs
  38. 2 2
      MediaBrowser.Providers/Music/ArtistMetadataService.cs
  39. 5 3
      MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
  40. 2 2
      MediaBrowser.Providers/TV/DummySeasonProvider.cs
  41. 16 15
      MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
  42. 4 4
      MediaBrowser.Providers/TV/SeriesPostScanTask.cs
  43. 6 6
      MediaBrowser.Providers/TV/TvdbPrescanTask.cs
  44. 23 15
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  45. 2 1
      MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  46. 5 6
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  47. 19 21
      MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs
  48. 7 6
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  49. 3 4
      MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs
  50. 9 8
      MediaBrowser.Server.Implementations/Library/MusicManager.cs
  51. 4 5
      MediaBrowser.Server.Implementations/Library/SearchEngine.cs
  52. 4 3
      MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
  53. 1 1
      MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
  54. 1 1
      MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
  55. 1 1
      MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
  56. 1 1
      MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
  57. 1 1
      MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs
  58. 1 1
      MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
  59. 1 2
      MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
  60. 1 2
      MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
  61. 2 2
      MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
  62. 3 4
      MediaBrowser.Server.Implementations/Session/SessionManager.cs
  63. 3 7
      MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
  64. 9 7
      MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
  65. 6 6
      MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs
  66. 1 2
      MediaBrowser.XbmcMetadata/EntryPoint.cs
  67. 2 2
      MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs

+ 23 - 11
MediaBrowser.Api/BaseApiService.cs

@@ -174,7 +174,7 @@ namespace MediaBrowser.Api
             return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
         }
 
-        protected IEnumerable<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null)
+        protected IList<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
         {
             if (!string.IsNullOrEmpty(parentId))
             {
@@ -189,10 +189,13 @@ namespace MediaBrowser.Api
                         throw new ArgumentException("User not found");
                     }
 
-                    return folder.GetRecursiveChildren(user);
+                    return folder
+                        .GetRecursiveChildren(user, filter)
+                        .ToList();
                 }
 
-                return folder.GetRecursiveChildren();
+                return folder
+                    .GetRecursiveChildren(filter);
             }
             if (userId.HasValue)
             {
@@ -203,10 +206,16 @@ namespace MediaBrowser.Api
                     throw new ArgumentException("User not found");
                 }
 
-                return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user);
+                return userManager
+                    .GetUserById(userId.Value)
+                    .RootFolder
+                    .GetRecursiveChildren(user, filter)
+                    .ToList();
             }
 
-            return libraryManager.RootFolder.GetRecursiveChildren();
+            return libraryManager
+                .RootFolder
+                .GetRecursiveChildren(filter);
         }
 
         /// <summary>
@@ -222,8 +231,9 @@ namespace MediaBrowser.Api
                 return name;
             }
 
-            return libraryManager.RootFolder.RecursiveChildren
-                .OfType<Audio>()
+            return libraryManager.RootFolder
+                .GetRecursiveChildren(i => i is IHasArtist)
+                .Cast<IHasArtist>()
                 .SelectMany(i => i.AllArtists)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .FirstOrDefault(i =>
@@ -264,8 +274,8 @@ namespace MediaBrowser.Api
                 return name;
             }
 
-            return libraryManager.RootFolder.GetRecursiveChildren()
-                .OfType<Game>()
+            return libraryManager.RootFolder
+                .GetRecursiveChildren(i => i is Game)
                 .SelectMany(i => i.Genres)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .FirstOrDefault(i =>
@@ -287,7 +297,8 @@ namespace MediaBrowser.Api
                 return name;
             }
 
-            return libraryManager.RootFolder.GetRecursiveChildren()
+            return libraryManager.RootFolder
+                .GetRecursiveChildren()
                 .SelectMany(i => i.Studios)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .FirstOrDefault(i =>
@@ -309,7 +320,8 @@ namespace MediaBrowser.Api
                 return name;
             }
 
-            return libraryManager.RootFolder.GetRecursiveChildren()
+            return libraryManager.RootFolder
+                .GetRecursiveChildren()
                 .SelectMany(i => i.People)
                 .Select(i => i.Name)
                 .Distinct(StringComparer.OrdinalIgnoreCase)

+ 8 - 7
MediaBrowser.Api/GamesService.cs

@@ -102,8 +102,8 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         public object Get(GetGameSystemSummaries request)
         {
-            var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
-                .OfType<GameSystem>()
+            var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem)
+                .Cast<GameSystem>()
                 .ToList();
 
             var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId.Value);
@@ -119,9 +119,8 @@ namespace MediaBrowser.Api
 
         public object Get(GetPlayerIndex request)
         {
-            var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
-                .OfType<Game>()
-                .ToList();
+            var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game)
+                .Cast<Game>();
 
             var lookup = games
                 .ToLookup(i => i.PlayersSupported ?? -1)
@@ -150,9 +149,11 @@ namespace MediaBrowser.Api
                 DisplayName = system.Name
             };
 
-            var items = user == null ? system.RecursiveChildren : system.GetRecursiveChildren(user);
+            var items = user == null ? 
+                system.GetRecursiveChildren(i => i is Game) :
+                system.GetRecursiveChildren(user, i => i is Game);
 
-            var games = items.OfType<Game>().ToList();
+            var games = items.Cast<Game>().ToList();
 
             summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
 

+ 1 - 1
MediaBrowser.Api/ItemRefreshService.cs

@@ -51,7 +51,7 @@ namespace MediaBrowser.Api
             var cancellationToken = CancellationToken.None;
 
             var albums = _libraryManager.RootFolder
-                                        .RecursiveChildren
+                                        .GetRecursiveChildren()
                                         .OfType<MusicAlbum>()
                                         .Where(i => i.HasArtist(item.Name))
                                         .ToList();

+ 1 - 1
MediaBrowser.Api/ItemUpdateService.cs

@@ -215,7 +215,7 @@ namespace MediaBrowser.Api
             {
                 var folder = (Folder)item;
 
-                foreach (var child in folder.RecursiveChildren.ToList())
+                foreach (var child in folder.GetRecursiveChildren())
                 {
                     child.IsLocked = newLockData;
                     await child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);

+ 41 - 40
MediaBrowser.Api/Library/LibraryService.cs

@@ -392,52 +392,43 @@ namespace MediaBrowser.Api.Library
         /// <returns>System.Object.</returns>
         public object Get(GetItemCounts request)
         {
-            var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager)
-                .Where(i => i.LocationType != LocationType.Virtual)
-                .ToList();
-
-            var filteredItems = request.UserId.HasValue ? FilterItems(items, request, request.UserId.Value).ToList() : items;
-
-            var albums = filteredItems.OfType<MusicAlbum>().ToList();
-            var episodes = filteredItems.OfType<Episode>().ToList();
-            var games = filteredItems.OfType<Game>().ToList();
-            var movies = filteredItems.OfType<Movie>().ToList();
-            var musicVideos = filteredItems.OfType<MusicVideo>().ToList();
-            var boxsets = filteredItems.OfType<BoxSet>().ToList();
-            var books = filteredItems.OfType<Book>().ToList();
-            var songs = filteredItems.OfType<Audio>().ToList();
-            var series = filteredItems.OfType<Series>().ToList();
+            var filteredItems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i.LocationType != LocationType.Virtual && FilterItem(i, request, request.UserId));
 
             var counts = new ItemCounts
             {
-                AlbumCount = albums.Count,
-                EpisodeCount = episodes.Count,
-                GameCount = games.Count,
-                GameSystemCount = filteredItems.OfType<GameSystem>().Count(),
-                MovieCount = movies.Count,
-                SeriesCount = series.Count,
-                SongCount = songs.Count,
-                MusicVideoCount = musicVideos.Count,
-                BoxSetCount = boxsets.Count,
-                BookCount = books.Count,
-
-                UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList()
+                AlbumCount = filteredItems.Count(i => i is MusicAlbum),
+                EpisodeCount = filteredItems.Count(i => i is Episode),
+                GameCount = filteredItems.Count(i => i is Game),
+                GameSystemCount = filteredItems.Count(i => i is GameSystem),
+                MovieCount = filteredItems.Count(i => i is Movie),
+                SeriesCount = filteredItems.Count(i => i is Series),
+                SongCount = filteredItems.Count(i => i is Audio),
+                MusicVideoCount = filteredItems.Count(i => i is MusicVideo),
+                BoxSetCount = filteredItems.Count(i => i is BoxSet),
+                BookCount = filteredItems.Count(i => i is Book),
+
+                UniqueTypes = filteredItems.Select(i => i.GetClientTypeName()).Distinct().ToList()
             };
 
             return ToOptimizedSerializedResultUsingCache(counts);
         }
 
-        private IEnumerable<T> FilterItems<T>(IEnumerable<T> items, GetItemCounts request, Guid userId)
-            where T : BaseItem
+        private bool FilterItem(BaseItem item, GetItemCounts request, Guid? userId)
         {
-            if (request.IsFavorite.HasValue)
+            if (userId.HasValue)
             {
-                var val = request.IsFavorite.Value;
+                if (request.IsFavorite.HasValue)
+                {
+                    var val = request.IsFavorite.Value;
 
-                items = items.Where(i => _userDataManager.GetUserData(userId, i.GetUserDataKey()).IsFavorite == val);
+                    if (_userDataManager.GetUserData(userId.Value, item.GetUserDataKey()).IsFavorite != val)
+                    {
+                        return false;
+                    }
+                }
             }
 
-            return items;
+            return true;
         }
 
         /// <summary>
@@ -711,13 +702,24 @@ namespace MediaBrowser.Api.Library
 
         public object Get(GetYearIndex request)
         {
-            IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager);
+            var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes)
+             ? new string[] { }
+             : request.IncludeItemTypes.Split(',');
 
-            if (!string.IsNullOrEmpty(request.IncludeItemTypes))
+            Func<BaseItem, bool> filter = i =>
             {
-                var vals = request.IncludeItemTypes.Split(',');
-                items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
-            }
+                if (includeTypes.Length > 0)
+                {
+                    if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            };
+
+            IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, filter);
 
             var lookup = items
                 .ToLookup(i => i.ProductionYear ?? -1)
@@ -747,8 +749,7 @@ namespace MediaBrowser.Api.Library
             var dtos = GetSoundtrackSongIds(item, inheritFromParent)
                 .Select(_libraryManager.GetItemById)
                 .OfType<MusicAlbum>()
-                .SelectMany(i => i.RecursiveChildren)
-                .OfType<Audio>()
+                .SelectMany(i => i.GetRecursiveChildren(a => a is Audio))
                 .OrderBy(i => i.SortName)
                 .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
 

+ 5 - 6
MediaBrowser.Api/Movies/MoviesService.cs

@@ -121,8 +121,7 @@ namespace MediaBrowser.Api.Movies
         {
             var user = _userManager.GetUserById(request.UserId.Value);
 
-            var movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId)
-                .Where(i => i is Movie);
+            IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
 
             movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
 
@@ -174,11 +173,11 @@ namespace MediaBrowser.Api.Movies
                 (request.UserId.HasValue ? user.RootFolder :
                 _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
 
+            Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
+            
             var inputItems = user == null
-                                 ? _libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id)
-                                 : user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id);
-
-            inputItems = inputItems.Where(includeInSearch);
+                                 ? _libraryManager.RootFolder.GetRecursiveChildren(filter)
+                                 : user.RootFolder.GetRecursiveChildren(user, filter);
 
             var list = inputItems.ToList();
 

+ 4 - 4
MediaBrowser.Api/Music/AlbumsService.cs

@@ -77,14 +77,14 @@ namespace MediaBrowser.Api.Music
             var album1 = (MusicAlbum)item1;
             var album2 = (MusicAlbum)item2;
 
-            var artists1 = album1.GetRecursiveChildren()
-                .OfType<Audio>()
+            var artists1 = album1.GetRecursiveChildren(i => i is IHasArtist)
+                .Cast<IHasArtist>()
                 .SelectMany(i => i.AllArtists)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToList();
 
-            var artists2 = album2.GetRecursiveChildren()
-                .OfType<Audio>()
+            var artists2 = album2.GetRecursiveChildren(i => i is IHasArtist)
+                .Cast<IHasArtist>()
                 .SelectMany(i => i.AllArtists)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);

+ 5 - 3
MediaBrowser.Api/SearchService.cs

@@ -194,21 +194,23 @@ namespace MediaBrowser.Api
             {
                 result.Series = season.Series.Name;
 
-                result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode);
+                result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
             }
 
             var series = item as Series;
 
             if (series != null)
             {
-                result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode);
+                result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count;
             }
 
             var album = item as MusicAlbum;
 
             if (album != null)
             {
-                var songs = album.GetRecursiveChildren().OfType<Audio>().ToList();
+                var songs = album.GetRecursiveChildren(i => i is Audio)
+                    .Cast<Audio>()
+                    .ToList();
 
                 result.SongCount = songs.Count;
 

+ 5 - 5
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -76,13 +76,13 @@ namespace MediaBrowser.Api
                 (request.UserId.HasValue ? user.RootFolder :
                 libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
 
-            var fields = request.GetItemFields().ToList();
+            Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
 
             var inputItems = user == null
-                                 ? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id)
-                                 : user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id);
+                                 ? libraryManager.RootFolder.GetRecursiveChildren(filter)
+                                 : user.RootFolder.GetRecursiveChildren(user, filter);
 
-            var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore)
+            var items = GetSimilaritems(item, inputItems, getSimilarityScore)
                 .ToList();
 
             IEnumerable<BaseItem> returnItems = items;
@@ -165,7 +165,7 @@ namespace MediaBrowser.Api
 
             // Find common keywords
             points += GetKeywords(item1).Where(i => GetKeywords(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
-            
+
             // Find common studios
             points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);
 

+ 10 - 9
MediaBrowser.Api/TvShowsService.cs

@@ -256,10 +256,10 @@ namespace MediaBrowser.Api
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId)
-                .OfType<Episode>();
+            var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode);
 
-            var itemsList = _libraryManager.Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
+            var itemsList = _libraryManager
+                .Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
                 .Cast<Episode>()
                 .ToList();
 
@@ -450,24 +450,25 @@ namespace MediaBrowser.Api
                 episodes = episodes.Where(i => i.IsVirtualUnaired == val);
             }
 
+            IEnumerable<BaseItem> returnItems = episodes;
+
             // This must be the last filter
             if (!string.IsNullOrEmpty(request.AdjacentTo))
             {
-                episodes = UserViewBuilder.FilterForAdjacency(episodes, request.AdjacentTo)
-                    .Cast<Episode>();
+                returnItems = UserViewBuilder.FilterForAdjacency(returnItems, request.AdjacentTo);
             }
 
-            episodes = _libraryManager.ReplaceVideosWithPrimaryVersions(episodes).Cast<Episode>();
+            returnItems = _libraryManager.ReplaceVideosWithPrimaryVersions(returnItems);
 
             var dtoOptions = GetDtoOptions(request);
 
-            var returnItems = _dtoService.GetBaseItemDtos(episodes, dtoOptions, user)
+            var dtos = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user)
                 .ToArray();
 
             return new ItemsResult
             {
-                TotalRecordCount = returnItems.Length,
-                Items = returnItems
+                TotalRecordCount = dtos.Length,
+                Items = dtos
             };
         }
     }

+ 40 - 26
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -56,46 +56,52 @@ namespace MediaBrowser.Api.UserLibrary
         protected ItemsResult GetResult(GetItemsByName request)
         {
             User user = null;
-            BaseItem item;
+            BaseItem parentItem;
             List<BaseItem> libraryItems;
 
             if (request.UserId.HasValue)
             {
                 user = UserManager.GetUserById(request.UserId.Value);
-                item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
-
+                parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
                 libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
 
             }
             else
             {
-                item = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
-
-                libraryItems = LibraryManager.RootFolder.RecursiveChildren.ToList();
+                parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
+                libraryItems = LibraryManager.RootFolder.GetRecursiveChildren().ToList();
             }
 
             IEnumerable<BaseItem> items;
 
-            if (item.IsFolder)
+            var excludeItemTypes = request.GetExcludeItemTypes();
+            var includeItemTypes = request.GetIncludeItemTypes();
+            var mediaTypes = request.GetMediaTypes();
+
+            Func<BaseItem, bool> filter = i => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes);
+
+            if (parentItem.IsFolder)
             {
-                var folder = (Folder)item;
+                var folder = (Folder)parentItem;
 
                 if (request.UserId.HasValue)
                 {
-                    items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user, true);
+                    items = request.Recursive ?
+                        folder.GetRecursiveChildren(user, filter) :
+                        folder.GetChildren(user, true).Where(filter);
                 }
                 else
                 {
-                    items = request.Recursive ? folder.GetRecursiveChildren() : folder.Children;
+                    items = request.Recursive ?
+                        folder.GetRecursiveChildren(filter) :
+                        folder.Children.Where(filter);
                 }
             }
             else
             {
-                items = new[] { item };
+                items = new[] { parentItem }.Where(filter);
             }
 
-            items = FilterItems(request, items);
-
             var extractedItems = GetAllItems(request, items);
 
             var filteredItems = FilterItems(request, extractedItems, user);
@@ -290,33 +296,41 @@ namespace MediaBrowser.Api.UserLibrary
         /// Filters the items.
         /// </summary>
         /// <param name="request">The request.</param>
-        /// <param name="items">The items.</param>
+        /// <param name="f">The f.</param>
+        /// <param name="excludeItemTypes">The exclude item types.</param>
+        /// <param name="includeItemTypes">The include item types.</param>
+        /// <param name="mediaTypes">The media types.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected virtual IEnumerable<BaseItem> FilterItems(GetItemsByName request, IEnumerable<BaseItem> items)
+        protected bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes)
         {
             // Exclude item types
-            if (!string.IsNullOrEmpty(request.ExcludeItemTypes))
+            if (excludeItemTypes.Length > 0)
             {
-                var vals = request.ExcludeItemTypes.Split(',');
-                items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+                if (excludeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
             }
 
             // Include item types
-            if (!string.IsNullOrEmpty(request.IncludeItemTypes))
+            if (includeItemTypes.Length > 0)
             {
-                var vals = request.IncludeItemTypes.Split(',');
-                items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+                if (!includeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
             }
 
             // Include MediaTypes
-            if (!string.IsNullOrEmpty(request.MediaTypes))
+            if (mediaTypes.Length > 0)
             {
-                var vals = request.MediaTypes.Split(',');
-
-                items = items.Where(f => vals.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+                if (!mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
             }
 
-            return items;
+            return true;
         }
 
         /// <summary>

+ 2 - 2
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -398,7 +398,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 if (user == null)
                 {
-                    items = ((Folder)item).RecursiveChildren;
+                    items = ((Folder)item).GetRecursiveChildren();
 
                     items = _libraryManager.ReplaceVideosWithPrimaryVersions(items);
                 }
@@ -464,7 +464,7 @@ namespace MediaBrowser.Api.UserLibrary
                 SortBy = request.GetOrderBy(),
                 SortOrder = request.SortOrder ?? SortOrder.Ascending,
 
-                Filter = (i, u) => ApplyAdditionalFilters(request, i, u, true, _libraryManager),
+                Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager),
 
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,

+ 37 - 26
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -259,7 +259,7 @@ namespace MediaBrowser.Api.UserLibrary
 
         [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string EnableImageTypes { get; set; }
-        
+
         public GetLatestMedia()
         {
             Limit = 20;
@@ -304,36 +304,45 @@ namespace MediaBrowser.Api.UserLibrary
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            // Avoid implicitly captured closure
-            var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ?
-                GetItemsConfiguredForLatest(user) :
-                GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId);
+            var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes)
+                ? new string[] { }
+                : request.IncludeItemTypes.Split(',');
 
-            libraryItems = libraryItems.OrderByDescending(i => i.DateCreated)
-                .Where(i => i.LocationType != LocationType.Virtual);
+            var currentUser = user;
+
+            Func<BaseItem, bool> filter = i =>
+            {
+                if (includeTypes.Length > 0)
+                {
+                    if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
+                }
 
+                if (request.IsPlayed.HasValue)
+                {
+                    var val = request.IsPlayed.Value;
+                    if (i.IsPlayed(currentUser) != val)
+                    {
+                        return false;
+                    }
+                }
 
-            //if (request.IsFolder.HasValue)
-            //{
-            //var val = request.IsFolder.Value;
-            libraryItems = libraryItems.Where(f => f.IsFolder == false);
-            //}
+                return i.LocationType != LocationType.Virtual && !i.IsFolder;
+            };
 
-            if (!string.IsNullOrEmpty(request.IncludeItemTypes))
-            {
-                var vals = request.IncludeItemTypes.Split(',');
-                libraryItems = libraryItems.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
-            }
+            // Avoid implicitly captured closure
+            var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ?
+                GetItemsConfiguredForLatest(user, filter) :
+                GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, filter);
 
-            var currentUser = user;
+            libraryItems = libraryItems.OrderByDescending(i => i.DateCreated);
 
             if (request.IsPlayed.HasValue)
             {
                 var takeLimit = request.Limit * 20;
-
-                var val = request.IsPlayed.Value;
-                libraryItems = libraryItems.Where(f => f.IsPlayed(currentUser) == val)
-                    .Take(takeLimit);
+                libraryItems = libraryItems.Take(takeLimit);
             }
 
             // Avoid implicitly captured closure
@@ -394,12 +403,15 @@ namespace MediaBrowser.Api.UserLibrary
             return ToOptimizedResult(dtos.ToList());
         }
 
-        private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user)
+        private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem,bool> filter)
         {
+            // Avoid implicitly captured closure
+            var currentUser = user;
+
             return user.RootFolder.GetChildren(user, true)
                 .OfType<Folder>()
                 .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
-                .SelectMany(i => i.GetRecursiveChildren(user))
+                .SelectMany(i => i.GetRecursiveChildren(currentUser, filter))
                 .DistinctBy(i => i.Id);
         }
 
@@ -453,8 +465,7 @@ namespace MediaBrowser.Api.UserLibrary
                 var currentUser = user;
 
                 var dtos = series
-                    .GetRecursiveChildren()
-                    .Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
+                    .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
                     .OrderBy(i =>
                     {
                         if (i.PremiereDate.HasValue)

+ 3 - 2
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             get
             {
-                return RecursiveChildren.OfType<Audio>();
+                return GetRecursiveChildren(i => i is Audio).Cast<Audio>();
             }
         }
 
@@ -173,7 +173,8 @@ namespace MediaBrowser.Controller.Entities.Audio
                 id.ArtistProviderIds = artist.ProviderIds;
             }
 
-            id.SongInfos = RecursiveChildren.OfType<Audio>()
+            id.SongInfos = GetRecursiveChildren(i => i is Audio)
+                .Cast<Audio>()
                 .Select(i => i.GetLookupInfo())
                 .ToList();
 

+ 18 - 7
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Entities.Audio
     {
         public bool IsAccessedByName { get; set; }
         public List<string> ProductionLocations { get; set; }
-        
+
         public override bool IsFolder
         {
             get
@@ -122,7 +122,7 @@ namespace MediaBrowser.Controller.Entities.Audio
 
         public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = RecursiveChildren.ToList();
+            var items = GetRecursiveChildren().ToList();
 
             var songs = items.OfType<Audio>().ToList();
 
@@ -169,7 +169,7 @@ namespace MediaBrowser.Controller.Entities.Audio
 
             // Refresh current item
             await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-            
+
             // Refresh all non-songs
             foreach (var item in others)
             {
@@ -204,7 +204,8 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             var info = GetItemLookupInfo<ArtistInfo>();
 
-            info.SongInfos = RecursiveChildren.OfType<Audio>()
+            info.SongInfos = GetRecursiveChildren(i => i is Audio)
+                .Cast<Audio>()
                 .Select(i => i.GetLookupInfo())
                 .ToList();
 
@@ -213,9 +214,19 @@ namespace MediaBrowser.Controller.Entities.Audio
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.OfType<IHasArtist>()
-                .Where(i => i.HasArtist(Name))
-                .Cast<BaseItem>();
+            return inputItems.Where(ItemFilter);
+        }
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get
+            {
+                return i =>
+                {
+                    var hasArtist = i as IHasArtist;
+                    return hasArtist != null && hasArtist.HasArtist(Name);
+                };
+            }
         }
     }
 }

+ 7 - 1
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -52,7 +52,13 @@ namespace MediaBrowser.Controller.Entities.Audio
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+            return inputItems.Where(ItemFilter);
+        }
+
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { return i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); }
         }
     }
 }

+ 3 - 2
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1155,7 +1155,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType))
             {
-                return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i =>
+                return LibraryManager.RootFolder.GetRecursiveChildren(i =>
                 {
                     if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase))
                     {
@@ -1173,7 +1173,8 @@ namespace MediaBrowser.Controller.Entities
                     }
 
                     return false;
-                });
+
+                }).FirstOrDefault();
             }
 
             return null;

+ 53 - 38
MediaBrowser.Controller/Entities/Folder.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Folder
     /// </summary>
-    public class Folder : BaseItem, IHasThemeMedia, IHasTags
+    public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage
     {
         public static IUserManager UserManager { get; set; }
         public static IUserViewManager UserViewManager { get; set; }
@@ -30,6 +30,14 @@ namespace MediaBrowser.Controller.Entities
         public List<Guid> ThemeVideoIds { get; set; }
         public List<string> Tags { get; set; }
 
+        public string PreferredMetadataLanguage { get; set; }
+
+        /// <summary>
+        /// Gets or sets the preferred metadata country code.
+        /// </summary>
+        /// <value>The preferred metadata country code.</value>
+        public string PreferredMetadataCountryCode { get; set; }
+
         public Folder()
         {
             LinkedChildren = new List<LinkedChild>();
@@ -796,18 +804,20 @@ namespace MediaBrowser.Controller.Entities
         {
             var user = query.User;
 
+            Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+
             var items = query.Recursive
-                ? GetRecursiveChildren(user)
-                : GetChildren(user, true);
+                ? GetRecursiveChildren(user, filter)
+                : GetChildren(user, true).Where(filter);
 
-            var result = SortAndFilter(items, query);
+            var result = PostFilterAndSort(items, query);
 
             return Task.FromResult(result);
         }
 
-        protected QueryResult<BaseItem> SortAndFilter(IEnumerable<BaseItem> items, InternalItemsQuery query)
+        protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
         {
-            return UserViewBuilder.FilterAndSort(items, this, null, query, LibraryManager, UserDataManager);
+            return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
         }
 
         /// <summary>
@@ -832,11 +842,11 @@ namespace MediaBrowser.Controller.Entities
             //the true root should return our users root folder children
             if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren);
 
-            var list = new List<BaseItem>();
+            var result = new Dictionary<Guid, BaseItem>();
 
-            var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, includeHidden, false);
+            AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
 
-            return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
+            return result.Values;
         }
 
         protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
@@ -849,31 +859,30 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <param name="user">The user.</param>
         /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
-        /// <param name="list">The list.</param>
+        /// <param name="result">The result.</param>
         /// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
         /// <param name="recursive">if set to <c>true</c> [recursive].</param>
+        /// <param name="filter">The filter.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool includeHidden, bool recursive)
+        private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter)
         {
-            var hasLinkedChildren = false;
-
             foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
             {
                 if (child.IsVisible(user))
                 {
                     if (includeHidden || !child.IsHiddenFromUser(user))
                     {
-                        list.Add(child);
+                        if (filter == null || filter(child))
+                        {
+                            result[child.Id] = child;
+                        }
                     }
 
                     if (recursive && child.IsFolder)
                     {
                         var folder = (Folder)child;
 
-                        if (folder.AddChildrenToList(user, includeLinkedChildren, list, includeHidden, true))
-                        {
-                            hasLinkedChildren = true;
-                        }
+                        folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
                     }
                 }
             }
@@ -884,14 +893,13 @@ namespace MediaBrowser.Controller.Entities
                 {
                     if (child.IsVisible(user))
                     {
-                        hasLinkedChildren = true;
-
-                        list.Add(child);
+                        if (filter == null || filter(child))
+                        {
+                            result[child.Id] = child;
+                        }
                     }
                 }
             }
-
-            return hasLinkedChildren;
         }
 
         /// <summary>
@@ -901,18 +909,23 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+        public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+        {
+            return GetRecursiveChildren(user, i => true);
+        }
+
+        public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter)
         {
             if (user == null)
             {
                 throw new ArgumentNullException("user");
             }
 
-            var list = new List<BaseItem>();
+            var result = new Dictionary<Guid, BaseItem>();
 
-            var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, true);
+            AddChildren(user, true, result, false, true, filter);
 
-            return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
+            return result.Values;
         }
 
         /// <summary>
@@ -920,10 +933,15 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <returns>IList{BaseItem}.</returns>
         public IList<BaseItem> GetRecursiveChildren()
+        {
+            return GetRecursiveChildren(null);
+        }
+
+        public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
         {
             var list = new List<BaseItem>();
 
-            AddChildrenToList(list, true, null);
+            AddChildrenToList(list, true, filter);
 
             return list;
         }
@@ -1136,8 +1154,7 @@ namespace MediaBrowser.Controller.Entities
             bool resetPosition)
         {
             // Sweep through recursively and update status
-            var tasks = GetRecursiveChildren(user, true)
-                .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+            var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
                 .Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
 
             await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -1151,8 +1168,7 @@ namespace MediaBrowser.Controller.Entities
         public override async Task MarkUnplayed(User user)
         {
             // Sweep through recursively and update status
-            var tasks = GetRecursiveChildren(user, true)
-                .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+            var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
                 .Select(c => c.MarkUnplayed(user));
 
             await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -1181,15 +1197,15 @@ namespace MediaBrowser.Controller.Entities
                 return this;
             }
 
-            return RecursiveChildren.FirstOrDefault(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
+            return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
                 (!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) ||
-                i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase));
+                i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
+                .FirstOrDefault();
         }
 
         public override bool IsPlayed(User user)
         {
-            return GetRecursiveChildren(user)
-                .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+            return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
                 .All(i => i.IsPlayed(user));
         }
 
@@ -1216,8 +1232,7 @@ namespace MediaBrowser.Controller.Entities
             }
             else
             {
-                children = folder.GetRecursiveChildren(user)
-                    .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
+                children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
             }
 
             // Loop through each recursive child

+ 7 - 1
MediaBrowser.Controller/Entities/GameGenre.cs

@@ -42,7 +42,13 @@ namespace MediaBrowser.Controller.Entities
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.Where(i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+            return inputItems.Where(ItemFilter);
+        }
+
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { return i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); }
         }
     }
 }

+ 6 - 1
MediaBrowser.Controller/Entities/Genre.cs

@@ -46,7 +46,12 @@ namespace MediaBrowser.Controller.Entities
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+            return inputItems.Where(ItemFilter);
+        }
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase); }
         }
     }
 }

+ 4 - 1
MediaBrowser.Controller/Entities/IItemByName.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -13,6 +14,8 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="inputItems">The input items.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems);
+
+        Func<BaseItem, bool> ItemFilter { get; }
     }
 
     public interface IHasDualAccess : IItemByName

+ 1 - 1
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
 
         public User User { get; set; }
 
-        public Func<BaseItem, User, bool> Filter { get; set; }
+        public Func<BaseItem, bool> Filter { get; set; }
 
         public bool? IsFolder { get; set; }
         public bool? IsFavorite { get; set; }

+ 3 - 11
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class BoxSet
     /// </summary>
-    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
+    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
     {
         public List<Share> Shares { get; set; }
 
@@ -55,14 +55,6 @@ namespace MediaBrowser.Controller.Entities.Movies
         /// <value>The tags.</value>
         public List<string> Keywords { get; set; }
 
-        public string PreferredMetadataLanguage { get; set; }
-
-        /// <summary>
-        /// Gets or sets the preferred metadata country code.
-        /// </summary>
-        /// <value>The preferred metadata country code.</value>
-        public string PreferredMetadataCountryCode { get; set; }
-
         /// <summary>
         /// Gets or sets the display order.
         /// </summary>
@@ -103,7 +95,7 @@ namespace MediaBrowser.Controller.Entities.Movies
             var currentOfficialRating = OfficialRating;
 
             // Gather all possible ratings
-            var ratings = RecursiveChildren
+            var ratings = GetRecursiveChildren()
                 .Concat(GetLinkedChildren())
                 .Where(i => i is Movie || i is Series)
                 .Select(i => i.OfficialRating)
@@ -148,7 +140,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         {
             // Refresh bottom up, children first, then the boxset
             // By then hopefully the  movies within will have Tmdb collection values
-            var items = RecursiveChildren.ToList();
+            var items = GetRecursiveChildren().ToList();
 
             var totalItems = items.Count;
             var percentages = new Dictionary<Guid, double>(totalItems);

+ 7 - 1
MediaBrowser.Controller/Entities/Person.cs

@@ -57,7 +57,13 @@ namespace MediaBrowser.Controller.Entities
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.Where(i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)));
+            return inputItems.Where(ItemFilter);
+        }
+
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { return i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)); }
         }
     }
 

+ 7 - 1
MediaBrowser.Controller/Entities/Studio.cs

@@ -52,7 +52,13 @@ namespace MediaBrowser.Controller.Entities
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         {
-            return inputItems.Where(i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase));
+            return inputItems.Where(ItemFilter);
+        }
+
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase); }
         }
     }
 }

+ 3 - 3
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -244,7 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
 
         private IEnumerable<Episode> GetEpisodes()
         {
-            var episodes = RecursiveChildren.OfType<Episode>();
+            var episodes = GetRecursiveChildren().OfType<Episode>();
             var series = Series;
 
             if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
@@ -254,12 +254,12 @@ namespace MediaBrowser.Controller.Entities.TV
 
                 if (seasonNumber.HasValue)
                 {
-                    list.AddRange(series.RecursiveChildren.OfType<Episode>()
+                    list.AddRange(series.GetRecursiveChildren().OfType<Episode>()
                         .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
                 }
                 else
                 {
-                    list.AddRange(series.RecursiveChildren.OfType<Episode>()
+                    list.AddRange(series.GetRecursiveChildren().OfType<Episode>()
                         .Where(i => !i.ParentIndexNumber.HasValue));
                 }
 

+ 4 - 12
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities.TV
     /// <summary>
     /// Class Series
     /// </summary>
-    public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures
+    public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures
     {
         public List<Guid> SpecialFeatureIds { get; set; }
         public List<Guid> SoundtrackIds { get; set; }
@@ -23,12 +23,6 @@ namespace MediaBrowser.Controller.Entities.TV
 
         public int? AnimeSeriesIndex { get; set; }
 
-        /// <summary>
-        /// Gets or sets the preferred metadata country code.
-        /// </summary>
-        /// <value>The preferred metadata country code.</value>
-        public string PreferredMetadataCountryCode { get; set; }
-
         public Series()
         {
             AirDays = new List<DayOfWeek>();
@@ -93,7 +87,7 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             get
             {
-                return RecursiveChildren.OfType<Episode>()
+                return GetRecursiveChildren(i => i is Episode)
                         .Select(i => i.DateCreated)
                         .OrderByDescending(i => i)
                         .FirstOrDefault();
@@ -206,8 +200,8 @@ namespace MediaBrowser.Controller.Entities.TV
 
         internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes)
         {
-            var episodes = GetRecursiveChildren(user)
-                .OfType<Episode>();
+            var episodes = GetRecursiveChildren(user, i => i is Episode)
+                .Cast<Episode>();
 
             episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
 
@@ -262,8 +256,6 @@ namespace MediaBrowser.Controller.Entities.TV
             return config.BlockUnratedItems.Contains(UnratedItem.Series);
         }
 
-        public string PreferredMetadataLanguage { get; set; }
-
         public SeriesInfo GetLookupInfo()
         {
             var info = GetItemLookupInfo<SeriesInfo>();

+ 6 - 3
MediaBrowser.Controller/Entities/UserRootFolder.cs

@@ -18,10 +18,13 @@ namespace MediaBrowser.Controller.Entities
     {
         public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
+            var user = query.User;
+            Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+            
             if (query.Recursive)
             {
-                var items = query.User.RootFolder.GetRecursiveChildren(query.User);
-                return SortAndFilter(items, query);
+                var items = query.User.RootFolder.GetRecursiveChildren(query.User, filter);
+                return PostFilterAndSort(items, query);
             }
 
             var result = await UserViewManager.GetUserViews(new UserViewQuery
@@ -30,7 +33,7 @@ namespace MediaBrowser.Controller.Entities
 
             }, CancellationToken.None).ConfigureAwait(false);
 
-            return SortAndFilter(result, query);
+            return PostFilterAndSort(result.Where(filter), query);
         }
 
         public override bool IsPreSorted

+ 3 - 2
MediaBrowser.Controller/Entities/UserView.cs

@@ -40,12 +40,13 @@ namespace MediaBrowser.Controller.Entities
             return result.Items;
         }
 
-        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter)
         {
             var result = GetItems(new InternalItemsQuery
             {
                 User = user,
-                Recursive = true
+                Recursive = true,
+                Filter = filter
 
             }).Result;
 

+ 50 - 22
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -406,7 +406,9 @@ 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), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => i is MusicVideo || i is Audio.Audio && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
@@ -414,7 +416,7 @@ namespace MediaBrowser.Controller.Entities
             if (query.Recursive)
             {
                 var recursiveItems = GetRecursiveChildren(parent, user,
-                    new[] {CollectionType.Movies, CollectionType.BoxSets, string.Empty})
+                    new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty })
                     .Where(i => i is Movie || i is BoxSet);
 
                 //var collections = _collectionManager.CollapseItemsWithinBoxSets(recursiveItems, user).ToList();
@@ -490,7 +492,9 @@ 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), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
@@ -499,7 +503,9 @@ 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), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private async Task<QueryResult<BaseItem>> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
@@ -544,15 +550,16 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(GetMediaFolders(user).SelectMany(i =>
             {
                 var hasCollectionType = i as ICollectionFolder;
+                Func<BaseItem, bool> filter = b => b is BoxSet;
 
                 if (hasCollectionType != null && string.Equals(hasCollectionType.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
                 {
-                    return i.GetChildren(user, true);
+                    return i.GetChildren(user, true).Where(filter);
                 }
 
-                return i.GetRecursiveChildren(user);
+                return i.GetRecursiveChildren(user, filter);
 
-            }).OfType<BoxSet>(), parent, query);
+            }), parent, query);
         }
 
         private async Task<QueryResult<BaseItem>> GetTvView(Folder parent, User user, InternalItemsQuery query)
@@ -598,7 +605,9 @@ 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>(), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private QueryResult<BaseItem> GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query)
@@ -607,7 +616,9 @@ 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>(), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private QueryResult<BaseItem> GetFavoriteGames(Folder parent, User user, InternalItemsQuery query)
@@ -622,7 +633,9 @@ 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>(), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
@@ -646,7 +659,9 @@ namespace MediaBrowser.Controller.Entities
             query.SortOrder = SortOrder.Descending;
             query.IsResumable = true;
 
-            return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType<Episode>(), parent, GetSpecialItemsLimit(), query);
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
         }
 
         private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
@@ -748,29 +763,32 @@ namespace MediaBrowser.Controller.Entities
             InternalItemsQuery query)
             where T : BaseItem
         {
-            return GetResult(items, queryParent, null, query);
+            items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
+
+            return PostFilterAndSort(items, queryParent, null, query, _libraryManager);
         }
 
-        private QueryResult<BaseItem> GetResult<T>(IEnumerable<T> items,
+        public bool FilterItem(BaseItem item, InternalItemsQuery query)
+        {
+            return Filter(item, query.User, query, _userDataManager, _libraryManager);
+        }
+
+        private QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
             BaseItem queryParent,
             int? totalRecordLimit,
             InternalItemsQuery query)
-            where T : BaseItem
         {
-            return FilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _userDataManager);
+            return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager);
         }
 
-        public static QueryResult<BaseItem> FilterAndSort(IEnumerable<BaseItem> items,
+        public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
             BaseItem queryParent,
             int? totalRecordLimit,
             InternalItemsQuery query,
-            ILibraryManager libraryManager,
-            IUserDataManager userDataManager)
+            ILibraryManager libraryManager)
         {
             var user = query.User;
 
-            items = items.Where(i => Filter(i, user, query, userDataManager, libraryManager));
-
             items = FilterVirtualEpisodes(items,
                 query.IsMissing,
                 query.IsVirtualUnaired,
@@ -1169,7 +1187,7 @@ namespace MediaBrowser.Controller.Entities
             };
         }
 
-        private static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
+        public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
         {
             if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
             {
@@ -1191,7 +1209,7 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            if (query.Filter != null && !query.Filter(item, user))
+            if (query.Filter != null && !query.Filter(item))
             {
                 return false;
             }
@@ -1641,6 +1659,16 @@ namespace MediaBrowser.Controller.Entities
             return parent.GetRecursiveChildren(user);
         }
 
+        private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes, Func<BaseItem,bool> filter)
+        {
+            if (parent == null || parent is UserView)
+            {
+                return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user, filter));
+            }
+
+            return parent.GetRecursiveChildren(user, filter);
+        }
+
         private async Task<IEnumerable<BaseItem>> GetLiveTvFolders(User user)
         {
             var list = new List<BaseItem>();

+ 8 - 1
MediaBrowser.Controller/Entities/Year.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 
@@ -56,5 +57,11 @@ namespace MediaBrowser.Controller.Entities
 
             return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
         }
+
+
+        public Func<BaseItem, bool> ItemFilter
+        {
+            get { throw new System.NotImplementedException(); }
+        }
     }
 }

+ 32 - 28
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -50,9 +50,16 @@ namespace MediaBrowser.Controller.Playlists
             return GetPlayableItems(user);
         }
 
-        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter)
         {
-            return GetPlayableItems(user);
+            var items = GetPlayableItems(user);
+
+            if (filter != null)
+            {
+                items = items.Where(filter);
+            }
+
+            return items;
         }
 
         public IEnumerable<Tuple<LinkedChild, BaseItem>> GetManageableItems()
@@ -76,38 +83,38 @@ namespace MediaBrowser.Controller.Playlists
                 .Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
         }
 
-        private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem i, User user)
+        private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem item, User user)
         {
-            var musicGenre = i as MusicGenre;
+            var musicGenre = item as MusicGenre;
             if (musicGenre != null)
             {
-                var items = user == null
-                    ? LibraryManager.RootFolder.GetRecursiveChildren()
-                    : user.RootFolder.GetRecursiveChildren(user, true);
+                Func<BaseItem, bool> filter = i => i is Audio && i.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase);
 
-                var songs = items
-                    .OfType<Audio>()
-                    .Where(a => a.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase));
+                var items = user == null
+                    ? LibraryManager.RootFolder.GetRecursiveChildren(filter)
+                    : user.RootFolder.GetRecursiveChildren(user, filter);
 
-                return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
+                return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
             }
 
-            var musicArtist = i as MusicArtist;
+            var musicArtist = item as MusicArtist;
             if (musicArtist != null)
             {
-                var items = user == null
-                    ? LibraryManager.RootFolder.GetRecursiveChildren()
-                    : user.RootFolder.GetRecursiveChildren(user, true);
+                Func<BaseItem, bool> filter = i =>
+                {
+                    var audio = i as Audio;
+                    return audio != null && audio.HasArtist(musicArtist.Name);
+                };
 
-                var songs = items
-                    .OfType<Audio>()
-                    .Where(a => a.HasArtist(musicArtist.Name));
+                var items = user == null
+                    ? LibraryManager.RootFolder.GetRecursiveChildren(filter)
+                    : user.RootFolder.GetRecursiveChildren(user, filter);
 
-                return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
+                return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
             }
 
             // Grab these explicitly to avoid the sorting that will happen below
-            var collection = i as BoxSet;
+            var collection = item as BoxSet;
             if (collection != null)
             {
                 var items = user == null
@@ -119,7 +126,7 @@ namespace MediaBrowser.Controller.Playlists
             }
 
             // Grab these explicitly to avoid the sorting that will happen below
-            var season = i as Season;
+            var season = item as Season;
             if (season != null)
             {
                 var items = user == null
@@ -130,21 +137,18 @@ namespace MediaBrowser.Controller.Playlists
                    .Where(m => !m.IsFolder);
             }
 
-            var folder = i as Folder;
+            var folder = item as Folder;
 
             if (folder != null)
             {
                 var items = user == null
-                    ? folder.GetRecursiveChildren()
-                    : folder.GetRecursiveChildren(user, true);
-
-                items = items
-                   .Where(m => !m.IsFolder);
+                    ? folder.GetRecursiveChildren(m => !m.IsFolder)
+                    : folder.GetRecursiveChildren(user, m => !m.IsFolder);
 
                 return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
             }
 
-            return new[] { i };
+            return new[] { item };
         }
 
         [IgnoreDataMember]

+ 2 - 4
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -479,9 +479,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private async Task<QueryResult<ServerItem>> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
         {
-            var items = user.RootFolder.GetRecursiveChildren(user)
-                .Where(i => i is Movie || i is Series)
-                .Where(i => i.ContainsPerson(person.Name))
+            var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Series && i.ContainsPerson(person.Name))
                 .ToList();
 
             var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
@@ -595,7 +593,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             });
         }
 
-        private bool FilterUnsupportedContent(BaseItem i, User user)
+        private bool FilterUnsupportedContent(BaseItem i)
         {
             // Unplayable
             if (i.LocationType == LocationType.Virtual && !i.IsFolder)

+ 7 - 3
MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs

@@ -58,10 +58,13 @@ namespace MediaBrowser.Providers.MediaInfo
             var options = GetOptions();
 
             var videos = _libraryManager.RootFolder
-                .RecursiveChildren
-                .OfType<Video>()
-                .Where(i =>
+                .GetRecursiveChildren(i =>
                 {
+                    if (!(i is Video))
+                    {
+                        return false;
+                    }
+
                     if (i.LocationType == LocationType.Remote || i.LocationType == LocationType.Virtual)
                     {
                         return false;
@@ -72,6 +75,7 @@ namespace MediaBrowser.Providers.MediaInfo
                            (options.DownloadMovieSubtitles &&
                             i is Movie);
                 })
+                .Cast<Video>()
                 .ToList();
 
             var numComplete = 0;

+ 2 - 3
MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs

@@ -177,9 +177,8 @@ namespace MediaBrowser.Providers.Movies
             var numComplete = 0;
 
             // Gather all movies into a lookup by tmdb id
-            var allMovies = _libraryManager.RootFolder.RecursiveChildren
-                .Where(i => i is Movie)
-                .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)))
+            var allMovies = _libraryManager.RootFolder
+                .GetRecursiveChildren(i => i is Movie && !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb)))
                 .ToLookup(i => i.GetProviderId(MetadataProviders.Tmdb));
 
             foreach (var id in list)

+ 2 - 1
MediaBrowser.Providers/Music/AlbumImageFromSongProvider.cs

@@ -20,7 +20,8 @@ namespace MediaBrowser.Providers.Music
         {
             var album = (MusicAlbum)item;
 
-            var image = album.RecursiveChildren.OfType<Audio>()
+            var image = album.GetRecursiveChildren()
+                .OfType<Audio>()
                 .Select(i => i.GetImagePath(type))
                 .FirstOrDefault(i => !string.IsNullOrEmpty(i));
 

+ 3 - 1
MediaBrowser.Providers/Music/AlbumMetadataService.cs

@@ -45,7 +45,9 @@ namespace MediaBrowser.Providers.Music
             {
                 if (!item.IsLocked)
                 {
-                    var songs = item.RecursiveChildren.OfType<Audio>().ToList();
+                    var songs = item.GetRecursiveChildren(i => i is Audio)
+                        .Cast<Audio>()
+                        .ToList();
 
                     if (!item.LockedFields.Contains(MetadataFields.Genres))
                     {

+ 2 - 2
MediaBrowser.Providers/Music/ArtistMetadataService.cs

@@ -44,8 +44,8 @@ namespace MediaBrowser.Providers.Music
                 if (!item.IsLocked)
                 {
                     var taggedItems = item.IsAccessedByName ?
-                        item.GetTaggedItems(_libraryManager.RootFolder.RecursiveChildren.Where(i => i is IHasArtist && !i.IsFolder)).ToList() :
-                        item.RecursiveChildren.Where(i => i is IHasArtist && !i.IsFolder).ToList();
+                        _libraryManager.RootFolder.GetRecursiveChildren(i => !i.IsFolder && item.ItemFilter(i)).ToList() :
+                        item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToList();
 
                     if (!item.LockedFields.Contains(MetadataFields.Genres))
                     {

+ 5 - 3
MediaBrowser.Providers/People/TvdbPersonImageProvider.cs

@@ -56,10 +56,12 @@ namespace MediaBrowser.Providers.People
 
         public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
         {
+            // Avoid implicitly captured closure
+            var itemName = item.Name;
+
             var seriesWithPerson = _library.RootFolder
-                .RecursiveChildren
-                .OfType<Series>()
-                .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)) && i.People.Any(p => string.Equals(p.Name, item.Name, StringComparison.OrdinalIgnoreCase)))
+                .GetRecursiveChildren(i => i is Series && !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)) && i.People.Any(p => string.Equals(p.Name, itemName, StringComparison.OrdinalIgnoreCase)))
+                .Cast<Series>()
                 .ToList();
 
             var infos = seriesWithPerson.Select(i => GetImageFromSeriesData(i, item.Name, cancellationToken))

+ 2 - 2
MediaBrowser.Providers/TV/DummySeasonProvider.cs

@@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.TV
 
         private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
         {
-            var episodesInSeriesFolder = series.RecursiveChildren
+            var episodesInSeriesFolder = series.GetRecursiveChildren()
                 .OfType<Episode>()
                 .Where(i => !i.IsInSeasonFolder)
                 .ToList();
@@ -134,7 +134,7 @@ namespace MediaBrowser.Providers.TV
                 .Where(i => i.LocationType == LocationType.Virtual)
                 .ToList();
 
-            var episodes = series.RecursiveChildren.OfType<Episode>().ToList();
+            var episodes = series.GetRecursiveChildren().OfType<Episode>().ToList();
 
             var seasonsToRemove = virtualSeasons
                 .Where(i =>

+ 16 - 15
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -139,7 +139,7 @@ namespace MediaBrowser.Providers.TV
         /// <returns></returns>
         private bool HasInvalidContent(IEnumerable<Series> group)
         {
-            var allItems = group.ToList().SelectMany(i => i.RecursiveChildren).ToList();
+            var allItems = group.ToList().SelectMany(i => i.GetRecursiveChildren()).ToList();
 
             return allItems.OfType<Season>().Any(i => !i.IndexNumber.HasValue) ||
                    allItems.OfType<Episode>().Any(i =>
@@ -163,22 +163,23 @@ namespace MediaBrowser.Providers.TV
         /// <param name="episodeLookup">The episode lookup.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        private async Task<bool> AddMissingEpisodes(List<Series> series, 
+        private async Task<bool> AddMissingEpisodes(List<Series> series,
             bool seriesHasBadData,
-            string seriesDataPath, 
-            IEnumerable<Tuple<int, int>> episodeLookup, 
+            string seriesDataPath,
+            IEnumerable<Tuple<int, int>> episodeLookup,
             CancellationToken cancellationToken)
         {
             var existingEpisodes = (from s in series
                                     let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
-                                    from c in s.RecursiveChildren.OfType<Episode>()
+                                    from c in s.GetRecursiveChildren().OfType<Episode>()
                                     select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) + seasonOffset, c))
                                    .ToList();
 
             var lookup = episodeLookup as IList<Tuple<int, int>> ?? episodeLookup.ToList();
 
             var seasonCounts = (from e in lookup
-                                group e by e.Item1 into g select g)
+                                group e by e.Item1 into g
+                                select g)
                                .ToDictionary(g => g.Key, g => g.Count());
 
             var hasChanges = false;
@@ -244,23 +245,23 @@ namespace MediaBrowser.Providers.TV
         {
             var seriesAndOffsets = series.Select(s => new { Series = s, SeasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) }).ToList();
 
-            var bestMatch = seriesAndOffsets.FirstOrDefault(s => s.Series.RecursiveChildren.OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ??
-                            seriesAndOffsets.FirstOrDefault(s => s.Series.RecursiveChildren.OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ??
-                            seriesAndOffsets.OrderBy(s => s.Series.RecursiveChildren.OfType<Season>().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First();
+            var bestMatch = seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ??
+                            seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ??
+                            seriesAndOffsets.OrderBy(s => s.Series.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First();
 
             return bestMatch.Series;
         }
-        
+
         /// <summary>
         /// Removes the virtual entry after a corresponding physical version has been added
         /// </summary>
-        private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, 
+        private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series,
             IEnumerable<Tuple<int, int>> episodeLookup)
         {
             var existingEpisodes = (from s in series
                                     let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
-                                   from c in s.RecursiveChildren.OfType<Episode>()
-                                   select new { SeasonOffset = seasonOffset, Episode = c })
+                                    from c in s.GetRecursiveChildren().OfType<Episode>()
+                                    select new { SeasonOffset = seasonOffset, Episode = c })
                                    .ToList();
 
             var physicalEpisodes = existingEpisodes
@@ -320,7 +321,7 @@ namespace MediaBrowser.Providers.TV
         /// <param name="series">The series.</param>
         /// <param name="episodeLookup">The episode lookup.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series, 
+        private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series,
             IEnumerable<Tuple<int, int>> episodeLookup)
         {
             var existingSeasons = (from s in series
@@ -361,7 +362,7 @@ namespace MediaBrowser.Providers.TV
 
                     // Season does not have a number
                     // Remove if there are no episodes directly in series without a season number
-                    return i.Season.Series.RecursiveChildren.OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || !s.IsInSeasonFolder);
+                    return i.Season.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || !s.IsInSeasonFolder);
                 })
                 .ToList();
 

+ 4 - 4
MediaBrowser.Providers/TV/SeriesPostScanTask.cs

@@ -44,8 +44,8 @@ namespace MediaBrowser.Providers.TV
         private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var seriesList = _libraryManager.RootFolder
-                .RecursiveChildren
-                .OfType<Series>()
+                .GetRecursiveChildren(i => i is Series)
+                .Cast<Series>()
                 .ToList();
 
             var provider = new DummySeasonProvider(_config, _logger, _localization, _libraryManager);
@@ -65,8 +65,8 @@ namespace MediaBrowser.Providers.TV
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
-                var episodes = series.RecursiveChildren
-                    .OfType<Episode>()
+                var episodes = series.GetRecursiveChildren(i => i is Episode)
+                    .Cast<Episode>()
                     .ToList();
 
                 var physicalEpisodes = episodes.Where(i => i.LocationType != LocationType.Virtual)

+ 6 - 6
MediaBrowser.Providers/TV/TvdbPrescanTask.cs

@@ -110,10 +110,10 @@ namespace MediaBrowser.Providers.TV
                 .Select(Path.GetFileName)
                 .ToList();
 
-            var seriesIdsInLibrary = _libraryManager.RootFolder.RecursiveChildren
-               .OfType<Series>()
+            var seriesIdsInLibrary = _libraryManager.RootFolder
+                .GetRecursiveChildren(i => i is Series && !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)))
+               .Cast<Series>()
                .Select(i => i.GetProviderId(MetadataProviders.Tvdb))
-               .Where(i => !string.IsNullOrEmpty(i))
                .ToList();
 
             var missingSeries = seriesIdsInLibrary.Except(existingDirectories, StringComparer.OrdinalIgnoreCase)
@@ -301,9 +301,9 @@ namespace MediaBrowser.Providers.TV
             var numComplete = 0;
 
             // Gather all series into a lookup by tvdb id
-            var allSeries = _libraryManager.RootFolder.RecursiveChildren
-                .OfType<Series>()
-                .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)))
+            var allSeries = _libraryManager.RootFolder
+                .GetRecursiveChildren(i => i is Series && !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)))
+                .Cast<Series>()
                 .ToLookup(i => i.GetProviderId(MetadataProviders.Tvdb));
 
             foreach (var seriesId in list)

+ 23 - 15
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -96,10 +96,10 @@ namespace MediaBrowser.Server.Implementations.Dto
                 if (byName != null && !(item is LiveTvChannel))
                 {
                     var libraryItems = user != null ?
-                       user.RootFolder.GetRecursiveChildren(user) :
-                       _libraryManager.RootFolder.RecursiveChildren;
+                       user.RootFolder.GetRecursiveChildren(user, byName.ItemFilter) :
+                       _libraryManager.RootFolder.GetRecursiveChildren(byName.ItemFilter);
 
-                    SetItemByNameInfo(item, dto, byName.GetTaggedItems(libraryItems).ToList(), user);
+                    SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
                 }
 
                 FillSyncInfo(dto, item, itemIdsWithSyncJobs, options);
@@ -119,10 +119,10 @@ namespace MediaBrowser.Server.Implementations.Dto
             if (byName != null && !(item is LiveTvChannel))
             {
                 var libraryItems = user != null ?
-                   user.RootFolder.GetRecursiveChildren(user) :
-                   _libraryManager.RootFolder.RecursiveChildren;
+                   user.RootFolder.GetRecursiveChildren(user, byName.ItemFilter) :
+                   _libraryManager.RootFolder.GetRecursiveChildren(byName.ItemFilter);
 
-                SetItemByNameInfo(item, dto, byName.GetTaggedItems(libraryItems).ToList(), user);
+                SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
 
                 return dto;
             }
@@ -153,7 +153,14 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             var result = _syncManager.GetLibraryItemIds(new SyncJobItemQuery
             {
-                TargetId = deviceId
+                TargetId = deviceId,
+                Statuses = new List<SyncJobItemStatus>
+                {
+                    SyncJobItemStatus.Converting,
+                    SyncJobItemStatus.Queued,
+                    SyncJobItemStatus.Transferring,
+                    SyncJobItemStatus.Synced
+                }
             });
 
             return result.Items;
@@ -451,9 +458,8 @@ namespace MediaBrowser.Server.Implementations.Dto
             if (!string.IsNullOrEmpty(item.Album))
             {
                 var parentAlbum = _libraryManager.RootFolder
-                    .GetRecursiveChildren()
-                    .Where(i => i is MusicAlbum)
-                    .FirstOrDefault(i => string.Equals(i.Name, item.Album, StringComparison.OrdinalIgnoreCase));
+                    .GetRecursiveChildren(i => i is MusicAlbum && string.Equals(i.Name, item.Album, StringComparison.OrdinalIgnoreCase))
+                    .FirstOrDefault();
 
                 if (parentAlbum != null)
                 {
@@ -1385,7 +1391,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             {
                 linkedChildren = user == null
                     ? folder.GetRecursiveChildren().ToList()
-                    : folder.GetRecursiveChildren(user, true).ToList();
+                    : folder.GetRecursiveChildren(user).ToList();
 
                 var parentWithBackdrop = linkedChildren.FirstOrDefault(i => i.GetImages(ImageType.Backdrop).Any());
 
@@ -1402,7 +1408,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                     linkedChildren = user == null
                         ? folder.GetRecursiveChildren().ToList()
-                        : folder.GetRecursiveChildren(user, true).ToList();
+                        : folder.GetRecursiveChildren(user).ToList();
                 }
                 var parentWithImage = linkedChildren.FirstOrDefault(i => i.GetImages(ImageType.Primary).Any());
 
@@ -1479,12 +1485,14 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             if (season != null)
             {
-                children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual);
+                children = season
+                    .GetEpisodes(user)
+                    .Where(i => i.LocationType != LocationType.Virtual);
             }
             else
             {
-                children = folder.GetRecursiveChildren(user)
-                    .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
+                children = folder
+                    .GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
             }
 
             // Loop through each recursive child

+ 2 - 1
MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -245,7 +245,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
             var collections = user.RootFolder.GetChildren(user, true).ToList();
 
-            var allRecursiveChildren = user.RootFolder.GetRecursiveChildren(user)
+            var allRecursiveChildren = user.RootFolder
+                .GetRecursiveChildren(user)
                 .Select(i => i.Id)
                 .Distinct()
                 .ToDictionary(i => i);

+ 5 - 6
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -226,7 +226,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
         private List<string> GetOtherDuplicatePaths(string targetPath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber)
         {
-            var episodePaths = series.RecursiveChildren
+            var episodePaths = series.GetRecursiveChildren()
                 .OfType<Episode>()
                 .Where(i =>
                 {
@@ -335,8 +335,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             result.ExtractedName = nameWithoutYear;
             result.ExtractedYear = yearInName;
 
-            return _libraryManager.RootFolder.RecursiveChildren
-                .OfType<Series>()
+            return _libraryManager.RootFolder.GetRecursiveChildren(i => i is Series)
+                .Cast<Series>()
                 .Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
                 .Where(i => i.Item2 > 0)
                 .OrderByDescending(i => i.Item2)
@@ -400,9 +400,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
             // If there's already a season folder, use that
             var season = series
-                .RecursiveChildren
-                .OfType<Season>()
-                .FirstOrDefault(i => i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber);
+                .GetRecursiveChildren(i => i is Season && i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber)
+                .FirstOrDefault();
 
             if (season != null)
             {

+ 19 - 21
MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs

@@ -67,29 +67,26 @@ namespace MediaBrowser.Server.Implementations.Intros
                 ? null
                 : _localization.GetRatingLevel(item.OfficialRating);
 
-            var libaryItems = user.RootFolder.GetRecursiveChildren(user, false)
-                .ToList();
-
             var random = new Random(Environment.TickCount + Guid.NewGuid().GetHashCode());
 
             var candidates = new List<ItemWithTrailer>();
 
             if (config.EnableIntrosFromMoviesInLibrary)
             {
-                var itemsWithTrailers = libaryItems
-                  .Where(i =>
-                  {
-                      var hasTrailers = i as IHasTrailers;
-
-                      if (hasTrailers != null && hasTrailers.LocalTrailerIds.Count > 0)
-                      {
-                          if (i is Movie)
-                          {
-                              return !IsDuplicate(item, i);
-                          }
-                      }
-                      return false;
-                  });
+                var itemsWithTrailers = user.RootFolder
+                    .GetRecursiveChildren(user, i =>
+                    {
+                        var hasTrailers = i as IHasTrailers;
+
+                        if (hasTrailers != null && hasTrailers.LocalTrailerIds.Count > 0)
+                        {
+                            if (i is Movie)
+                            {
+                                return !IsDuplicate(item, i);
+                            }
+                        }
+                        return false;
+                    });
 
                 candidates.AddRange(itemsWithTrailers.Select(i => new ItemWithTrailer
                 {
@@ -141,15 +138,16 @@ namespace MediaBrowser.Server.Implementations.Intros
                 }));
             }
 
+            return GetResult(item, candidates, config, ratingLevel);
+        }
+
+        private IEnumerable<IntroInfo> GetResult(BaseItem item, IEnumerable<ItemWithTrailer> candidates, CinemaModeConfiguration config, int? ratingLevel)
+        {
             var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ?
                 GetCustomIntros(item) :
                 new List<IntroInfo>();
 
             var trailerLimit = config.TrailerLimit;
-            if (customIntros.Count > 0)
-            {
-                trailerLimit--;
-            }
 
             // Avoid implicitly captured closure
             return candidates.Where(i =>

+ 7 - 6
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -337,8 +337,8 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task.</returns>
         private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
         {
-            var seasons = RootFolder.RecursiveChildren
-                .OfType<Season>()
+            var seasons = RootFolder.GetRecursiveChildren(i => i is Season)
+                .Cast<Season>()
                 .Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value == 0 && !string.Equals(i.Name, newName, StringComparison.Ordinal))
                 .ToList();
 
@@ -393,7 +393,7 @@ namespace MediaBrowser.Server.Implementations.Library
             var locationType = item.LocationType;
 
             var children = item.IsFolder
-                ? ((Folder)item).RecursiveChildren.ToList()
+                ? ((Folder)item).GetRecursiveChildren().ToList()
                 : new List<BaseItem>();
 
             foreach (var metadataPath in GetMetadataPaths(item, children))
@@ -919,9 +919,10 @@ namespace MediaBrowser.Server.Implementations.Library
             {
                 var validFilename = _fileSystem.GetValidFilename(name).Trim();
 
-                var existing = RootFolder.RecursiveChildren
-                    .OfType<T>()
-                    .FirstOrDefault(i => string.Equals(_fileSystem.GetValidFilename(i.Name).Trim(), validFilename, StringComparison.OrdinalIgnoreCase));
+                var existing = RootFolder
+                    .GetRecursiveChildren(i => i is T && string.Equals(_fileSystem.GetValidFilename(i.Name).Trim(), validFilename, StringComparison.OrdinalIgnoreCase))
+                    .Cast<T>()
+                    .FirstOrDefault();
 
                 if (existing != null)
                 {

+ 3 - 4
MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Channels;
@@ -25,8 +24,8 @@ namespace MediaBrowser.Server.Implementations.Library
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
             var items = _libraryManager.RootFolder
-                .RecursiveChildren
-                .OfType<IHasTrailers>()
+                .GetRecursiveChildren(i => i is IHasTrailers)
+                .Cast<IHasTrailers>()
                 .ToList();
 
             var channelTrailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery

+ 9 - 8
MediaBrowser.Server.Implementations/Library/MusicManager.cs

@@ -32,8 +32,8 @@ namespace MediaBrowser.Server.Implementations.Library
             var artist = _libraryManager.GetArtist(name);
 
             var genres = user.RootFolder
-                .GetRecursiveChildren(user)
-                .OfType<Audio>()
+                .GetRecursiveChildren(user, i => i is Audio)
+                .Cast<Audio>()
                 .Where(i => i.HasArtist(name))
                 .SelectMany(i => i.Genres)
                 .Concat(artist.Genres)
@@ -45,8 +45,8 @@ namespace MediaBrowser.Server.Implementations.Library
         public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user)
         {
             var genres = item
-                .GetRecursiveChildren(user, true)
-               .OfType<Audio>()
+                .GetRecursiveChildren(user, i => i is Audio)
+               .Cast<Audio>()
                .SelectMany(i => i.Genres)
                .Concat(item.Genres)
                .Distinct(StringComparer.OrdinalIgnoreCase);
@@ -57,8 +57,8 @@ namespace MediaBrowser.Server.Implementations.Library
         public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
         {
             var genres = item
-                .GetRecursiveChildren(user, true)
-               .OfType<Audio>()
+               .GetRecursiveChildren(user, i => i is Audio)
+               .Cast<Audio>()
                .SelectMany(i => i.Genres)
                .Concat(item.Genres)
                .Distinct(StringComparer.OrdinalIgnoreCase);
@@ -68,12 +68,13 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user)
         {
-            var inputItems = user.RootFolder.GetRecursiveChildren(user);
+            var inputItems = user.RootFolder
+                .GetRecursiveChildren(user, i => i is Audio);
 
             var genresDictionary = genres.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
 
             return inputItems
-                .OfType<Audio>()
+                .Cast<Audio>()
                 .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
                 .Where(i => i.Item2 > 0)
                 .OrderByDescending(i => i.Item2)

+ 4 - 5
MediaBrowser.Server.Implementations/Library/SearchEngine.cs

@@ -35,20 +35,19 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             IEnumerable<BaseItem> inputItems;
 
+            Func<BaseItem, bool> filter = i => !(i is ICollectionFolder);
+
             if (string.IsNullOrWhiteSpace(query.UserId))
             {
-                inputItems = _libraryManager.RootFolder.RecursiveChildren;
+                inputItems = _libraryManager.RootFolder.GetRecursiveChildren(filter);
             }
             else
             {
                 var user = _userManager.GetUserById(query.UserId);
 
-                inputItems = user.RootFolder.GetRecursiveChildren(user, true);
+                inputItems = user.RootFolder.GetRecursiveChildren(user, filter);
             }
 
-
-            inputItems = inputItems.Where(i => !(i is ICollectionFolder));
-
             inputItems = _libraryManager.ReplaceVideosWithPrimaryVersions(inputItems);
 
             var results = await GetSearchHints(inputItems, query).ConfigureAwait(false);

+ 4 - 3
MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -44,9 +44,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var allItems = _libraryManager.RootFolder.GetRecursiveChildren();
-
-            var allSongs = allItems.Where(i => !i.IsFolder).OfType<IHasArtist>().ToList();
+            var allSongs = _libraryManager.RootFolder
+                .GetRecursiveChildren(i => !i.IsFolder && (i is IHasArtist))
+                .Cast<IHasArtist>()
+                .ToList();
 
             var allArtists = allSongs.SelectMany(i => i.AllArtists)
                 .Distinct(StringComparer.OrdinalIgnoreCase)

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs

@@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.RootFolder.RecursiveChildren.Where(i => (i is Game))
+            var items = _libraryManager.RootFolder.GetRecursiveChildren(i => (i is Game))
                 .SelectMany(i => i.Genres)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs

@@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.RootFolder.RecursiveChildren.Where(i => !(i is IHasMusicGenres) && !(i is Game))
+            var items = _libraryManager.RootFolder.GetRecursiveChildren(i => !(i is IHasMusicGenres) && !(i is Game))
                 .SelectMany(i => i.Genres)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs

@@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.RootFolder.RecursiveChildren.Where(i => (i is IHasMusicGenres))
+            var items = _libraryManager.RootFolder.GetRecursiveChildren(i => (i is IHasMusicGenres))
                 .SelectMany(i => i.Genres)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs

@@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.RootFolder.RecursiveChildren
+            var items = _libraryManager.RootFolder.GetRecursiveChildren()
                 .SelectMany(i => i.Studios)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs

@@ -20,7 +20,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
 
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var allYears = _libraryManager.RootFolder.RecursiveChildren
+            var allYears = _libraryManager.RootFolder.GetRecursiveChildren(i => i.ProductionYear.HasValue)
                 .Select(i => i.ProductionYear ?? -1)
                 .Where(i => i > 0)
                 .Distinct()

+ 1 - 1
MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.Photos
         protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
         {
             var photoAlbum = (PhotoAlbum)item;
-            var items = GetFinalItems(photoAlbum.RecursiveChildren.Where(i => i is Photo).ToList());
+            var items = GetFinalItems(photoAlbum.GetRecursiveChildren(i => i is Photo).ToList());
 
             return Task.FromResult(items);
         }

+ 1 - 2
MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs

@@ -23,8 +23,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
 
         protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
         {
-            return RecursiveChildren
-                .OfType<Playlist>();
+            return GetRecursiveChildren(i => i is Playlist);
         }
 
         public override bool IsHidden

+ 1 - 2
MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs

@@ -81,8 +81,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
                         var folder = item as Folder;
                         if (folder != null)
                         {
-                            options.MediaType = folder.GetRecursiveChildren()
-                                .Where(i => !i.IsFolder && i.SupportsAddingToPlaylist)
+                            options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
                                 .Select(i => i.MediaType)
                                 .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
                         }

+ 2 - 2
MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs

@@ -145,8 +145,8 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// <returns>Task.</returns>
         public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var videos = _libraryManager.RootFolder.RecursiveChildren
-                .OfType<Video>()
+            var videos = _libraryManager.RootFolder.GetRecursiveChildren(i => i is Video)
+                .Cast<Video>()
                 .ToList();
 
             var numComplete = 0;

+ 3 - 4
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -899,10 +899,9 @@ namespace MediaBrowser.Server.Implementations.Session
             {
                 var folder = (Folder)item;
 
-                var items = user == null ? folder.RecursiveChildren :
-                    folder.GetRecursiveChildren(user);
-
-                items = items.Where(i => !i.IsFolder);
+                var items = user == null ? 
+                    folder.GetRecursiveChildren(i => !i.IsFolder) :
+                    folder.GetRecursiveChildren(user, i => !i.IsFolder);
 
                 items = items.OrderBy(i => i.SortName);
 

+ 3 - 7
MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs

@@ -288,18 +288,14 @@ namespace MediaBrowser.Server.Implementations.Sync
             var itemByName = item as IItemByName;
             if (itemByName != null)
             {
-                var items = user.RootFolder
-                    .GetRecursiveChildren(user);
-
-                return itemByName.GetTaggedItems(items);
+                return user.RootFolder
+                    .GetRecursiveChildren(user, itemByName.ItemFilter);
             }
 
             if (item.IsFolder)
             {
                 var folder = (Folder)item;
-                var items = folder.GetRecursiveChildren(user);
-
-                items = items.Where(i => !i.IsFolder);
+                var items = folder.GetRecursiveChildren(user, i => !i.IsFolder);
 
                 if (!folder.IsPreSorted)
                 {

+ 9 - 7
MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs

@@ -36,8 +36,8 @@ namespace MediaBrowser.Server.Implementations.TV
                 ? new string[] { }
                 : new[] { request.ParentId };
 
-            var items = GetAllLibraryItems(user, parentIds)
-                .OfType<Series>();
+            var items = GetAllLibraryItems(user, parentIds, i => i is Series)
+                .Cast<Series>();
 
             // Avoid implicitly captured closure
             var episodes = GetNextUpEpisodes(request, user, items);
@@ -54,8 +54,9 @@ namespace MediaBrowser.Server.Implementations.TV
                 throw new ArgumentException("User not found");
             }
 
-            var items = parentsFolders.SelectMany(i => i.GetRecursiveChildren(user))
-                .OfType<Series>();
+            var items = parentsFolders
+                .SelectMany(i => i.GetRecursiveChildren(user, s => s is Series))
+                .Cast<Series>();
 
             // Avoid implicitly captured closure
             var episodes = GetNextUpEpisodes(request, user, items);
@@ -63,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.TV
             return GetResult(episodes, null, request);
         }
 
-        private IEnumerable<BaseItem> GetAllLibraryItems(User user, string[] parentIds)
+        private IEnumerable<BaseItem> GetAllLibraryItems(User user, string[] parentIds, Func<BaseItem,bool> filter)
         {
             if (parentIds.Length > 0)
             {
@@ -71,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.TV
                 {
                     var folder = (Folder)_libraryManager.GetItemById(new Guid(i));
 
-                    return folder.GetRecursiveChildren(user);
+                    return folder.GetRecursiveChildren(user, filter);
 
                 });
             }
@@ -81,7 +82,7 @@ namespace MediaBrowser.Server.Implementations.TV
                 throw new ArgumentException("User not found");
             }
 
-            return user.RootFolder.GetRecursiveChildren(user);
+            return user.RootFolder.GetRecursiveChildren(user, filter);
         }
 
         public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series)
@@ -126,6 +127,7 @@ namespace MediaBrowser.Server.Implementations.TV
         {
             // Get them in display order, then reverse
             var allEpisodes = series.GetSeasons(user, true, true)
+                .Where(i => !i.IndexNumber.HasValue || i.IndexNumber.Value != 0)
                 .SelectMany(i => i.GetEpisodes(user, true, true))
                 .Reverse()
                 .ToList();

+ 6 - 6
MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs

@@ -37,10 +37,10 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
                     switch (environment.SystemArchitecture)
                     {
                         case Architecture.X86_X64:
-                            info.Version = "20140716";
+                            info.Version = "20150124";
                             break;
                         case Architecture.X86:
-                            info.Version = "20140923";
+                            info.Version = "20150124";
                             break;
                     }
                     break;
@@ -128,14 +128,14 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
                         case Architecture.X86_X64:
                             return new[]
                             {
-                                "http://ffmpeg.gusari.org/static/64bit/ffmpeg.static.64bit.latest.tar.gz",
-                                "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg.static.64bit.2014-07-16.tar.gz"
+                                "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-64bit-static.tar.xz",
+                                "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg-release-64bit-static.tar.xz"
                             };
                         case Architecture.X86:
                             return new[]
                             {
-                                "http://ffmpeg.gusari.org/static/32bit/ffmpeg.static.32bit.latest.tar.gz",
-                                "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg.static.32bit.2014-07-16.tar.gz"
+                                "http://johnvansickle.com/ffmpeg/releases/ffmpeg-release-32bit-static.tar.xz",
+                                "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/linux/ffmpeg-release-32bit-static.tar.xz"
                             };
                     }
                     break;

+ 1 - 2
MediaBrowser.XbmcMetadata/EntryPoint.cs

@@ -50,8 +50,7 @@ namespace MediaBrowser.XbmcMetadata
                         return;
                     }
 
-                    var items = _libraryManager.RootFolder.RecursiveChildren;
-                    items = person.GetTaggedItems(items).ToList();
+                    var items = _libraryManager.RootFolder.GetRecursiveChildren(person.ItemFilter);
 
                     foreach (var item in items)
                     {

+ 2 - 2
MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs

@@ -51,8 +51,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
             }
             
             var albums = artist
-                .RecursiveChildren
-                .OfType<MusicAlbum>()
+                .GetRecursiveChildren(i => i is MusicAlbum)
+                .Cast<MusicAlbum>()
                 .ToList();
 
             AddAlbums(albums, writer);