Browse Source

Merge branch 'dev'

Conflicts:
	MediaBrowser.Controller/Entities/BaseItem.cs
	MediaBrowser.WebDashboard/dashboard-ui/bower_components/emby-apiclient/.bower.json
	MediaBrowser.WebDashboard/dashboard-ui/bower_components/emby-webcomponents/.bower.json
	MediaBrowser.WebDashboard/dashboard-ui/bower_components/iron-icons/.bower.json
	MediaBrowser.WebDashboard/dashboard-ui/bower_components/iron-icons/bower.json
	MediaBrowser.WebDashboard/dashboard-ui/strings/html/kk.json
	MediaBrowser.WebDashboard/dashboard-ui/strings/html/ru.json
	MediaBrowser.WebDashboard/dashboard-ui/strings/javascript/kk.json
	MediaBrowser.WebDashboard/dashboard-ui/strings/javascript/ru.json
	SharedVersion.cs
Luke Pulverenti 9 years ago
parent
commit
8a2e023e28
100 changed files with 1409 additions and 1431 deletions
  1. 1 1
      Emby.Drawing/Emby.Drawing.csproj
  2. 16 12
      Emby.Drawing/ImageMagick/ImageMagickEncoder.cs
  3. 1 1
      Emby.Drawing/packages.config
  4. 0 44
      MediaBrowser.Api/BaseApiService.cs
  5. 16 5
      MediaBrowser.Api/GamesService.cs
  6. 52 8
      MediaBrowser.Api/Library/LibraryService.cs
  7. 3 3
      MediaBrowser.Api/MediaBrowser.Api.csproj
  8. 25 20
      MediaBrowser.Api/Movies/MoviesService.cs
  9. 1 1
      MediaBrowser.Api/PackageService.cs
  10. 7 0
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  11. 2 1
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  12. 1 1
      MediaBrowser.Api/SearchService.cs
  13. 0 4
      MediaBrowser.Api/StartupWizardService.cs
  14. 14 8
      MediaBrowser.Api/System/SystemService.cs
  15. 16 17
      MediaBrowser.Api/TvShowsService.cs
  16. 7 0
      MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
  17. 6 9
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  18. 19 4
      MediaBrowser.Api/UserLibrary/UserViewsService.cs
  19. 1 1
      MediaBrowser.Api/packages.config
  20. 1 1
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  21. 1 1
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  22. 10 6
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  23. 89 58
      MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
  24. 212 0
      MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
  25. 6 6
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  26. 3 2
      MediaBrowser.Common.Implementations/packages.config
  27. 8 1
      MediaBrowser.Common/Net/INetworkManager.cs
  28. 2 1
      MediaBrowser.Common/Updates/IInstallationManager.cs
  29. 2 2
      MediaBrowser.Controller/Channels/ChannelAudioItem.cs
  30. 6 0
      MediaBrowser.Controller/Channels/ChannelFolderItem.cs
  31. 2 2
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  32. 16 18
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  33. 7 2
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  34. 5 0
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  35. 182 77
      MediaBrowser.Controller/Entities/BaseItem.cs
  36. 3 14
      MediaBrowser.Controller/Entities/Book.cs
  37. 8 7
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  38. 55 77
      MediaBrowser.Controller/Entities/Folder.cs
  39. 2 9
      MediaBrowser.Controller/Entities/Game.cs
  40. 5 0
      MediaBrowser.Controller/Entities/GameSystem.cs
  41. 5 0
      MediaBrowser.Controller/Entities/ICollectionFolder.cs
  42. 18 0
      MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
  43. 35 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  44. 6 31
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  45. 3 3
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  46. 2 2
      MediaBrowser.Controller/Entities/MusicVideo.cs
  47. 9 0
      MediaBrowser.Controller/Entities/Person.cs
  48. 1 8
      MediaBrowser.Controller/Entities/Photo.cs
  49. 2 27
      MediaBrowser.Controller/Entities/PhotoAlbum.cs
  50. 0 7
      MediaBrowser.Controller/Entities/Studio.cs
  51. 17 16
      MediaBrowser.Controller/Entities/TV/Episode.cs
  52. 7 14
      MediaBrowser.Controller/Entities/TV/Season.cs
  53. 5 0
      MediaBrowser.Controller/Entities/TV/Series.cs
  54. 3 3
      MediaBrowser.Controller/Entities/Trailer.cs
  55. 20 1
      MediaBrowser.Controller/Entities/User.cs
  56. 9 1
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  57. 73 20
      MediaBrowser.Controller/Entities/UserView.cs
  58. 89 166
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  59. 1 7
      MediaBrowser.Controller/Entities/Video.cs
  60. 3 1
      MediaBrowser.Controller/IServerApplicationHost.cs
  61. 24 3
      MediaBrowser.Controller/Library/ILibraryManager.cs
  62. 1 1
      MediaBrowser.Controller/Library/ItemResolveArgs.cs
  63. 7 3
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  64. 2 2
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  65. 23 25
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  66. 7 3
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  67. 6 0
      MediaBrowser.Controller/LiveTv/RecordingGroup.cs
  68. 4 3
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  69. 2 1
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  70. 0 1
      MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
  71. 0 6
      MediaBrowser.Controller/Net/IHttpServer.cs
  72. 14 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  73. 1 0
      MediaBrowser.Controller/Playlists/Playlist.cs
  74. 0 32
      MediaBrowser.Controller/Providers/MetadataStatus.cs
  75. 3 2
      MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
  76. 1 1
      MediaBrowser.Controller/packages.config
  77. 5 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  78. 37 24
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  79. 1 1
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  80. 25 18
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  81. 3 0
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  82. 1 1
      MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs
  83. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  84. 29 47
      MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
  85. 14 1
      MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
  86. 1 0
      MediaBrowser.Dlna/packages.config
  87. 0 3
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  88. 0 166
      MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs
  89. 0 147
      MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs
  90. 0 154
      MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs
  91. 1 1
      MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
  92. 28 25
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  93. 2 1
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  94. 20 3
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  95. 3 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  96. 3 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  97. 3 0
      MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs
  98. 9 22
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  99. 1 0
      MediaBrowser.Model/Connect/ConnectUser.cs
  100. 6 0
      MediaBrowser.Model/Entities/MediaStream.cs

+ 1 - 1
Emby.Drawing/Emby.Drawing.csproj

@@ -37,7 +37,7 @@
     </Reference>
     <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
+      <HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 16 - 12
Emby.Drawing/ImageMagick/ImageMagickEncoder.cs

@@ -72,11 +72,16 @@ namespace Emby.Drawing.ImageMagick
 
         private void LogVersion()
         {
-            _logger.Info("ImageMagick version: " + Wand.VersionString);
+            _logger.Info("ImageMagick version: " + GetVersion());
             TestWebp();
             Wand.SetMagickThreadCount(1);
         }
 
+        public static string GetVersion()
+        {
+            return Wand.VersionString;
+        }
+
         private bool _webpAvailable = true;
         private void TestWebp()
         {
@@ -148,7 +153,7 @@ namespace Emby.Drawing.ImageMagick
                     DrawIndicator(originalImage, width, height, options);
 
                     originalImage.CurrentImage.CompressionQuality = quality;
-                    //originalImage.CurrentImage.StripImage();
+                    originalImage.CurrentImage.StripImage();
 
                     originalImage.SaveImage(outputPath);
                 }
@@ -165,7 +170,7 @@ namespace Emby.Drawing.ImageMagick
                         DrawIndicator(wand, width, height, options);
 
                         wand.CurrentImage.CompressionQuality = quality;
-                        //wand.CurrentImage.StripImage();
+                        wand.CurrentImage.StripImage();
 
                         wand.SaveImage(outputPath);
                     }
@@ -176,15 +181,14 @@ namespace Emby.Drawing.ImageMagick
 
         private void ScaleImage(MagickWand wand, int width, int height)
         {
-            wand.CurrentImage.ResizeImage(width, height);
-            //if (_config.Configuration.EnableHighQualityImageScaling)
-            //{
-            //    wand.CurrentImage.ResizeImage(width, height);
-            //}
-            //else
-            //{
-            //    wand.CurrentImage.ScaleImage(width, height);
-            //}
+            if (_config.Configuration.EnableHighQualityImageScaling)
+            {
+                wand.CurrentImage.ResizeImage(width, height);
+            }
+            else
+            {
+                wand.CurrentImage.ScaleImage(width, height);
+            }
         }
 
         /// <summary>

+ 1 - 1
Emby.Drawing/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
+  <package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 0 - 44
MediaBrowser.Api/BaseApiService.cs

@@ -198,50 +198,6 @@ namespace MediaBrowser.Api
             return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
         }
 
-        protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
-        {
-            if (!string.IsNullOrEmpty(parentId))
-            {
-                var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
-
-                if (!string.IsNullOrWhiteSpace(userId))
-                {
-                    var user = userManager.GetUserById(userId);
-
-                    if (user == null)
-                    {
-                        throw new ArgumentException("User not found");
-                    }
-
-                    return folder
-                        .GetRecursiveChildren(user, filter)
-                        .ToList();
-                }
-
-                return folder
-                    .GetRecursiveChildren(filter);
-            }
-            if (!string.IsNullOrWhiteSpace(userId))
-            {
-                var user = userManager.GetUserById(userId);
-
-                if (user == null)
-                {
-                    throw new ArgumentException("User not found");
-                }
-
-                return userManager
-                    .GetUserById(userId)
-                    .RootFolder
-                    .GetRecursiveChildren(user, filter)
-                    .ToList();
-            }
-
-            return libraryManager
-                .RootFolder
-                .GetRecursiveChildren(filter);
-        }
-
         /// <summary>
         /// Deslugs an artist name by finding the correct entry in the library
         /// </summary>

+ 16 - 5
MediaBrowser.Api/GamesService.cs

@@ -102,12 +102,16 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         public object Get(GetGameSystemSummaries request)
         {
-            var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem)
+            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(GameSystem).Name }
+            };
+            var parentIds = new string[] { } ;
+            var gameSystems = _libraryManager.GetItems(query, parentIds)
                 .Cast<GameSystem>()
                 .ToList();
 
-            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
-
             var result = gameSystems
                 .Select(i => GetSummary(i, user))
                 .ToList();
@@ -119,8 +123,15 @@ namespace MediaBrowser.Api
 
         public object Get(GetPlayerIndex request)
         {
-            var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game)
-                .Cast<Game>();
+            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Game).Name }
+            };
+            var parentIds = new string[] { };
+            var games = _libraryManager.GetItems(query, parentIds)
+                .Cast<Game>()
+                .ToList();
 
             var lookup = games
                 .ToLookup(i => i.PlayersSupported ?? -1)

+ 52 - 8
MediaBrowser.Api/Library/LibraryService.cs

@@ -416,7 +416,7 @@ namespace MediaBrowser.Api.Library
 
         public object Get(GetMediaFolders request)
         {
-            var items = _libraryManager.GetUserRootFolder().Children.OrderBy(i => i.SortName).ToList();
+            var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList();
 
             if (request.IsHidden.HasValue)
             {
@@ -610,7 +610,7 @@ namespace MediaBrowser.Api.Library
 
             var dtoOptions = GetDtoOptions(request);
 
-            BaseItem parent = item.Parent;
+            BaseItem parent = item.GetParent();
 
             while (parent != null)
             {
@@ -621,7 +621,7 @@ namespace MediaBrowser.Api.Library
 
                 baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 
-                parent = parent.Parent;
+                parent = parent.GetParent();
             }
 
             return baseItemDtos.ToList();
@@ -629,7 +629,7 @@ namespace MediaBrowser.Api.Library
 
         private BaseItem TranslateParentItem(BaseItem item, User user)
         {
-            if (item.Parent is AggregateFolder)
+            if (item.GetParent() is AggregateFolder)
             {
                 return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
             }
@@ -677,6 +677,50 @@ namespace MediaBrowser.Api.Library
             return ToOptimizedSerializedResultUsingCache(counts);
         }
 
+        private IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter)
+        {
+            if (!string.IsNullOrEmpty(parentId))
+            {
+                var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
+
+                if (!string.IsNullOrWhiteSpace(userId))
+                {
+                    var user = userManager.GetUserById(userId);
+
+                    if (user == null)
+                    {
+                        throw new ArgumentException("User not found");
+                    }
+
+                    return folder
+                        .GetRecursiveChildren(user, filter)
+                        .ToList();
+                }
+
+                return folder
+                    .GetRecursiveChildren(filter);
+            }
+            if (!string.IsNullOrWhiteSpace(userId))
+            {
+                var user = userManager.GetUserById(userId);
+
+                if (user == null)
+                {
+                    throw new ArgumentException("User not found");
+                }
+
+                return userManager
+                    .GetUserById(userId)
+                    .RootFolder
+                    .GetRecursiveChildren(user, filter)
+                    .ToList();
+            }
+
+            return libraryManager
+                .RootFolder
+                .GetRecursiveChildren(filter);
+        }
+
         private bool FilterItem(BaseItem item, GetItemCounts request, string userId)
         {
             if (!string.IsNullOrWhiteSpace(userId))
@@ -817,9 +861,9 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
-                item = item.Parent;
+                item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);
@@ -860,9 +904,9 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
-                item = item.Parent;
+                item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);

+ 3 - 3
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -49,6 +49,9 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
     </Reference>
@@ -63,9 +66,6 @@
     <Reference Include="ServiceStack.Text">
       <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq">
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 25 - 20
MediaBrowser.Api/Movies/MoviesService.cs

@@ -117,10 +117,7 @@ namespace MediaBrowser.Api.Movies
         public async Task<object> Get(GetSimilarMovies request)
         {
             var result = await GetSimilarItemsResult(
-                // Strip out secondary versions
-                request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
-
-                SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
+                request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -128,10 +125,7 @@ namespace MediaBrowser.Api.Movies
         public async Task<object> Get(GetSimilarTrailers request)
         {
             var result = await GetSimilarItemsResult(
-                // Strip out secondary versions
-                request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
-
-                SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
+                request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -140,8 +134,12 @@ namespace MediaBrowser.Api.Movies
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
-
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Movie).Name }
+            };
+            var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
+            var movies = _libraryManager.GetItems(query, parentIds);
             movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
 
             var listEligibleForCategories = new List<BaseItem>();
@@ -184,21 +182,27 @@ namespace MediaBrowser.Api.Movies
             return ToOptimizedResult(result);
         }
 
-        private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+        private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
         {
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id) ?
                 (!string.IsNullOrWhiteSpace(request.UserId) ? 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(filter)
-                                 : user.RootFolder.GetRecursiveChildren(user, filter);
-
-            var list = inputItems.ToList();
+            
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Movie).Name }
+            };
+            var parentIds = new string[] { };
+            var list = _libraryManager.GetItems(query, parentIds)
+                .Where(i =>
+                {
+                    // Strip out secondary versions
+                    var v = i as Video;
+                    return v != null && !v.PrimaryVersionId.HasValue;
+                })
+                .ToList();
 
             if (user != null && user.Configuration.IncludeTrailersInSuggestions)
             {
@@ -379,9 +383,10 @@ namespace MediaBrowser.Api.Movies
         {
             foreach (var name in names)
             {
-                var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery
+                var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery(user)
                 {
                     Person = name
+
                 });
 
                 var items = allMovies

+ 1 - 1
MediaBrowser.Api/PackageService.cs

@@ -233,7 +233,7 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
             }
 
-            Task.Run(() => _installationManager.InstallPackage(package, new Progress<double>(), CancellationToken.None));
+            Task.Run(() => _installationManager.InstallPackage(package, true, new Progress<double>(), CancellationToken.None));
         }
 
         /// <summary>

+ 7 - 0
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1562,6 +1562,13 @@ namespace MediaBrowser.Api.Playback
                 RequestedUrl = url
             };
 
+            //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
+            //    (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
+            //    (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
+            //{
+            //    state.SegmentLength = 6;
+            //}
+
             if (!string.IsNullOrWhiteSpace(request.AudioCodec))
             {
                 state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

+ 2 - 1
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -111,7 +111,8 @@ namespace MediaBrowser.Api.Playback.Hls
                             throw;
                         }
 
-                        await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false);
+                        var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
+                        await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
                     }
                 }
                 finally

+ 1 - 1
MediaBrowser.Api/SearchService.cs

@@ -283,7 +283,7 @@ namespace MediaBrowser.Api
         private T GetParentWithImage<T>(BaseItem item, ImageType type)
             where T : BaseItem
         {
-            return item.Parents.OfType<T>().FirstOrDefault(i => i.HasImage(type));
+            return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
         }
     }
 }

+ 0 - 4
MediaBrowser.Api/StartupWizardService.cs

@@ -68,11 +68,7 @@ namespace MediaBrowser.Api
             _config.Configuration.EnableLocalizedGuids = true;
             _config.Configuration.MergeMetadataAndImagesByName = true;
             _config.Configuration.EnableStandaloneMetadata = true;
-            _config.Configuration.EnableLibraryMetadataSubFolder = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
-            _config.Configuration.DisableXmlSavers = true;
-            _config.Configuration.DisableStartupScan = true;
-            _config.Configuration.EnableUserViews = true;
             _config.Configuration.EnableDateLastRefresh = true;
             _config.SaveConfiguration();
         }

+ 14 - 8
MediaBrowser.Api/System/SystemService.cs

@@ -13,6 +13,7 @@ using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Net;
 
 namespace MediaBrowser.Api.System
 {
@@ -32,6 +33,12 @@ namespace MediaBrowser.Api.System
 
     }
 
+    [Route("/System/Ping", "POST")]
+    public class PingSystem : IReturnVoid
+    {
+
+    }
+
     /// <summary>
     /// Class RestartApplication
     /// </summary>
@@ -59,7 +66,7 @@ namespace MediaBrowser.Api.System
 
     [Route("/System/Endpoint", "GET", Summary = "Gets information about the request endpoint")]
     [Authenticated]
-    public class GetEndpointInfo : IReturn<EndpointInfo>
+    public class GetEndpointInfo : IReturn<EndPointInfo>
     {
         public string Endpoint { get; set; }
     }
@@ -104,6 +111,11 @@ namespace MediaBrowser.Api.System
             _security = security;
         }
 
+        public object Post(PingSystem request)
+        {
+            return _appHost.Name;
+        }
+
         public object Get(GetServerLogs request)
         {
             List<FileSystemMetadata> files;
@@ -199,17 +211,11 @@ namespace MediaBrowser.Api.System
 
         public object Get(GetEndpointInfo request)
         {
-            return ToOptimizedResult(new EndpointInfo
+            return ToOptimizedResult(new EndPointInfo
             {
                 IsLocal = Request.IsLocal,
                 IsInNetwork = _network.IsInLocalNetwork(request.Endpoint ?? Request.RemoteIp)
             });
         }
     }
-
-    public class EndpointInfo
-    {
-        public bool IsLocal { get; set; }
-        public bool IsInNetwork { get; set; }
-    }
 }

+ 16 - 17
MediaBrowser.Api/TvShowsService.cs

@@ -159,7 +159,7 @@ namespace MediaBrowser.Api
 
         [ApiMember(Name = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string StartItemId { get; set; }
-        
+
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
@@ -273,29 +273,28 @@ namespace MediaBrowser.Api
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            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)
-                .Cast<Episode>()
-                .ToList();
-
-            var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList();
-
             var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
-            var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList();
 
-            previousEpisodes.AddRange(unairedEpisodes);
+            var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
 
-            var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Episode).Name },
+                SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
+                SortOrder = SortOrder.Ascending,
+                MinPremiereDate = minPremiereDate,
+                StartIndex = request.StartIndex,
+                Limit = request.Limit
+
+            }, parentIds);
 
             var options = GetDtoOptions(request);
 
-            var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray();
+            var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray();
 
             var result = new ItemsResult
             {
-                TotalRecordCount = itemsList.Count,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = returnItems
             };
 
@@ -440,7 +439,7 @@ namespace MediaBrowser.Api
                 }
 
                 episodes = season.GetEpisodes(user);
-            } 
+            }
             else if (request.Season.HasValue)
             {
                 var series = _libraryManager.GetItemById(request.Id) as Series;
@@ -495,7 +494,7 @@ namespace MediaBrowser.Api
                 .ToList();
 
             var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit);
-            
+
             var dtoOptions = GetDtoOptions(request);
 
             var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user)

+ 7 - 0
MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs

@@ -206,6 +206,8 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Genres { get; set; }
 
+        public string GenreIds { get; set; }
+        
         [ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string OfficialRatings { get; set; }
 
@@ -385,6 +387,11 @@ namespace MediaBrowser.Api.UserLibrary
             return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
         }
 
+        public string[] GetGenreIds()
+        {
+            return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+        }
+
         public string[] GetPersonTypes()
         {
             return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

+ 6 - 9
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -112,6 +112,11 @@ namespace MediaBrowser.Api.UserLibrary
                 user == null ? _libraryManager.RootFolder : user.RootFolder :
                 parentItem;
 
+            if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
+            {
+                item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+            }
+
             // Default list type = children
 
             if (!string.IsNullOrEmpty(request.Ids))
@@ -211,6 +216,7 @@ namespace MediaBrowser.Api.UserLibrary
                 Tags = request.GetTags(),
                 OfficialRatings = request.GetOfficialRatings(),
                 Genres = request.GetGenres(),
+                GenreIds = request.GetGenreIds(),
                 Studios = request.GetStudios(),
                 StudioIds = request.GetStudioIds(),
                 Person = request.Person,
@@ -423,15 +429,6 @@ namespace MediaBrowser.Api.UserLibrary
                 return false;
             }
 
-            // Min index number
-            if (request.MinIndexNumber.HasValue)
-            {
-                if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
-                {
-                    return false;
-                }
-            }
-
             // Min official rating
             if (!string.IsNullOrEmpty(request.MinOfficialRating))
             {

+ 19 - 4
MediaBrowser.Api/UserLibrary/UserViewsService.cs

@@ -26,6 +26,8 @@ namespace MediaBrowser.Api.UserLibrary
 
         [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
         public bool? IncludeExternalContent { get; set; }
+
+        public string PresetViews { get; set; }
     }
 
     [Route("/Users/{UserId}/SpecialViewOptions", "GET")]
@@ -75,9 +77,24 @@ namespace MediaBrowser.Api.UserLibrary
                 query.IncludeExternalContent = request.IncludeExternalContent.Value;
             }
 
+            if (!string.IsNullOrWhiteSpace(request.PresetViews))
+            {
+                query.PresetViews = request.PresetViews.Split(',');
+            }
+
+            var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
+            if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
+            }
+            //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
+
             var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
 
             var dtoOptions = GetDtoOptions(request);
+            dtoOptions.Fields = new List<ItemFields>();
+            dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio);
+            dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId);
 
             var user = _userManager.GetUserById(request.UserId);
 
@@ -123,7 +140,7 @@ namespace MediaBrowser.Api.UserLibrary
             var views = user.RootFolder
                 .GetChildren(user, true)
                 .OfType<Folder>()
-                .Where(i => !UserView.IsExcludedFromGrouping(i))
+                .Where(UserView.IsEligibleForGrouping)
                 .ToList();
 
             var list = views
@@ -141,9 +158,7 @@ namespace MediaBrowser.Api.UserLibrary
 
         private bool IsEligibleForSpecialView(ICollectionFolder view)
         {
-            var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
-
-            return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+            return UserView.IsEligibleForEnhancedView(view.CollectionType);
         }
     }
 

+ 1 - 1
MediaBrowser.Api/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 1 - 1
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -133,7 +133,7 @@ namespace MediaBrowser.Common.Implementations
         /// Gets the HTTP client.
         /// </summary>
         /// <value>The HTTP client.</value>
-        protected IHttpClient HttpClient { get; private set; }
+        public IHttpClient HttpClient { get; private set; }
         /// <summary>
         /// Gets the network manager.
         /// </summary>

+ 1 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             request.Method = method;
             request.Timeout = options.TimeoutMs;
-
+            
             if (httpWebRequest != null)
             {
                 if (!string.IsNullOrEmpty(options.Host))

+ 10 - 6
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -51,9 +51,11 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
     </Reference>
-    <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.dll</HintPath>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
+    <Reference Include="NLog">
+      <HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@@ -62,13 +64,14 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
     </Reference>
-    <Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath>
+    <Reference Include="SimpleInjector">
+      <HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
     <Reference Include="System.Net" />
     <Reference Include="System.Xml" />
     <Reference Include="ServiceStack.Text">
@@ -105,6 +108,7 @@
     <Compile Include="Security\SuppporterInfoResponse.cs" />
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
+    <Compile Include="Updates\GithubUpdater.cs" />
     <Compile Include="Updates\InstallationManager.cs" />
   </ItemGroup>
   <ItemGroup>

+ 89 - 58
MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs

@@ -7,6 +7,7 @@ using System.Net;
 using System.Net.NetworkInformation;
 using System.Net.Sockets;
 using System.Threading;
+using MoreLinq;
 
 namespace MediaBrowser.Common.Implementations.Networking
 {
@@ -31,14 +32,14 @@ namespace MediaBrowser.Common.Implementations.Networking
             }
         }
 
-        private volatile List<string> _localIpAddresses;
+		private volatile List<IPAddress> _localIpAddresses;
         private readonly object _localIpAddressSyncLock = new object();
 
         /// <summary>
         /// Gets the machine's local ip address
         /// </summary>
         /// <returns>IPAddress.</returns>
-        public IEnumerable<string> GetLocalIpAddresses()
+		public IEnumerable<IPAddress> GetLocalIpAddresses()
         {
             if (_localIpAddresses == null)
             {
@@ -58,25 +59,24 @@ namespace MediaBrowser.Common.Implementations.Networking
             return _localIpAddresses;
         }
 
-        private IEnumerable<string> GetLocalIpAddressesInternal()
+		private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
         {
             var list = GetIPsDefault()
-                .Where(i => !IPAddress.IsLoopback(i))
-                .Select(i => i.ToString())
-                .Where(FilterIpAddress)
                 .ToList();
 
-            if (list.Count > 0)
+            if (list.Count == 0)
             {
-                return list;
+				list.AddRange(GetLocalIpAddressesFallback());
             }
 
-            return GetLocalIpAddressesFallback().Where(FilterIpAddress);
+			return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
         }
 
-        private bool FilterIpAddress(string address)
+		private bool FilterIpAddress(IPAddress address)
         {
-            if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+			var addressString = address.ToString ();
+
+			if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
             {
                 return false;
             }
@@ -84,8 +84,16 @@ namespace MediaBrowser.Common.Implementations.Networking
             return true;
         }
 
-        private bool IsInPrivateAddressSpace(string endpoint)
+        public bool IsInPrivateAddressSpace(string endpoint)
         {
+            if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
+            {
+                return true;
+            }
+
+            // Handle ipv4 mapped to ipv6
+            endpoint = endpoint.Replace("::ffff:", string.Empty);
+
             // Private address space:
             // http://en.wikipedia.org/wiki/Private_network
 
@@ -96,9 +104,6 @@ namespace MediaBrowser.Common.Implementations.Networking
 
             return
 
-                // If url was requested with computer name, we may see this
-                endpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
-
                 endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
                 endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
                 endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
@@ -131,26 +136,41 @@ namespace MediaBrowser.Common.Implementations.Networking
                 throw new ArgumentNullException("endpoint");
             }
 
-            if (IsInPrivateAddressSpace(endpoint))
-            {
-                return true;
-            }
-
-            const int lengthMatch = 4;
-
-            if (endpoint.Length >= lengthMatch)
+            IPAddress address;
+            if (IPAddress.TryParse(endpoint, out address))
             {
-                var prefix = endpoint.Substring(0, lengthMatch);
+                var addressString = address.ToString();
 
-                if (GetLocalIpAddresses()
-                    .Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+                int lengthMatch = 100;
+                if (address.AddressFamily == AddressFamily.InterNetwork)
                 {
-                    return true;
+                    lengthMatch = 4;
+                    if (IsInPrivateAddressSpace(addressString))
+                    {
+                        return true;
+                    }
+                }
+                else if (address.AddressFamily == AddressFamily.InterNetworkV6)
+                {
+                    lengthMatch = 10;
+                    if (IsInPrivateAddressSpace(endpoint))
+                    {
+                        return true;
+                    }
                 }
-            }
 
-            IPAddress address;
-            if (resolveHost && !IPAddress.TryParse(endpoint, out address))
+                // Should be even be doing this with ipv6?
+                if (addressString.Length >= lengthMatch)
+                {
+                    var prefix = addressString.Substring(0, lengthMatch);
+
+					if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        return true;
+                    }
+                }
+            } 
+            else if (resolveHost)
             {
                 Uri uri;
                 if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri))
@@ -188,33 +208,45 @@ namespace MediaBrowser.Common.Implementations.Networking
             return Dns.GetHostAddresses(hostName);
         }
 
-        private IEnumerable<IPAddress> GetIPsDefault()
-        {
-            foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
-            {
-                var props = adapter.GetIPProperties();
-                var gateways = from ga in props.GatewayAddresses
-                               where !ga.Address.Equals(IPAddress.Any)
-                               select true;
-
-                if (!gateways.Any())
-                {
-                    continue;
-                }
-
-                foreach (var uni in props.UnicastAddresses)
-                {
-                    var address = uni.Address;
-                    if (address.AddressFamily != AddressFamily.InterNetwork)
-                    {
-                        continue;
-                    }
-                    yield return address;
-                }
-            }
-        }
-
-        private IEnumerable<string> GetLocalIpAddressesFallback()
+		private List<IPAddress> GetIPsDefault()
+		{
+			NetworkInterface[] interfaces;
+
+			try
+			{
+				interfaces = NetworkInterface.GetAllNetworkInterfaces();
+			}
+			catch (Exception ex)
+			{
+				Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
+				return new List<IPAddress>();
+			}
+
+			return interfaces.SelectMany(network => {
+
+				try
+				{
+                    Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
+
+					var properties = network.GetIPProperties();
+
+					return properties.UnicastAddresses
+                        .Where(i => i.IsDnsEligible)
+                        .Select(i => i.Address)
+                        .Where(i => i.AddressFamily == AddressFamily.InterNetwork)
+						.ToList();
+				}
+				catch (Exception ex)
+				{
+					Logger.ErrorException("Error querying network interface", ex);
+					return new List<IPAddress>();
+				}
+
+			}).DistinctBy(i => i.ToString())
+				.ToList();
+		}
+
+		private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
         {
             var host = Dns.GetHostEntry(Dns.GetHostName());
 
@@ -222,7 +254,6 @@ namespace MediaBrowser.Common.Implementations.Networking
             // It's not fool-proof so ultimately the consumer will have to examine them and decide
             return host.AddressList
                 .Where(i => i.AddressFamily == AddressFamily.InterNetwork)
-                .Select(i => i.ToString())
                 .Reverse();
         }
 

+ 212 - 0
MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs

@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Common.Implementations.Updates
+{
+    public class GithubUpdater
+    {
+        private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _jsonSerializer;
+        private TimeSpan _cacheLength;
+
+        public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength)
+        {
+            _httpClient = httpClient;
+            _jsonSerializer = jsonSerializer;
+            _cacheLength = cacheLength;
+        }
+
+        public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken)
+        {
+            var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
+
+            var options = new HttpRequestOptions
+            {
+                Url = url,
+                EnableKeepAlive = false,
+                CancellationToken = cancellationToken,
+                UserAgent = "Emby/3.0"
+
+            };
+
+            if (_cacheLength.Ticks > 0)
+            {
+                options.CacheMode = CacheMode.Unconditional;
+                options.CacheLength = _cacheLength;
+            }
+
+            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            {
+                var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+
+                return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+            }
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
+        {
+            if (updateLevel == PackageVersionClass.Release)
+            {
+                obj = obj.Where(i => !i.prerelease).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Beta)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Dev)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+
+            var availableUpdate = obj
+                .Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename))
+                .Where(i => i != null)
+                .OrderByDescending(i => Version.Parse(i.AvailableVersion))
+                .FirstOrDefault();
+            
+            return availableUpdate ?? new CheckForUpdateResult
+            {
+                IsUpdateAvailable = false
+            };
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
+        {
+            Version version;
+            if (!Version.TryParse(obj.tag_name, out version))
+            {
+                return null;
+            }
+
+            if (version < minVersion)
+            {
+                return null;
+            }
+
+            var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename));
+
+            if (asset == null)
+            {
+                return null;
+            }
+
+            return new CheckForUpdateResult
+            {
+                AvailableVersion = version.ToString(),
+                IsUpdateAvailable = version > minVersion,
+                Package = new PackageVersionInfo
+                {
+                    classification = obj.prerelease ?
+                        (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
+                        PackageVersionClass.Release,
+                    name = packageName,
+                    sourceUrl = asset.browser_download_url,
+                    targetFilename = targetFilename,
+                    versionStr = version.ToString(),
+                    requiredVersionStr = "1.0.0",
+                    description = obj.body
+                }
+            };
+        }
+
+        private bool IsAsset(Asset asset, string assetFilename)
+        {
+            var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
+
+            if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                return true;
+            }
+
+            return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase);
+        }
+
+        public class Uploader
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class Asset
+        {
+            public string url { get; set; }
+            public int id { get; set; }
+            public string name { get; set; }
+            public object label { get; set; }
+            public Uploader uploader { get; set; }
+            public string content_type { get; set; }
+            public string state { get; set; }
+            public int size { get; set; }
+            public int download_count { get; set; }
+            public string created_at { get; set; }
+            public string updated_at { get; set; }
+            public string browser_download_url { get; set; }
+        }
+
+        public class Author
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class RootObject
+        {
+            public string url { get; set; }
+            public string assets_url { get; set; }
+            public string upload_url { get; set; }
+            public string html_url { get; set; }
+            public int id { get; set; }
+            public string tag_name { get; set; }
+            public string target_commitish { get; set; }
+            public string name { get; set; }
+            public bool draft { get; set; }
+            public Author author { get; set; }
+            public bool prerelease { get; set; }
+            public string created_at { get; set; }
+            public string published_at { get; set; }
+            public List<Asset> assets { get; set; }
+            public string tarball_url { get; set; }
+            public string zipball_url { get; set; }
+            public string body { get; set; }
+        }
+    }
+}

+ 6 - 6
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -438,11 +438,12 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             if (package == null)
             {
@@ -495,7 +496,7 @@ namespace MediaBrowser.Common.Implementations.Updates
 
             try
             {
-                await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
+                await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
 
                 lock (CurrentInstallations)
                 {
@@ -551,18 +552,17 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package internal.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Do the install
             await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
 
-            var extension = Path.GetExtension(package.targetFilename) ?? "";
-
             // Do plugin-specific processing
-            if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase))
+            if (isPlugin)
             {
                 // Set last update time if we were installed before
                 var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))

+ 3 - 2
MediaBrowser.Common.Implementations/packages.config

@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="NLog" version="4.1.0" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
+  <package id="NLog" version="4.2.3" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-  <package id="SimpleInjector" version="3.0.5" targetFramework="net45" />
+  <package id="SimpleInjector" version="3.1.2" targetFramework="net45" />
 </packages>

+ 8 - 1
MediaBrowser.Common/Net/INetworkManager.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Common.Net
         /// Gets the machine's local ip address
         /// </summary>
         /// <returns>IPAddress.</returns>
-        IEnumerable<string> GetLocalIpAddresses();
+		IEnumerable<IPAddress> GetLocalIpAddresses();
 
         /// <summary>
         /// Gets a random port number that is currently available
@@ -25,6 +25,13 @@ namespace MediaBrowser.Common.Net
         /// <returns>[string] MAC Address</returns>
         string GetMacAddress();
 
+        /// <summary>
+        /// Determines whether [is in private address space] [the specified endpoint].
+        /// </summary>
+        /// <param name="endpoint">The endpoint.</param>
+        /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns>
+        bool IsInPrivateAddressSpace(string endpoint);
+
         /// <summary>
         /// Gets the network shares.
         /// </summary>

+ 2 - 1
MediaBrowser.Common/Updates/IInstallationManager.cs

@@ -105,11 +105,12 @@ namespace MediaBrowser.Common.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
+        Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Uninstalls a plugin

+ 2 - 2
MediaBrowser.Controller/Channels/ChannelAudioItem.cs

@@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Channels
 
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
+            return UnratedItem.ChannelContent;
         }
 
         protected override string CreateUserDataKey()

+ 6 - 0
MediaBrowser.Controller/Channels/ChannelFolderItem.cs

@@ -6,6 +6,7 @@ using System;
 using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Channels
@@ -20,6 +21,11 @@ namespace MediaBrowser.Controller.Channels
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.ChannelContent;
+        }
+
         [IgnoreDataMember]
         public override bool SupportsLocalMetadata
         {

+ 2 - 2
MediaBrowser.Controller/Channels/ChannelVideoItem.cs

@@ -42,9 +42,9 @@ namespace MediaBrowser.Controller.Channels
             return ExternalId;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
+            return UnratedItem.ChannelContent;
         }
 
         [IgnoreDataMember]

+ 16 - 18
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -27,9 +27,22 @@ namespace MediaBrowser.Controller.Entities.Audio
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
-        public List<string> Tags { get; set; }
         public ExtraType? ExtraType { get; set; }
 
+        /// <summary>
+        /// Gets or sets the artist.
+        /// </summary>
+        /// <value>The artist.</value>
+        public List<string> Artists { get; set; }
+
+        public List<string> AlbumArtists { get; set; }
+
+        /// <summary>
+        /// Gets or sets the album.
+        /// </summary>
+        /// <value>The album.</value>
+        public string Album { get; set; }
+
         [IgnoreDataMember]
         public bool IsThemeMedia
         {
@@ -43,7 +56,6 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             Artists = new List<string>();
             AlbumArtists = new List<string>();
-            Tags = new List<string>();
         }
 
         [IgnoreDataMember]
@@ -92,14 +104,6 @@ namespace MediaBrowser.Controller.Entities.Audio
                    locationType != LocationType.Virtual;
         }
 
-        /// <summary>
-        /// Gets or sets the artist.
-        /// </summary>
-        /// <value>The artist.</value>
-        public List<string> Artists { get; set; }
-
-        public List<string> AlbumArtists { get; set; }
-        
         [IgnoreDataMember]
         public List<string> AllArtists
         {
@@ -114,12 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             }
         }
 
-        /// <summary>
-        /// Gets or sets the album.
-        /// </summary>
-        /// <value>The album.</value>
-        public string Album { get; set; }
-
         [IgnoreDataMember]
         public MusicAlbum AlbumEntity
         {
@@ -173,9 +171,9 @@ namespace MediaBrowser.Controller.Entities.Audio
             return base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Music);
+            return UnratedItem.Music;
         }
 
         public SongInfo GetLookupInfo()

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

@@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             get
             {
-                return Parents.OfType<MusicArtist>().FirstOrDefault();
+                return GetParents().OfType<MusicArtist>().FirstOrDefault();
             }
         }
 
@@ -110,13 +110,18 @@ namespace MediaBrowser.Controller.Entities.Audio
             return config.BlockUnratedItems.Contains(UnratedItem.Music);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Music;
+        }
+
         public AlbumInfo GetLookupInfo()
         {
             var id = GetItemLookupInfo<AlbumInfo>();
 
             id.AlbumArtists = AlbumArtists;
 
-            var artist = Parents.OfType<MusicArtist>().FirstOrDefault();
+            var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
 
             if (artist != null)
             {

+ 5 - 0
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -138,6 +138,11 @@ namespace MediaBrowser.Controller.Entities.Audio
             return config.BlockUnratedItems.Contains(UnratedItem.Music);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Music;
+        }
+
         public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var items = GetRecursiveChildren().ToList();

+ 182 - 77
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Configuration;
@@ -24,6 +23,7 @@ using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
     {
         protected BaseItem()
         {
+            Tags = new List<string>();
             Genres = new List<string>();
             Studios = new List<string>();
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// The supported image extensions
         /// </summary>
-        public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" };
+        public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" };
 
         public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
 
@@ -103,7 +104,8 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the name.
         /// </summary>
         /// <value>The name.</value>
-        public string Name
+        [IgnoreDataMember]
+        public virtual string Name
         {
             get
             {
@@ -122,14 +124,23 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the id.
         /// </summary>
         /// <value>The id.</value>
+        [IgnoreDataMember]
         public Guid Id { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether this instance is hd.
         /// </summary>
         /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
         public bool? IsHD { get; set; }
 
+        /// <summary>
+        /// Gets or sets the audio.
+        /// </summary>
+        /// <value>The audio.</value>
+        [IgnoreDataMember]
+        public ProgramAudio? Audio { get; set; }
+
         /// <summary>
         /// Return the id that should be used to key display prefs for this item.
         /// Default is based on the type for everything except actual generic folders.
@@ -149,6 +160,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the path.
         /// </summary>
         /// <value>The path.</value>
+        [IgnoreDataMember]
         public virtual string Path { get; set; }
 
         [IgnoreDataMember]
@@ -173,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
         }
 
         /// <summary>
-        /// Id of the program.
+        /// If this content came from an external service, the id of the content on that service
         /// </summary>
         [IgnoreDataMember]
         public string ExternalId
@@ -201,11 +213,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        public virtual bool IsHiddenFromUser(User user)
-        {
-            return false;
-        }
-
         [IgnoreDataMember]
         public virtual bool IsOwnedItem
         {
@@ -325,12 +332,14 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the date created.
         /// </summary>
         /// <value>The date created.</value>
+        [IgnoreDataMember]
         public DateTime DateCreated { get; set; }
 
         /// <summary>
         /// Gets or sets the date modified.
         /// </summary>
         /// <value>The date modified.</value>
+        [IgnoreDataMember]
         public DateTime DateModified { get; set; }
 
         public DateTime DateLastSaved { get; set; }
@@ -407,6 +416,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the name of the forced sort.
         /// </summary>
         /// <value>The name of the forced sort.</value>
+        [IgnoreDataMember]
         public string ForcedSortName
         {
             get { return _forcedSortName; }
@@ -447,10 +457,7 @@ namespace MediaBrowser.Controller.Entities
         {
             var idString = Id.ToString("N");
 
-            if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
-            {
-                basePath = System.IO.Path.Combine(basePath, "library");
-            }
+            basePath = System.IO.Path.Combine(basePath, "library");
 
             return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
         }
@@ -493,6 +500,7 @@ namespace MediaBrowser.Controller.Entities
             return sortable;
         }
 
+        [IgnoreDataMember]
         public Guid ParentId { get; set; }
 
         /// <summary>
@@ -502,15 +510,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public Folder Parent
         {
-            get
-            {
-                if (ParentId != Guid.Empty)
-                {
-                    return LibraryManager.GetItemById(ParentId) as Folder;
-                }
-
-                return null;
-            }
+            get { return GetParent() as Folder; }
             set
             {
 
@@ -525,16 +525,28 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public IEnumerable<Folder> Parents
         {
-            get
+            get { return GetParents().OfType<Folder>(); }
+        }
+
+        public BaseItem GetParent()
+        {
+            if (ParentId != Guid.Empty)
             {
-                var parent = Parent;
+                return LibraryManager.GetItemById(ParentId);
+            }
 
-                while (parent != null)
-                {
-                    yield return parent;
+            return null;
+        }
 
-                    parent = parent.Parent;
-                }
+        public IEnumerable<BaseItem> GetParents()
+        {
+            var parent = GetParent();
+
+            while (parent != null)
+            {
+                yield return parent;
+
+                parent = parent.GetParent();
             }
         }
 
@@ -546,19 +558,20 @@ namespace MediaBrowser.Controller.Entities
         public T FindParent<T>()
             where T : Folder
         {
-            return Parents.OfType<T>().FirstOrDefault();
+            return GetParents().OfType<T>().FirstOrDefault();
         }
 
         [IgnoreDataMember]
         public virtual BaseItem DisplayParent
         {
-            get { return Parent; }
+            get { return GetParent(); }
         }
 
         /// <summary>
         /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
         /// </summary>
         /// <value>The premiere date.</value>
+        [IgnoreDataMember]
         public DateTime? PremiereDate { get; set; }
 
         /// <summary>
@@ -572,31 +585,35 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the display type of the media.
         /// </summary>
         /// <value>The display type of the media.</value>
+        [IgnoreDataMember]
         public string DisplayMediaType { get; set; }
 
         /// <summary>
         /// Gets or sets the official rating.
         /// </summary>
         /// <value>The official rating.</value>
+        [IgnoreDataMember]
         public string OfficialRating { get; set; }
 
         /// <summary>
         /// Gets or sets the official rating description.
         /// </summary>
         /// <value>The official rating description.</value>
+        [IgnoreDataMember]
         public string OfficialRatingDescription { get; set; }
 
         /// <summary>
         /// Gets or sets the custom rating.
         /// </summary>
         /// <value>The custom rating.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public string CustomRating { get; set; }
 
         /// <summary>
         /// Gets or sets the overview.
         /// </summary>
         /// <value>The overview.</value>
+        [IgnoreDataMember]
         public string Overview { get; set; }
 
         /// <summary>
@@ -609,37 +626,48 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the genres.
         /// </summary>
         /// <value>The genres.</value>
+        [IgnoreDataMember]
         public List<string> Genres { get; set; }
 
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
         /// <summary>
         /// Gets or sets the home page URL.
         /// </summary>
         /// <value>The home page URL.</value>
+        [IgnoreDataMember]
         public string HomePageUrl { get; set; }
 
         /// <summary>
         /// Gets or sets the community rating.
         /// </summary>
         /// <value>The community rating.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public float? CommunityRating { get; set; }
 
         /// <summary>
         /// Gets or sets the community rating vote count.
         /// </summary>
         /// <value>The community rating vote count.</value>
+        [IgnoreDataMember]
         public int? VoteCount { get; set; }
 
         /// <summary>
         /// Gets or sets the run time ticks.
         /// </summary>
         /// <value>The run time ticks.</value>
+        [IgnoreDataMember]
         public long? RunTimeTicks { get; set; }
 
         /// <summary>
         /// Gets or sets the production year.
         /// </summary>
         /// <value>The production year.</value>
+        [IgnoreDataMember]
         public int? ProductionYear { get; set; }
 
         /// <summary>
@@ -647,19 +675,34 @@ namespace MediaBrowser.Controller.Entities
         /// This could be episode number, album track number, etc.
         /// </summary>
         /// <value>The index number.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public int? IndexNumber { get; set; }
 
         /// <summary>
         /// For an episode this could be the season number, or for a song this could be the disc number.
         /// </summary>
         /// <value>The parent index number.</value>
+        [IgnoreDataMember]
         public int? ParentIndexNumber { get; set; }
 
         [IgnoreDataMember]
-        public virtual string OfficialRatingForComparison
+        public string OfficialRatingForComparison
         {
-            get { return OfficialRating; }
+            get
+            {
+                if (!string.IsNullOrWhiteSpace(OfficialRating))
+                {
+                    return OfficialRating;
+                }
+
+                var parent = DisplayParent;
+                if (parent != null)
+                {
+                    return parent.OfficialRatingForComparison;
+                }
+
+                return null;
+            }
         }
 
         [IgnoreDataMember]
@@ -721,21 +764,21 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.ResolvePaths(files, directoryService, null)
                 .OfType<Audio.Audio>()
                 .Select(audio =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
-
-                if (dbItem != null)
                 {
-                    audio = dbItem;
-                }
+                    // Try to retrieve it from the db. If we don't find it, use the resolved version
+                    var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
 
-                audio.ExtraType = ExtraType.ThemeSong;
+                    if (dbItem != null)
+                    {
+                        audio = dbItem;
+                    }
 
-                return audio;
+                    audio.ExtraType = ExtraType.ThemeSong;
 
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
+                    return audio;
+
+                    // Sort them so that the list can be easily compared for changes
+                }).OrderBy(i => i.Path).ToList();
         }
 
         /// <summary>
@@ -751,21 +794,21 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.ResolvePaths(files, directoryService, null)
                 .OfType<Video>()
                 .Select(item =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(item.Id) as Video;
-
-                if (dbItem != null)
                 {
-                    item = dbItem;
-                }
+                    // Try to retrieve it from the db. If we don't find it, use the resolved version
+                    var dbItem = LibraryManager.GetItemById(item.Id) as Video;
+
+                    if (dbItem != null)
+                    {
+                        item = dbItem;
+                    }
 
-                item.ExtraType = ExtraType.ThemeVideo;
+                    item.ExtraType = ExtraType.ThemeVideo;
 
-                return item;
+                    return item;
 
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
+                    // Sort them so that the list can be easily compared for changes
+                }).OrderBy(i => i.Path).ToList();
         }
 
         public Task RefreshMetadata(CancellationToken cancellationToken)
@@ -821,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         protected virtual bool SupportsOwnedItems
         {
-            get { return IsFolder || Parent != null; }
+            get { return IsFolder || GetParent() != null; }
         }
 
         [IgnoreDataMember]
@@ -846,7 +889,7 @@ namespace MediaBrowser.Controller.Entities
 
             var localTrailersChanged = false;
 
-            if (LocationType == LocationType.FileSystem && Parent != null)
+            if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
                 var hasThemeMedia = this as IHasThemeMedia;
                 if (hasThemeMedia != null)
@@ -1008,7 +1051,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrWhiteSpace(lang))
             {
-                lang = Parents
+                lang = GetParents()
                     .Select(i => i.PreferredMetadataLanguage)
                     .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
             }
@@ -1038,7 +1081,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrWhiteSpace(lang))
             {
-                lang = Parents
+                lang = GetParents()
                     .Select(i => i.PreferredMetadataCountryCode)
                     .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
             }
@@ -1119,6 +1162,23 @@ namespace MediaBrowser.Controller.Entities
         }
 
         public int? GetParentalRatingValue()
+        {
+            var rating = CustomRating;
+
+            if (string.IsNullOrWhiteSpace(rating))
+            {
+                rating = OfficialRating;
+            }
+
+            if (string.IsNullOrWhiteSpace(rating))
+            {
+                return null;
+            }
+
+            return LocalizationManager.GetRatingLevel(rating);
+        }
+
+        public int? GetInheritedParentalRatingValue()
         {
             var rating = CustomRatingForComparison;
 
@@ -1156,6 +1216,11 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
+        public virtual UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Other;
+        }
+
         /// <summary>
         /// Gets the block unrated value.
         /// </summary>
@@ -1174,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            return config.BlockUnratedItems.Contains(UnratedItem.Other);
+            return config.BlockUnratedItems.Contains(GetBlockUnratedType());
         }
 
         /// <summary>
@@ -1206,14 +1271,14 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            if (Parents.Any(i => !i.IsVisible(user)))
+            if (GetParents().Any(i => !i.IsVisible(user)))
             {
                 return false;
             }
 
             if (checkFolders)
             {
-                var topParent = Parents.LastOrDefault() ?? this;
+                var topParent = GetParents().LastOrDefault() ?? this;
 
                 if (string.IsNullOrWhiteSpace(topParent.Path))
                 {
@@ -1307,15 +1372,6 @@ namespace MediaBrowser.Controller.Entities
             return null;
         }
 
-        /// <summary>
-        /// Adds a person to the item
-        /// </summary>
-        /// <param name="person">The person.</param>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public void AddPerson(PersonInfo person)
-        {
-        }
-
         /// <summary>
         /// Adds a studio to the item
         /// </summary>
@@ -1779,8 +1835,8 @@ namespace MediaBrowser.Controller.Entities
                 ProviderIds = ProviderIds,
                 IndexNumber = IndexNumber,
                 ParentIndexNumber = ParentIndexNumber,
-                Year = ProductionYear,
-                PremiereDate = PremiereDate
+				Year = ProductionYear,
+				PremiereDate = PremiereDate
             };
         }
 
@@ -1875,5 +1931,54 @@ namespace MediaBrowser.Controller.Entities
                 DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
             };
         }
+
+        public virtual IEnumerable<Guid> GetAncestorIds()
+        {
+            return GetParents().Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id));
+        }
+
+        public BaseItem GetTopParent()
+        {
+            if (IsTopParent)
+            {
+                return this;
+            }
+
+            return GetParents().FirstOrDefault(i => i.IsTopParent);
+        }
+
+        [IgnoreDataMember]
+        public virtual bool IsTopParent
+        {
+            get
+            {
+                if (GetParent() is AggregateFolder || this is Channel || this is BasePluginFolder)
+                {
+                    return true;
+                }
+
+                var view = this as UserView;
+                if (view != null && string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+
+                return false;
+            }
+        }
+
+        [IgnoreDataMember]
+        public virtual bool SupportsAncestors
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
+        {
+            return new[] { Id };
+        }
     }
-}
+}

+ 3 - 14
MediaBrowser.Controller/Entities/Book.cs

@@ -17,19 +17,8 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         public string SeriesName { get; set; }
 
-        public Book()
-        {
-            Tags = new List<string>();
-        }
-
         public override bool CanDownload()
         {
             var locationType = LocationType;
@@ -37,9 +26,9 @@ namespace MediaBrowser.Controller.Entities
                    locationType != LocationType.Virtual;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Book);
+            return UnratedItem.Book;
         }
 
         public BookInfo GetLookupInfo()
@@ -48,7 +37,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrEmpty(SeriesName))
             {
-                info.SeriesName = Parents.Select(i => i.Name).FirstOrDefault();
+                info.SeriesName = GetParents().Select(i => i.Name).FirstOrDefault();
             }
             else
             {

+ 8 - 7
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -181,9 +181,7 @@ namespace MediaBrowser.Controller.Entities
         }
         private List<LinkedChild> GetLinkedChildrenInternal()
         {
-            return LibraryManager.RootFolder.Children
-                .OfType<Folder>()
-                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
+            return GetPhysicalParents()
                 .SelectMany(c => c.LinkedChildren)
                 .ToList();
         }
@@ -199,11 +197,14 @@ namespace MediaBrowser.Controller.Entities
 
         private IEnumerable<BaseItem> GetActualChildren()
         {
-            return
-                LibraryManager.RootFolder.Children
+            return GetPhysicalParents().SelectMany(c => c.Children);
+        }
+
+        public IEnumerable<Folder> GetPhysicalParents()
+        {
+            return LibraryManager.RootFolder.Children
                 .OfType<Folder>()
-                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
-                .SelectMany(c => c.Children);
+                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
         }
 
         [IgnoreDataMember]

+ 55 - 77
MediaBrowser.Controller/Entities/Folder.cs

@@ -28,7 +28,6 @@ namespace MediaBrowser.Controller.Entities
 
         public List<Guid> ThemeSongIds { get; set; }
         public List<Guid> ThemeVideoIds { get; set; }
-        public List<string> Tags { get; set; }
 
         public Folder()
         {
@@ -36,7 +35,6 @@ namespace MediaBrowser.Controller.Entities
 
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
-            Tags = new List<string>();
         }
 
         [IgnoreDataMember]
@@ -151,7 +149,15 @@ namespace MediaBrowser.Controller.Entities
 
             await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
 
-            await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+            if (!EnableNewFolderQuerying())
+            {
+                await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+            }
+        }
+
+        private static bool EnableNewFolderQuerying()
+        {
+            return ConfigurationManager.Configuration.MigrationVersion >= 1;
         }
 
         protected void AddChildrenInternal(IEnumerable<BaseItem> children)
@@ -196,21 +202,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                // Never want folders to be blocked by "BlockNotRated"
-                if (this is Series)
-                {
-                    return base.OfficialRatingForComparison;
-                }
-
-                return !string.IsNullOrWhiteSpace(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None";
-            }
-        }
-
         /// <summary>
         /// Removes the child.
         /// </summary>
@@ -224,7 +215,12 @@ namespace MediaBrowser.Controller.Entities
 
             item.SetParent(null);
 
-            return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
+            if (!EnableNewFolderQuerying())
+            {
+                return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
+            }
+
+            return Task.FromResult(true);
         }
 
         /// <summary>
@@ -457,32 +453,25 @@ namespace MediaBrowser.Controller.Entities
                 {
                     BaseItem currentChild;
 
-                    if (currentChildren.TryGetValue(child.Id, out currentChild))
+                    if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
                     {
-                        if (IsValidFromResolver(currentChild, child))
+                        var currentChildLocationType = currentChild.LocationType;
+                        if (currentChildLocationType != LocationType.Remote &&
+                            currentChildLocationType != LocationType.Virtual)
                         {
-                            var currentChildLocationType = currentChild.LocationType;
-                            if (currentChildLocationType != LocationType.Remote &&
-                                currentChildLocationType != LocationType.Virtual)
-                            {
-                                currentChild.DateModified = child.DateModified;
-                            }
-
-                            await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
-                            validChildren.Add(currentChild);
-                        }
-                        else
-                        {
-                            newItems.Add(child);
-                            validChildren.Add(child);
+                            currentChild.DateModified = child.DateModified;
                         }
+
+                        await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
+                        validChildren.Add(currentChild);
+
+                        continue;
                     }
-                    else
-                    {
-                        // Brand new item - needs to be added
-                        newItems.Add(child);
-                        validChildren.Add(child);
-                    }
+
+                    // Brand new item - needs to be added
+                    child.SetParent(this);
+                    newItems.Add(child);
+                    validChildren.Add(child);
                 }
 
                 // If any items were added or removed....
@@ -508,7 +497,6 @@ namespace MediaBrowser.Controller.Entities
                         }
                         else
                         {
-                            await UpdateIsOffline(item, false).ConfigureAwait(false);
                             actualRemovals.Add(item);
                         }
                     }
@@ -519,6 +507,11 @@ namespace MediaBrowser.Controller.Entities
 
                         foreach (var item in actualRemovals)
                         {
+                            Logger.Debug("Removed item: " + item.Path);
+
+                            item.SetParent(null);
+                            item.IsOffline = false;
+                            await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false);
                             LibraryManager.ReportItemRemoved(item);
                         }
                     }
@@ -527,7 +520,10 @@ namespace MediaBrowser.Controller.Entities
 
                     AddChildrenInternal(newItems);
 
-                    await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+                    if (!EnableNewFolderQuerying())
+                    {
+                        await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+                    }
                 }
             }
 
@@ -721,7 +717,7 @@ namespace MediaBrowser.Controller.Entities
                 return true;
             }
 
-            return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath);
+            return false;
         }
 
         /// <summary>
@@ -757,19 +753,16 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected IEnumerable<BaseItem> GetCachedChildren()
         {
-            if (ConfigurationManager.Configuration.DisableStartupScan)
+            if (EnableNewFolderQuerying())
             {
-                return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
-                //return ItemRepository.GetItems(new InternalItemsQuery
-                //{
-                //    ParentId = Id
+                return ItemRepository.GetItemList(new InternalItemsQuery
+                {
+                    ParentId = Id
 
-                //}).Items.Select(RetrieveChild).Where(i => i != null);
-            }
-            else
-            {
-                return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
+                }).Select(RetrieveChild).Where(i => i != null);
             }
+
+            return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
         }
 
         private BaseItem RetrieveChild(BaseItem child)
@@ -832,19 +825,7 @@ namespace MediaBrowser.Controller.Entities
             return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
         }
 
-        /// <summary>
-        /// Gets allowed children of an item
-        /// </summary>
-        /// <param name="user">The user.</param>
-        /// <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> GetChildren(User user, bool includeLinkedChildren)
-        {
-            return GetChildren(user, includeLinkedChildren, false);
-        }
-
-        internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden)
         {
             if (user == null)
             {
@@ -856,7 +837,7 @@ namespace MediaBrowser.Controller.Entities
 
             var result = new Dictionary<Guid, BaseItem>();
 
-            AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
+            AddChildren(user, includeLinkedChildren, result, false, null);
 
             return result.Values;
         }
@@ -872,29 +853,25 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="user">The user.</param>
         /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</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 void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter)
+        private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter)
         {
             foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
             {
                 if (child.IsVisible(user))
                 {
-                    if (includeHidden || !child.IsHiddenFromUser(user))
+                    if (filter == null || filter(child))
                     {
-                        if (filter == null || filter(child))
-                        {
-                            result[child.Id] = child;
-                        }
+                        result[child.Id] = child;
                     }
 
                     if (recursive && child.IsFolder)
                     {
                         var folder = (Folder)child;
 
-                        folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
+                        folder.AddChildren(user, includeLinkedChildren, result, true, filter);
                     }
                 }
             }
@@ -935,7 +912,7 @@ namespace MediaBrowser.Controller.Entities
 
             var result = new Dictionary<Guid, BaseItem>();
 
-            AddChildren(user, true, result, false, true, filter);
+            AddChildren(user, true, result, true, filter);
 
             return result.Values;
         }
@@ -1184,6 +1161,7 @@ namespace MediaBrowser.Controller.Entities
                 Recursive = true,
                 IsFolder = false,
                 IsUnaired = false
+
             };
 
             if (!user.Configuration.DisplayMissingEpisodes)
@@ -1322,4 +1300,4 @@ namespace MediaBrowser.Controller.Entities
             }
         }
     }
-}
+}

+ 2 - 9
MediaBrowser.Controller/Entities/Game.cs

@@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities
             RemoteTrailerIds = new List<Guid>();
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
-            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
@@ -34,12 +33,6 @@ namespace MediaBrowser.Controller.Entities
                    locationType != LocationType.Virtual;
         }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         /// <summary>
         /// Gets or sets the remote trailers.
         /// </summary>
@@ -105,9 +98,9 @@ namespace MediaBrowser.Controller.Entities
             return base.GetDeletePaths();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Game);
+            return UnratedItem.Game;
         }
 
         public GameInfo GetLookupInfo()

+ 5 - 0
MediaBrowser.Controller/Entities/GameSystem.cs

@@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.Entities
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Game;
+        }
+
         public GameSystemInfo GetLookupInfo()
         {
             var id = GetItemLookupInfo<GameSystemInfo>();

+ 5 - 0
MediaBrowser.Controller/Entities/ICollectionFolder.cs

@@ -16,6 +16,11 @@ namespace MediaBrowser.Controller.Entities
         IEnumerable<string> PhysicalLocations { get; }
     }
 
+    public interface ISupportsUserSpecificView
+    {
+        bool EnableUserSpecificView { get; }
+    }
+
     public static class CollectionFolderExtensions
     {
         public static string GetViewType(this ICollectionFolder folder, User user)

+ 18 - 0
MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHiddenFromDisplay
+    {
+        /// <summary>
+        /// Determines whether the specified user is hidden.
+        /// </summary>
+        /// <param name="user">The user.</param>
+        /// <returns><c>true</c> if the specified user is hidden; otherwise, <c>false</c>.</returns>
+        bool IsHiddenFromUser(User user);
+    }
+}

+ 35 - 0
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -30,6 +31,7 @@ namespace MediaBrowser.Controller.Entities
         public string[] MediaTypes { get; set; }
         public string[] IncludeItemTypes { get; set; }
         public string[] ExcludeItemTypes { get; set; }
+        public string[] ExcludeTags { get; set; }
         public string[] Genres { get; set; }
 
         public bool? IsMissing { get; set; }
@@ -69,12 +71,15 @@ namespace MediaBrowser.Controller.Entities
 
         public string[] Studios { get; set; }
         public string[] StudioIds { get; set; }
+        public string[] GenreIds { get; set; }
         public ImageType[] ImageTypes { get; set; }
         public VideoType[] VideoTypes { get; set; }
+        public UnratedItem[] BlockUnratedItems { get; set; }
         public int[] Years { get; set; }
         public string[] Tags { get; set; }
         public string[] OfficialRatings { get; set; }
 
+        public DateTime? MinPremiereDate { get; set; }
         public DateTime? MinStartDate { get; set; }
         public DateTime? MaxStartDate { get; set; }
         public DateTime? MinEndDate { get; set; }
@@ -87,6 +92,7 @@ namespace MediaBrowser.Controller.Entities
 
         public int? MinPlayers { get; set; }
         public int? MaxPlayers { get; set; }
+        public int? MinIndexNumber { get; set; }
         public double? MinCriticRating { get; set; }
         public double? MinCommunityRating { get; set; }
        
@@ -101,9 +107,14 @@ namespace MediaBrowser.Controller.Entities
         public LocationType? LocationType { get; set; }
 
         public Guid? ParentId { get; set; }
+        public string[] AncestorIds { get; set; }
+        public string[] TopParentIds { get; set; }
+
+        public LocationType[] ExcludeLocationTypes { get; set; }
         
         public InternalItemsQuery()
         {
+            BlockUnratedItems = new UnratedItem[] { };
             Tags = new string[] { };
             OfficialRatings = new string[] { };
             SortBy = new string[] { };
@@ -113,6 +124,7 @@ namespace MediaBrowser.Controller.Entities
             Genres = new string[] { };
             Studios = new string[] { };
             StudioIds = new string[] { };
+            GenreIds = new string[] { };
             ImageTypes = new ImageType[] { };
             VideoTypes = new VideoType[] { };
             Years = new int[] { };
@@ -120,6 +132,29 @@ namespace MediaBrowser.Controller.Entities
             PersonIds = new string[] { };
             ChannelIds = new string[] { };
             ItemIds = new string[] { };
+            AncestorIds = new string[] { };
+            TopParentIds = new string[] { };
+            ExcludeTags = new string[] { };
+            ExcludeLocationTypes = new LocationType[] { };
+        }
+
+        public InternalItemsQuery(User user)
+            : this()
+        {
+            if (user != null)
+            {
+                var policy = user.Policy;
+                MaxParentalRating = policy.MaxParentalRating;
+
+                if (policy.MaxParentalRating.HasValue)
+                {
+                    BlockUnratedItems = policy.BlockUnratedItems;
+                }
+
+                ExcludeTags = policy.BlockedTags;
+                
+                User = user;
+            }
         }
     }
 }

+ 6 - 31
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -8,15 +8,13 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Entities.Movies
 {
     /// <summary>
     /// Class BoxSet
     /// </summary>
-    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
+    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares
     {
         public List<Share> Shares { get; set; }
 
@@ -65,6 +63,11 @@ namespace MediaBrowser.Controller.Entities.Movies
             return config.BlockUnratedItems.Contains(UnratedItem.Movie);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Movie;
+        }
+
         [IgnoreDataMember]
         public override bool IsPreSorted
         {
@@ -154,34 +157,6 @@ namespace MediaBrowser.Controller.Entities.Movies
             return GetItemLookupInfo<BoxSetInfo>();
         }
 
-        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
-        {
-            // Refresh bottom up, children first, then the boxset
-            // By then hopefully the  movies within will have Tmdb collection values
-            var items = GetRecursiveChildren().ToList();
-
-            var totalItems = items.Count;
-            var numComplete = 0;
-
-            // Refresh songs
-            foreach (var item in items)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-
-                await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-                numComplete++;
-                double percent = numComplete;
-                percent /= totalItems;
-                progress.Report(percent * 100);
-            }
-
-            // Refresh current item
-            await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-            progress.Report(100);
-        }
-
         public override bool IsVisible(User user)
         {
             var userId = user.Id.ToString("N");

+ 3 - 3
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -130,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.Movies
 
             // Must have a parent to have special features
             // In other words, it must be part of the Parent/Child tree
-            if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder)
+            if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder)
             {
                 var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
@@ -159,9 +159,9 @@ namespace MediaBrowser.Controller.Entities.Movies
             return itemsChanged;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Movie);
+            return UnratedItem.Movie;
         }
 
         public MovieInfo GetLookupInfo()

+ 2 - 2
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -56,9 +56,9 @@ namespace MediaBrowser.Controller.Entities
             return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Music);
+            return UnratedItem.Music;
         }
 
         public MusicVideoInfo GetLookupInfo()

+ 9 - 0
MediaBrowser.Controller/Entities/Person.cs

@@ -101,6 +101,15 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsAncestors
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 
     /// <summary>

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

@@ -9,12 +9,10 @@ namespace MediaBrowser.Controller.Entities
 {
     public class Photo : BaseItem, IHasTags, IHasTaglines
     {
-        public List<string> Tags { get; set; }
         public List<string> Taglines { get; set; }
 
         public Photo()
         {
-            Tags = new List<string>();
             Taglines = new List<string>();
         }
 
@@ -51,7 +49,7 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return Parents.OfType<PhotoAlbum>().FirstOrDefault();
+                return GetParents().OfType<PhotoAlbum>().FirstOrDefault();
             }
         }
 
@@ -70,10 +68,5 @@ namespace MediaBrowser.Controller.Entities
         public double? Longitude { get; set; }
         public double? Altitude { get; set; }
         public int? IsoSpeedRating { get; set; }
-
-        protected override bool GetBlockUnratedValue(UserPolicy config)
-        {
-            return config.BlockUnratedItems.Contains(UnratedItem.Other);
-        }
     }
 }

+ 2 - 27
MediaBrowser.Controller/Entities/PhotoAlbum.cs

@@ -9,8 +9,9 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class PhotoAlbum : Folder, IMetadataContainer
+    public class PhotoAlbum : Folder
     {
+        [IgnoreDataMember]
         public override bool SupportsLocalMetadata
         {
             get
@@ -32,31 +33,5 @@ namespace MediaBrowser.Controller.Entities
         {
             return config.BlockUnratedItems.Contains(UnratedItem.Other);
         }
-
-        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
-        {
-            var items = GetRecursiveChildren().ToList();
-
-            var totalItems = items.Count;
-            var numComplete = 0;
-
-            // Refresh songs
-            foreach (var item in items)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-
-                await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-                numComplete++;
-                double percent = numComplete;
-                percent /= totalItems;
-                progress.Report(percent * 100);
-            }
-
-            // Refresh current item
-            await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-            progress.Report(100);
-        }
     }
 }

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

@@ -10,13 +10,6 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Studio : BaseItem, IItemByName, IHasTags
     {
-        public List<string> Tags { get; set; }
-
-        public Studio()
-        {
-            Tags = new List<string>();
-        }
-
         /// <summary>
         /// Gets the user data key.
         /// </summary>

+ 17 - 16
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             get
             {
-                return Season ?? Parent;
+                return Season ?? GetParent();
             }
         }
 
@@ -115,19 +115,6 @@ namespace MediaBrowser.Controller.Entities.TV
             return base.CreateUserDataKey();
         }
 
-        /// <summary>
-        /// Our rating comes from our series
-        /// </summary>
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                var series = Series;
-                return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
-            }
-        }
-
         /// <summary>
         /// This Episode's Series Instance
         /// </summary>
@@ -265,14 +252,28 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
+        public override IEnumerable<Guid> GetAncestorIds()
+        {
+            var list = base.GetAncestorIds().ToList();
+
+            var seasonId = SeasonId;
+
+            if (seasonId.HasValue && !list.Contains(seasonId.Value))
+            {
+                list.Add(seasonId.Value);
+            }
+
+            return list;
+        }
+
         public override IEnumerable<string> GetDeletePaths()
         {
             return new[] { Path };
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Series);
+            return UnratedItem.Series;
         }
 
         public EpisodeInfo GetLookupInfo()

+ 7 - 14
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -6,6 +6,7 @@ using MoreLinq;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.Entities.TV
 {
@@ -32,7 +33,7 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public override BaseItem DisplayParent
         {
-            get { return Series ?? Parent; }
+            get { return Series ?? GetParent(); }
         }
 
         // Genre, Rating and Stuido will all be the same
@@ -87,19 +88,6 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
-        /// <summary>
-        /// Our rating comes from our series
-        /// </summary>
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                var series = Series;
-                return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
-            }
-        }
-
         /// <summary>
         /// Creates the name of the sort.
         /// </summary>
@@ -234,6 +222,11 @@ namespace MediaBrowser.Controller.Entities.TV
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Series;
+        }
+
         [IgnoreDataMember]
         public string SeriesName
         {

+ 5 - 0
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -333,6 +333,11 @@ namespace MediaBrowser.Controller.Entities.TV
             return config.BlockUnratedItems.Contains(UnratedItem.Series);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Series;
+        }
+
         public SeriesInfo GetLookupInfo()
         {
             var info = GetItemLookupInfo<SeriesInfo>();

+ 3 - 3
MediaBrowser.Controller/Entities/Trailer.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Entities
             get
             {
                 // Local trailers are not part of children
-                return Parent == null;
+                return GetParent() == null;
             }
         }
 
@@ -97,9 +97,9 @@ namespace MediaBrowser.Controller.Entities
             return base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Trailer);
+            return UnratedItem.Trailer;
         }
 
         public TrailerInfo GetLookupInfo()

+ 20 - 1
MediaBrowser.Controller/Entities/User.cs

@@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities
     {
         public static IUserManager UserManager { get; set; }
         public static IXmlSerializer XmlSerializer { get; set; }
-        public bool EnableUserViews { get; set; }
 
         /// <summary>
         /// From now on all user paths will be Id-based. 
@@ -58,6 +57,26 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        private string _name;
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public override string Name
+        {
+            get
+            {
+                return _name;
+            }
+            set
+            {
+                _name = value;
+
+                // lazy load this again
+                SortName = null;
+            }
+        }
+
         /// <summary>
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself

+ 9 - 1
MediaBrowser.Controller/Entities/UserRootFolder.cs

@@ -55,13 +55,21 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+        {
+            var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
+            list.AddRange(LibraryManager.RootFolder.VirtualChildren);
+
+            return list;
+        }
+
         /// <summary>
         /// Get the children of this folder from the actual file system
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
         {
-            return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren);
+            return base.GetNonCachedChildren(directoryService);
         }
 
         public override bool BeforeMetadataRefresh()

+ 73 - 20
MediaBrowser.Controller/Entities/UserView.cs

@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.Runtime.Serialization;
 using System.Threading.Tasks;
+using System.Linq;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
         public Guid DisplayParentId { get; set; }
 
         public Guid? UserId { get; set; }
-        
+
         public static ITVSeriesManager TVSeriesManager;
         public static IPlaylistManager PlaylistManager;
 
@@ -24,7 +25,26 @@ namespace MediaBrowser.Controller.Entities
         {
             return true;
         }
-        
+
+        public override IEnumerable<Guid> GetIdsForAncestorQuery()
+        {
+            var list = new List<Guid>();
+
+            if (DisplayParentId != Guid.Empty)
+            {
+                list.Add(DisplayParentId);
+            }
+            else if (ParentId != Guid.Empty)
+            {
+                list.Add(ParentId);
+            }
+            else
+            {
+                list.Add(Id);
+            }
+            return list;
+        }
+
         public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
             var parent = this as Folder;
@@ -81,16 +101,11 @@ namespace MediaBrowser.Controller.Entities
             return GetChildren(user, false);
         }
 
-        public static bool IsExcludedFromGrouping(Folder folder)
+        public static bool IsUserSpecific(Folder folder)
         {
             var standaloneTypes = new List<string>
             {
-                CollectionType.Books,
-                CollectionType.HomeVideos,
-                CollectionType.Photos,
-                CollectionType.Playlists,
-                CollectionType.BoxSets,
-                CollectionType.MusicVideos
+                CollectionType.Playlists
             };
 
             var collectionFolder = folder as ICollectionFolder;
@@ -100,25 +115,63 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            var supportsUserSpecific = folder as ISupportsUserSpecificView;
+            if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView)
+            {
+                return true;
+            }
+
             return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
         }
 
-        public static bool IsUserSpecific(Folder folder)
+        public static bool IsEligibleForGrouping(Folder folder)
         {
-            var standaloneTypes = new List<string>
-            {
-                CollectionType.Playlists,
-                CollectionType.BoxSets
+            var collectionFolder = folder as ICollectionFolder;
+            return collectionFolder != null && IsEligibleForGrouping(collectionFolder.CollectionType);
+        }
+
+        public static bool IsEligibleForGrouping(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Movies, 
+                CollectionType.TvShows,
+                string.Empty
             };
 
-            var collectionFolder = folder as ICollectionFolder;
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
 
-            if (collectionFolder == null)
-            {
-                return false;
-            }
+        public static bool IsEligibleForEnhancedView(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Movies, 
+                CollectionType.TvShows 
+            };
 
-            return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
+
+        public static bool EnableOriginalFolder(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Games, 
+                CollectionType.Books, 
+                CollectionType.MusicVideos, 
+                CollectionType.HomeVideos, 
+                CollectionType.Photos, 
+                CollectionType.Music, 
+                CollectionType.BoxSets
+            };
+
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
+
+        protected override Task ValidateChildrenInternal(IProgress<double> progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService)
+        {
+            return Task.FromResult(true);
         }
 
         [IgnoreDataMember]

+ 89 - 166
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -120,59 +120,34 @@ namespace MediaBrowser.Controller.Entities
                         return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false);
                     }
 
+                case CollectionType.Photos:
                 case CollectionType.Books:
                 case CollectionType.HomeVideos:
+                case CollectionType.Games:
                 case CollectionType.MusicVideos:
+                {
+                    if (query.Recursive)
+                    {
+                        return GetResult(queryParent.GetRecursiveChildren(user, true), queryParent, query);
+                    }
                     return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+                }
 
                 case CollectionType.Folders:
                     return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query);
 
-                case CollectionType.Games:
-                    return await GetGameView(user, queryParent, query).ConfigureAwait(false);
-
                 case CollectionType.Playlists:
                     return await GetPlaylistsView(queryParent, user, query).ConfigureAwait(false);
 
                 case CollectionType.BoxSets:
                     return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
 
-                case CollectionType.Photos:
-                    return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
-
                 case CollectionType.TvShows:
                     return await GetTvView(queryParent, user, query).ConfigureAwait(false);
 
-                case CollectionType.Music:
-                    return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
-
                 case CollectionType.Movies:
                     return await GetMovieFolders(queryParent, user, query).ConfigureAwait(false);
 
-                case SpecialFolder.MusicGenres:
-                    return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.MusicGenre:
-                    return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameGenres:
-                    return await GetGameGenres(queryParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameGenre:
-                    return await GetGameGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameSystems:
-                    return GetGameSystems(queryParent, user, query);
-
-                case SpecialFolder.LatestGames:
-                    return GetLatestGames(queryParent, user, query);
-
-                case SpecialFolder.RecentlyPlayedGames:
-                    return GetRecentlyPlayedGames(queryParent, user, query);
-
-                case SpecialFolder.GameFavorites:
-                    return GetFavoriteGames(queryParent, user, query);
-
                 case SpecialFolder.TvShowSeries:
                     return GetTvSeries(queryParent, user, query);
 
@@ -212,6 +187,21 @@ namespace MediaBrowser.Controller.Entities
                 case SpecialFolder.MovieCollections:
                     return GetMovieCollections(queryParent, user, query);
 
+                case SpecialFolder.TvFavoriteEpisodes:
+                    return GetFavoriteEpisodes(queryParent, user, query);
+
+                case SpecialFolder.TvFavoriteSeries:
+                    return GetFavoriteSeries(queryParent, user, query);
+
+                case CollectionType.Music:
+                    return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
+
+                case SpecialFolder.MusicGenres:
+                    return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
+
+                case SpecialFolder.MusicGenre:
+                    return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
+
                 case SpecialFolder.MusicLatest:
                     return GetMusicLatest(queryParent, user, query);
 
@@ -230,12 +220,6 @@ namespace MediaBrowser.Controller.Entities
                 case SpecialFolder.MusicSongs:
                     return GetMusicSongs(queryParent, user, query);
 
-                case SpecialFolder.TvFavoriteEpisodes:
-                    return GetFavoriteEpisodes(queryParent, user, query);
-
-                case SpecialFolder.TvFavoriteSeries:
-                    return GetFavoriteSeries(queryParent, user, query);
-
                 case SpecialFolder.MusicFavorites:
                     return await GetMusicFavorites(queryParent, user, query).ConfigureAwait(false);
 
@@ -262,18 +246,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
-        {
-            var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
-
-            return GetResult(list, parent, query);
-        }
-
-        private int GetSpecialItemsLimit()
-        {
-            return 50;
-        }
-
         private async Task<QueryResult<BaseItem>> GetMusicFolders(Folder parent, User user, InternalItemsQuery query)
         {
             if (query.Recursive)
@@ -289,7 +261,7 @@ namespace MediaBrowser.Controller.Entities
             list.Add(await GetUserView(SpecialFolder.MusicPlaylists, "1", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicAlbums, "2", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, "3", parent).ConfigureAwait(false));
-            //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicArtists, "4", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicSongs, "5", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicGenres, "6", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicFavorites, "7", parent).ConfigureAwait(false));
@@ -422,6 +394,36 @@ namespace MediaBrowser.Controller.Entities
             return PostFilterAndSort(items, parent, null, query);
         }
 
+        private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
+        {
+            query.IsFavorite = true;
+
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, null, query);
+        }
+
+        private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
+        {
+            query.IsFavorite = true;
+
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, null, query);
+        }
+
+        private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
+        {
+            var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
+
+            return GetResult(list, parent, query);
+        }
+
+        private int GetSpecialItemsLimit()
+        {
+            return 50;
+        }
+
         private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
         {
             if (query.Recursive)
@@ -480,24 +482,6 @@ namespace MediaBrowser.Controller.Entities
             return PostFilterAndSort(items, parent, null, query);
         }
 
-        private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
-        private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
         private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
         {
             var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query));
@@ -617,54 +601,6 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(list, parent, query);
         }
 
-        private async Task<QueryResult<BaseItem>> GetGameView(User user, Folder parent, InternalItemsQuery query)
-        {
-            if (query.Recursive)
-            {
-                var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => FilterItem(i, query));
-                return PostFilterAndSort(items, parent, null, query);
-            }
-
-            var list = new List<BaseItem>();
-
-            list.Add(await GetUserView(SpecialFolder.LatestGames, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameFavorites, "2", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameSystems, "3", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameGenres, "4", parent).ConfigureAwait(false));
-
-            return GetResult(list, parent, query);
-        }
-
-        private QueryResult<BaseItem> GetLatestGames(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
-
-            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)
-        {
-            query.IsPlayed = true;
-            query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
-
-            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)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
         private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
         {
             query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
@@ -745,49 +681,6 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(items, queryParent, query);
         }
 
-        private QueryResult<BaseItem> GetGameSystems(Folder parent, User user, InternalItemsQuery query)
-        {
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is GameSystem && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
-        private async Task<QueryResult<BaseItem>> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
-        {
-            var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games },
-                i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
-
-            return GetResult(items, queryParent, query);
-        }
-
-        private async Task<QueryResult<BaseItem>> GetGameGenres(Folder parent, User user, InternalItemsQuery query)
-        {
-            var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Games })
-                .OfType<Game>()
-                .SelectMany(i => i.Genres)
-                .DistinctNames()
-                .Select(i =>
-                {
-                    try
-                    {
-                        return _libraryManager.GetGameGenre(i);
-                    }
-                    catch
-                    {
-                        // Full exception logged at lower levels
-                        _logger.Error("Error getting game genre");
-                        return null;
-                    }
-
-                })
-                .Where(i => i != null)
-                .Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, i.SortName, parent));
-
-            var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
-
-            return GetResult(genres, parent, query);
-        }
-
         private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result)
             where T : BaseItem
         {
@@ -1061,6 +954,11 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            if (request.GenreIds.Length > 0)
+            {
+                return false;
+            }
+
             if (request.VideoTypes.Length > 0)
             {
                 return false;
@@ -1101,10 +999,15 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            if (request.MinIndexNumber.HasValue)
+            {
+                return false;
+            }
+
             return true;
         }
 
-        public static IEnumerable<BaseItem> FilterVirtualEpisodes(
+        private static IEnumerable<BaseItem> FilterVirtualEpisodes(
             IEnumerable<BaseItem> items,
             bool? isMissing,
             bool? isVirtualUnaired,
@@ -1374,7 +1277,7 @@ namespace MediaBrowser.Controller.Entities
             if (query.IsInBoxSet.HasValue)
             {
                 var val = query.IsInBoxSet.Value;
-                if (item.Parents.OfType<BoxSet>().Any() != val)
+                if (item.GetParents().OfType<BoxSet>().Any() != val)
                 {
                     return false;
                 }
@@ -1657,6 +1560,16 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            // Apply genre filter
+            if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
+            {
+                var genreItem = libraryManager.GetItemById(id);
+                return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
+            }))
+            {
+                return false;
+            }
+
             // Apply year filter
             if (query.Years.Length > 0)
             {
@@ -1779,6 +1692,16 @@ namespace MediaBrowser.Controller.Entities
                 }
             }
 
+            if (query.MinIndexNumber.HasValue)
+            {
+                var val = query.MinIndexNumber.Value;
+
+                if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
+                {
+                    return false;
+                }
+            }
+
             return true;
         }
 
@@ -1789,12 +1712,12 @@ namespace MediaBrowser.Controller.Entities
                 return _libraryManager.RootFolder
                     .Children
                     .OfType<Folder>()
-                    .Where(i => !UserView.IsExcludedFromGrouping(i));
+                    .Where(UserView.IsEligibleForGrouping);
             }
             return user.RootFolder
-                .GetChildren(user, true, true)
+                .GetChildren(user, true)
                 .OfType<Folder>()
-                .Where(i => user.IsFolderGrouped(i.Id) && !UserView.IsExcludedFromGrouping(i));
+                .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
         }
 
         private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)

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

@@ -185,12 +185,6 @@ namespace MediaBrowser.Controller.Entities
         public bool IsShortcut { get; set; }
         public string ShortcutPath { get; set; }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         /// <summary>
         /// Gets or sets the video bit rate.
         /// </summary>
@@ -356,7 +350,7 @@ namespace MediaBrowser.Controller.Entities
             // Must have a parent to have additional parts or alternate versions
             // In other words, it must be part of the Parent/Child tree
             // The additional parts won't have additional parts themselves
-            if (LocationType == LocationType.FileSystem && Parent != null)
+            if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
                 if (!IsStacked)
                 {

+ 3 - 1
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -1,6 +1,8 @@
 using MediaBrowser.Common;
 using MediaBrowser.Model.System;
 using System;
+using System.Collections.Generic;
+using System.Net;
 
 namespace MediaBrowser.Controller
 {
@@ -63,7 +65,7 @@ namespace MediaBrowser.Controller
         /// Gets the local ip address.
         /// </summary>
         /// <value>The local ip address.</value>
-        string LocalIpAddress { get; }
+        List<IPAddress> LocalIpAddresses { get; }
 
         /// <summary>
         /// Gets the local API URL.

+ 24 - 3
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -337,7 +337,6 @@ namespace MediaBrowser.Controller.Library
             string parentId,
             string viewType, 
             string sortName, 
-            string uniqueId,
             CancellationToken cancellationToken);
 
         /// <summary>
@@ -391,13 +390,11 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
-        /// <param name="uniqueId">The unique identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;UserView&gt;.</returns>
         Task<UserView> GetShadowView(BaseItem parent,
           string viewType,
           string sortName,
-          string uniqueId,
           CancellationToken cancellationToken);
         
         /// <summary>
@@ -543,5 +540,29 @@ namespace MediaBrowser.Controller.Library
         /// <param name="imageIndex">Index of the image.</param>
         /// <returns>Task.</returns>
         Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
+
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="parentIds">The parent ids.</param>
+        /// <returns>List&lt;BaseItem&gt;.</returns>
+        IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds);
+
+        /// <summary>
+        /// Gets the items result.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="parentIds">The parent ids.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds);
+
+        /// <summary>
+        /// Ignores the file.
+        /// </summary>
+        /// <param name="file">The file.</param>
+        /// <param name="parent">The parent.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+        bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
     }
 }

+ 1 - 1
MediaBrowser.Controller/Library/ItemResolveArgs.cs

@@ -155,7 +155,7 @@ namespace MediaBrowser.Controller.Library
                 // Not officially supported but in some cases we can handle it.
                 if (item == null)
                 {
-                    item = parent.Parents.OfType<T>().FirstOrDefault();
+                    item = parent.GetParents().OfType<T>().FirstOrDefault();
                 }
 
                 return item != null;

+ 7 - 3
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsLive { get; set; }
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
-        public ProgramAudio? Audio { get; set; }
 
         /// <summary>
         /// Gets the user data key.
@@ -106,9 +105,9 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -140,5 +139,10 @@ namespace MediaBrowser.Controller.LiveTv
 
             return list;
         }
+
+        public override bool IsVisibleStandalone(User user)
+        {
+            return IsVisible(user);
+        }
     }
 }

+ 2 - 2
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -22,9 +22,9 @@ namespace MediaBrowser.Controller.LiveTv
             return GetClientTypeName() + "-" + Name;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel);
+            return UnratedItem.LiveTvChannel;
         }
 
         /// <summary>

+ 23 - 25
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -40,10 +40,11 @@ namespace MediaBrowser.Controller.LiveTv
         }
 
         /// <summary>
-        /// Gets or sets the type of the channel.
+        /// Gets or sets the name.
         /// </summary>
-        /// <value>The type of the channel.</value>
-        public ChannelType ChannelType { get; set; }
+        /// <value>The name.</value>
+        [IgnoreDataMember]
+        public string ServiceName { get; set; }
 
         /// <summary>
         /// The start date of the program, in UTC.
@@ -51,12 +52,6 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
 
-        /// <summary>
-        /// Gets or sets the audio.
-        /// </summary>
-        /// <value>The audio.</value>
-        public ProgramAudio? Audio { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance is repeat.
         /// </summary>
@@ -71,12 +66,6 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public string EpisodeTitle { get; set; }
 
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance is movie.
         /// </summary>
@@ -153,14 +142,14 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        [IgnoreDataMember]
-        public override string MediaType
-        {
-            get
-            {
-                return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
-            }
-        }
+        //[IgnoreDataMember]
+        //public override string MediaType
+        //{
+        //    get
+        //    {
+        //        return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
+        //    }
+        //}
 
         [IgnoreDataMember]
         public bool IsAiring
@@ -189,9 +178,9 @@ namespace MediaBrowser.Controller.LiveTv
             return "Program";
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -236,5 +225,14 @@ namespace MediaBrowser.Controller.LiveTv
                 return base.SupportsPeople;
             }
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsAncestors
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 7 - 3
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsLive { get; set; }
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
-        public ProgramAudio? Audio { get; set; }
 
         /// <summary>
         /// Gets the user data key.
@@ -121,9 +120,9 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -155,5 +154,10 @@ namespace MediaBrowser.Controller.LiveTv
 
             return list;
         }
+
+        public override bool IsVisibleStandalone(User user)
+        {
+            return IsVisible(user);
+        }
     }
 }

+ 6 - 0
MediaBrowser.Controller/LiveTv/RecordingGroup.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Controller.LiveTv
@@ -11,6 +12,11 @@ namespace MediaBrowser.Controller.LiveTv
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.LiveTvProgram;
+        }
+
         public override bool SupportsLocalMetadata
         {
             get

+ 4 - 3
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -51,6 +51,9 @@
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
     </Reference>
@@ -65,9 +68,6 @@
     <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq">
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
@@ -162,6 +162,7 @@
     <Compile Include="Entities\IHasThemeMedia.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IHasUserData.cs" />
+    <Compile Include="Entities\IHiddenFromDisplay.cs" />
     <Compile Include="Entities\IItemByName.cs" />
     <Compile Include="Entities\ILibraryItem.cs" />
     <Compile Include="Entities\ImageSourceInfo.cs" />

+ 2 - 1
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -35,9 +35,10 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Extracts the audio image.
         /// </summary>
         /// <param name="path">The path.</param>
+        /// <param name="imageStreamIndex">Index of the image stream.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<Stream> ExtractAudioImage(string path, CancellationToken cancellationToken);
+        Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken);
 
         /// <summary>
         /// Extracts the video image.

+ 0 - 1
MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs

@@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         public IIsoMount MountedIso { get; set; }
         public VideoType VideoType { get; set; }
         public List<string> PlayableStreamFileNames { get; set; }
-        public bool ExtractKeyFrameInterval { get; set; }
 
         public MediaInfoRequest()
         {

+ 0 - 6
MediaBrowser.Controller/Net/IHttpServer.cs

@@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Net
         /// the ssl certificate localtion on the file system.</param>
         void StartServer(IEnumerable<string> urlPrefixes, string certificatePath);
 
-        /// <summary>
-        /// Gets the local end points.
-        /// </summary>
-        /// <value>The local end points.</value>
-        IEnumerable<string> LocalEndPoints { get; }
-
         /// <summary>
         /// Stops this instance.
         /// </summary>

+ 14 - 0
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -176,6 +176,20 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
         QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
+
+        /// <summary>
+        /// Gets the item list.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>List&lt;BaseItem&gt;.</returns>
+        IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
+
+        /// <summary>
+        /// Updates the inherited values.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task UpdateInheritedValues(CancellationToken cancellationToken);
     }
 }
 

+ 1 - 0
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -144,6 +144,7 @@ namespace MediaBrowser.Controller.Playlists
 
         public string PlaylistMediaType { get; set; }
 
+        [IgnoreDataMember]
         public override string MediaType
         {
             get

+ 0 - 32
MediaBrowser.Controller/Providers/MetadataStatus.cs

@@ -10,24 +10,6 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The item identifier.</value>
         public Guid ItemId { get; set; }
 
-        /// <summary>
-        /// Gets or sets the name of the item.
-        /// </summary>
-        /// <value>The name of the item.</value>
-        public string ItemName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the item.
-        /// </summary>
-        /// <value>The type of the item.</value>
-        public string ItemType { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the name of the series.
-        /// </summary>
-        /// <value>The name of the series.</value>
-        public string SeriesName { get; set; }
-
         /// <summary>
         /// Gets or sets the date last metadata refresh.
         /// </summary>
@@ -40,22 +22,8 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The date last images refresh.</value>
         public DateTime? DateLastImagesRefresh { get; set; }
 
-        /// <summary>
-        /// Gets or sets the last result error message.
-        /// </summary>
-        /// <value>The last result error message.</value>
-        public string LastErrorMessage { get; set; }
-
         public DateTime? ItemDateModified { get; set; }
 
-        public void AddStatus(string errorMessage)
-        {
-            if (string.IsNullOrEmpty(LastErrorMessage))
-            {
-                LastErrorMessage = errorMessage;
-            }
-        }
-
         public bool IsDirty { get; private set; }
 
         public void SetDateLastMetadataRefresh(DateTime? date)

+ 3 - 2
MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using CommonIO;
+using MediaBrowser.Controller.Entities;
 
 namespace MediaBrowser.Controller.Resolvers
 {
@@ -7,6 +8,6 @@ namespace MediaBrowser.Controller.Resolvers
     /// </summary>
     public interface IResolverIgnoreRule
     {
-        bool ShouldIgnore(ItemResolveArgs args);
+        bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent);
     }
 }

+ 1 - 1
MediaBrowser.Controller/packages.config

@@ -2,6 +2,6 @@
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 5 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -26,6 +26,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly ILocalizationManager _localization;
         private readonly IChannelManager _channelManager;
         private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IUserViewManager _userViewManager;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -34,7 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
+            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -46,6 +47,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _localization = localization;
             _channelManager = channelManager;
             _mediaSourceManager = mediaSourceManager;
+            _userViewManager = userViewManager;
         }
 
         private int SystemUpdateId
@@ -86,7 +88,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 _config,
                 _localization,
                 _channelManager,
-                _mediaSourceManager)
+                _mediaSourceManager,
+                _userViewManager)
                 .ProcessControlRequest(request);
         }
 

+ 37 - 24
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -24,6 +24,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
+using MediaBrowser.Model.Library;
 
 namespace MediaBrowser.Dlna.ContentDirectory
 {
@@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IUserDataManager _userDataManager;
         private readonly IServerConfigurationManager _config;
         private readonly User _user;
+        private readonly IUserViewManager _userViewManager;
 
         private const string NS_DC = "http://purl.org/dc/elements/1.1/";
         private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@@ -47,7 +49,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private readonly DeviceProfile _profile;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
@@ -55,6 +57,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _user = user;
             _systemUpdateId = systemUpdateId;
             _channelManager = channelManager;
+            _userViewManager = userViewManager;
             _profile = profile;
             _config = config;
 
@@ -450,16 +453,32 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 sortOrders.Add(ItemSortBy.SortName);
             }
 
-            var queryResult = await folder.GetItems(new InternalItemsQuery
+            QueryResult<BaseItem> queryResult;
+
+            if (folder is UserRootFolder)
             {
-                Limit = limit,
-                StartIndex = startIndex,
-                SortBy = sortOrders.ToArray(),
-                SortOrder = sort.SortOrder,
-                User = user,
-                Filter = FilterUnsupportedContent
+                var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None)
+                            .ConfigureAwait(false);
+
+                queryResult = new QueryResult<BaseItem>
+                {
+                    Items = views.Cast<BaseItem>().ToArray()
+                };
+                queryResult.TotalRecordCount = queryResult.Items.Length;
+            }
+            else
+            {
+                queryResult = await folder.GetItems(new InternalItemsQuery
+               {
+                   Limit = limit,
+                   StartIndex = startIndex,
+                   SortBy = sortOrders.ToArray(),
+                   SortOrder = sort.SortOrder,
+                   User = user,
+                   Filter = FilterUnsupportedContent
 
-            }).ConfigureAwait(false);
+               }).ConfigureAwait(false);
+            }
 
             var options = _config.GetDlnaConfiguration();
 
@@ -481,23 +500,17 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
         {
-            var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
-                Person = person.Name
-
-            }).Items;
-
-            var items = itemsWithPerson
-                .Where(i => i is Movie || i is Series || i is IChannelItem)
-                .Where(i => i.IsVisibleStandalone(user))
-                .ToList();
+                Person = person.Name,
+                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name },
+                SortBy = new[] { ItemSortBy.SortName },
+                Limit = limit,
+                StartIndex = startIndex
 
-            items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
-                .Skip(startIndex ?? 0)
-                .Take(limit ?? int.MaxValue)
-                .ToList();
+            }, new string[] { });
 
-            var serverItems = items.Select(i => new ServerItem
+            var serverItems = itemsResult.Items.Select(i => new ServerItem
             {
                 Item = i,
                 StubType = null
@@ -506,7 +519,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             return new QueryResult<ServerItem>
             {
-                TotalRecordCount = serverItems.Length,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = serverItems
             };
         }

+ 1 - 1
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -966,7 +966,7 @@ namespace MediaBrowser.Dlna.Didl
                 }
             }
 
-            item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
+            item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
 
             if (item != null)
             {

+ 25 - 18
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -43,19 +43,19 @@ namespace MediaBrowser.Dlna.Main
         private readonly List<string> _registeredServerIds = new List<string>();
         private bool _dlnaServerStarted;
 
-        public DlnaEntryPoint(IServerConfigurationManager config, 
-            ILogManager logManager, 
-            IServerApplicationHost appHost, 
-            INetworkManager network, 
-            ISessionManager sessionManager, 
-            IHttpClient httpClient, 
-            ILibraryManager libraryManager, 
-            IUserManager userManager, 
-            IDlnaManager dlnaManager, 
-            IImageProcessor imageProcessor, 
-            IUserDataManager userDataManager, 
-            ILocalizationManager localization, 
-            IMediaSourceManager mediaSourceManager, 
+        public DlnaEntryPoint(IServerConfigurationManager config,
+            ILogManager logManager,
+            IServerApplicationHost appHost,
+            INetworkManager network,
+            ISessionManager sessionManager,
+            IHttpClient httpClient,
+            ILibraryManager libraryManager,
+            IUserManager userManager,
+            IDlnaManager dlnaManager,
+            IImageProcessor imageProcessor,
+            IUserDataManager userDataManager,
+            ILocalizationManager localization,
+            IMediaSourceManager mediaSourceManager,
             ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery)
         {
             _config = config;
@@ -148,13 +148,20 @@ namespace MediaBrowser.Dlna.Main
 
         private void RegisterServerEndpoints()
         {
-            foreach (var address in _network.GetLocalIpAddresses())
+            foreach (var address in _appHost.LocalIpAddresses)
             {
-                var guid = address.GetMD5();
+                //if (IPAddress.IsLoopback(address))
+                //{
+                //    // Should we allow this?
+                //    continue;
+                //}
+
+                var addressString = address.ToString();
+                var guid = addressString.GetMD5();
 
                 var descriptorURI = "/dlna/" + guid.ToString("N") + "/description.xml";
 
-                var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI);
+                var uri = new Uri(_appHost.GetLocalApiUrl(addressString) + descriptorURI);
 
                 var services = new List<string>
                 {
@@ -165,8 +172,8 @@ namespace MediaBrowser.Dlna.Main
                     "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
                     "uuid:" + guid.ToString("N")
                 };
-                
-                _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services);
+
+                _ssdpHandler.RegisterNotification(guid, uri, address, services);
 
                 _registeredServerIds.Add(guid.ToString("N"));
             }

+ 3 - 0
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -44,6 +44,9 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
     </Reference>

+ 1 - 1
MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs

@@ -92,7 +92,7 @@ namespace MediaBrowser.Dlna.Profiles
                 {
                     Container = "ts",
                     VideoCodec = "h264",
-                    AudioCodec = "mp3",
+                    AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new TranscodingProfile

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -48,7 +48,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

+ 29 - 47
MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs

@@ -11,6 +11,8 @@ using System.Net.NetworkInformation;
 using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MoreLinq;
 
 namespace MediaBrowser.Dlna.Ssdp
 {
@@ -26,50 +28,39 @@ namespace MediaBrowser.Dlna.Ssdp
 
         public event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
         public event EventHandler<SsdpMessageEventArgs> DeviceLeft;
+        private readonly INetworkManager _networkManager;
 
-        public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost)
+        public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost, INetworkManager networkManager)
         {
             _tokenSource = new CancellationTokenSource();
 
             _logger = logger;
             _config = config;
             _appHost = appHost;
+            _networkManager = networkManager;
         }
 
+		private List<IPAddress> GetLocalIpAddresses()
+		{
+		    return _networkManager.GetLocalIpAddresses().ToList();
+		}
+
         public void Start(SsdpHandler ssdpHandler)
         {
             _ssdpHandler = ssdpHandler;
             _ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived;
 
-            foreach (var network in GetNetworkInterfaces())
-            {
-                _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
-
-                if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
-                    continue;
-
-                var properties = network.GetIPProperties();
-                var ipV4 = properties.GetIPv4Properties();
-                if (null == ipV4)
-                    continue;
-
-                var localIps = properties.UnicastAddresses
-                    .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork)
-                    .Select(i => i.Address)
-                    .ToList();
-
-                foreach (var localIp in localIps)
-                {
-                    try
-                    {
-                        CreateListener(localIp);
-                    }
-                    catch (Exception e)
-                    {
-                        _logger.ErrorException("Failed to Initilize Socket", e);
-                    }
-                }
-            }
+            foreach (var localIp in GetLocalIpAddresses())
+			{
+				try
+				{
+					CreateListener(localIp);
+				}
+				catch (Exception e)
+				{
+					_logger.ErrorException("Failed to Initilize Socket", e);
+				}
+			}
         }
 
         void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e)
@@ -89,8 +80,11 @@ namespace MediaBrowser.Dlna.Ssdp
             {
                 if (e.LocalEndPoint == null)
                 {
-                    var ip = _appHost.LocalIpAddress;
-                    e.LocalEndPoint = new IPEndPoint(IPAddress.Parse(ip), 0);
+                    var ip = _appHost.LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
+                    if (ip != null)
+                    {
+                        e.LocalEndPoint = new IPEndPoint(ip, 0);
+                    }
                 }
 
                 if (e.LocalEndPoint != null)
@@ -107,29 +101,17 @@ namespace MediaBrowser.Dlna.Ssdp
             }
         }
 
-        private IEnumerable<NetworkInterface> GetNetworkInterfaces()
-        {
-            try
-            {
-                return NetworkInterface.GetAllNetworkInterfaces();
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
-                return new List<NetworkInterface>();
-            }
-        }
         private void CreateListener(IPAddress localIp)
         {
             Task.Factory.StartNew(async (o) =>
             {
                 try
                 {
-                    var endPoint = new IPEndPoint(localIp, 1900);
+					_logger.Info("Creating SSDP listener on {0}", localIp);
 
-                    var socket = GetMulticastSocket(localIp, endPoint);
+					var endPoint = new IPEndPoint(localIp, 1900);
 
-                    _logger.Info("Creating SSDP listener on {0}", localIp);
+                    var socket = GetMulticastSocket(localIp, endPoint);
 
                     var receiveBuffer = new byte[64000];
 

+ 14 - 1
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -15,6 +15,7 @@ using System.Net.Sockets;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.Win32;
 
 namespace MediaBrowser.Dlna.Ssdp
 {
@@ -112,7 +113,9 @@ namespace MediaBrowser.Dlna.Ssdp
         {
             get
             {
-                return _devices.Values.SelectMany(i => i).ToList();
+                var devices = _devices.Values.ToList();
+
+                return devices.SelectMany(i => i).ToList();
             }
         }
 
@@ -121,6 +124,15 @@ namespace MediaBrowser.Dlna.Ssdp
             RestartSocketListener();
 
             ReloadAliveNotifier();
+            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+        }
+
+        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            if (e.Mode == PowerModes.Resume)
+            {
+                NotifyAll();
+            }
         }
 
         public void SendSearchMessage(EndPoint localIp)
@@ -433,6 +445,7 @@ namespace MediaBrowser.Dlna.Ssdp
         public void Dispose()
         {
             _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
+            SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
 
             _isDisposed = true;
             while (_messageQueue.Count != 0)

+ 1 - 0
MediaBrowser.Dlna/packages.config

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 0 - 3
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -79,14 +79,11 @@
     <Compile Include="Providers\SeriesXmlProvider.cs" />
     <Compile Include="Providers\VideoXmlProvider.cs" />
     <Compile Include="Savers\BoxSetXmlSaver.cs" />
-    <Compile Include="Savers\EpisodeXmlSaver.cs" />
     <Compile Include="Savers\FolderXmlSaver.cs" />
     <Compile Include="Savers\GameSystemXmlSaver.cs" />
     <Compile Include="Savers\GameXmlSaver.cs" />
-    <Compile Include="Savers\MovieXmlSaver.cs" />
     <Compile Include="Savers\PersonXmlSaver.cs" />
     <Compile Include="Savers\PlaylistXmlSaver.cs" />
-    <Compile Include="Savers\SeriesXmlSaver.cs" />
     <Compile Include="Savers\XmlSaverHelpers.cs" />
   </ItemGroup>
   <ItemGroup>

+ 0 - 166
MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs

@@ -1,166 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    public class EpisodeXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IItemRepository _itemRepository;
-
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public EpisodeXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _itemRepository = itemRepository;
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            return item is Episode && updateType >= ItemUpdateType.MetadataDownload;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var episode = (Episode)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Item>");
-
-            if (!string.IsNullOrEmpty(item.Name))
-            {
-                builder.Append("<EpisodeName>" + SecurityElement.Escape(episode.Name) + "</EpisodeName>");
-            }
-
-            if (episode.IndexNumber.HasValue)
-            {
-                builder.Append("<EpisodeNumber>" + SecurityElement.Escape(episode.IndexNumber.Value.ToString(_usCulture)) + "</EpisodeNumber>");
-            }
-
-            if (episode.IndexNumberEnd.HasValue)
-            {
-                builder.Append("<EpisodeNumberEnd>" + SecurityElement.Escape(episode.IndexNumberEnd.Value.ToString(_usCulture)) + "</EpisodeNumberEnd>");
-            }
-
-            if (episode.AirsAfterSeasonNumber.HasValue)
-            {
-                builder.Append("<airsafter_season>" + SecurityElement.Escape(episode.AirsAfterSeasonNumber.Value.ToString(_usCulture)) + "</airsafter_season>");
-            }
-            if (episode.AirsBeforeEpisodeNumber.HasValue)
-            {
-                builder.Append("<airsbefore_episode>" + SecurityElement.Escape(episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture)) + "</airsbefore_episode>");
-            }
-            if (episode.AirsBeforeSeasonNumber.HasValue)
-            {
-                builder.Append("<airsbefore_season>" + SecurityElement.Escape(episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture)) + "</airsbefore_season>");
-            }
-   
-            if (episode.ParentIndexNumber.HasValue)
-            {
-                builder.Append("<SeasonNumber>" + SecurityElement.Escape(episode.ParentIndexNumber.Value.ToString(_usCulture)) + "</SeasonNumber>");
-            }
-
-            if (episode.AbsoluteEpisodeNumber.HasValue)
-            {
-                builder.Append("<absolute_number>" + SecurityElement.Escape(episode.AbsoluteEpisodeNumber.Value.ToString(_usCulture)) + "</absolute_number>");
-            }
-            
-            if (episode.DvdEpisodeNumber.HasValue)
-            {
-                builder.Append("<DVD_episodenumber>" + SecurityElement.Escape(episode.DvdEpisodeNumber.Value.ToString(_usCulture)) + "</DVD_episodenumber>");
-            }
-
-            if (episode.DvdSeasonNumber.HasValue)
-            {
-                builder.Append("<DVD_season>" + SecurityElement.Escape(episode.DvdSeasonNumber.Value.ToString(_usCulture)) + "</DVD_season>");
-            } 
-            
-            if (episode.PremiereDate.HasValue)
-            {
-                builder.Append("<FirstAired>" + SecurityElement.Escape(episode.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>");
-            }
-
-            XmlSaverHelpers.AddCommonNodes(episode, _libraryManager, builder);
-            XmlSaverHelpers.AddMediaInfo(episode, builder, _itemRepository);
-
-            builder.Append("</Item>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    "FirstAired",
-                    "SeasonNumber",
-                    "EpisodeNumber",
-                    "EpisodeName",
-                    "EpisodeNumberEnd",
-                    "airsafter_season",
-                    "airsbefore_episode",
-                    "airsbefore_season",
-                    "DVD_episodenumber",
-                    "DVD_season",
-                    "absolute_number"
-
-                }, _config, _fileSystem);
-        }
-
-        /// <summary>
-        /// Gets the save path.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public string GetSavePath(IHasMetadata item)
-        {
-            var filename = Path.ChangeExtension(Path.GetFileName(item.Path), ".xml");
-
-            return Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename);
-        }
-    }
-}

+ 0 - 147
MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs

@@ -1,147 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    /// <summary>
-    /// Saves movie.xml for movies, trailers and music videos
-    /// </summary>
-    public class MovieXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IItemRepository _itemRepository;
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public MovieXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _itemRepository = itemRepository;
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            var video = item as Video;
-
-            // Check parent for null to avoid running this against things like video backdrops
-            if (video != null && !(item is Episode) && !video.IsOwnedItem)
-            {
-                return updateType >= ItemUpdateType.MetadataDownload;
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var video = (Video)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Title>");
-
-            XmlSaverHelpers.AddCommonNodes(video, _libraryManager, builder);
-
-            var musicVideo = item as MusicVideo;
-
-            if (musicVideo != null)
-            {
-                if (musicVideo.Artists.Count > 0)
-                {
-                    builder.Append("<Artist>" + SecurityElement.Escape(string.Join(";", musicVideo.Artists.ToArray())) + "</Artist>");
-                }
-                if (!string.IsNullOrEmpty(musicVideo.Album))
-                {
-                    builder.Append("<Album>" + SecurityElement.Escape(musicVideo.Album) + "</Album>");
-                }
-            }
-
-            var movie = item as Movie;
-
-            if (movie != null)
-            {
-                if (!string.IsNullOrEmpty(movie.TmdbCollectionName))
-                {
-                    builder.Append("<TmdbCollectionName>" + SecurityElement.Escape(movie.TmdbCollectionName) + "</TmdbCollectionName>");
-                }
-            }
-            
-            XmlSaverHelpers.AddMediaInfo(video, builder, _itemRepository);
-
-            builder.Append("</Title>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    // Deprecated. No longer saving in this field.
-                    "IMDBrating",
-                    
-                    // Deprecated. No longer saving in this field.
-                    "Description",
-
-                    "Artist",
-                    "Album",
-                    "TmdbCollectionName"
-                }, _config, _fileSystem);
-        }
-
-        public string GetSavePath(IHasMetadata item)
-        {
-            return GetMovieSavePath((Video)item);
-        }
-
-        public static string GetMovieSavePath(Video item)
-        {
-            if (item.IsInMixedFolder)
-            {
-                return Path.ChangeExtension(item.Path, ".xml");
-            }
-
-            return Path.Combine(item.ContainingFolderPath, "movie.xml");
-        }
-    }
-}

+ 0 - 154
MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs

@@ -1,154 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    public class SeriesXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public SeriesXmlProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            return item is Series && updateType >= ItemUpdateType.MetadataDownload;
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var series = (Series)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Series>");
-
-            var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
-
-            if (!string.IsNullOrEmpty(tvdb))
-            {
-                builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>");
-            }
-
-            if (series.Status.HasValue)
-            {
-                builder.Append("<Status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</Status>");
-            }
-
-            if (series.Studios.Count > 0)
-            {
-                builder.Append("<Network>" + SecurityElement.Escape(series.Studios[0]) + "</Network>");
-            }
-
-            if (!string.IsNullOrEmpty(series.AirTime))
-            {
-                builder.Append("<Airs_Time>" + SecurityElement.Escape(series.AirTime) + "</Airs_Time>");
-            }
-
-            if (series.AirDays != null)
-            {
-                if (series.AirDays.Count == 7)
-                {
-                    builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>");
-                }
-                else if (series.AirDays.Count > 0)
-                {
-                    builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>");
-                }
-            }
-
-            if (series.PremiereDate.HasValue)
-            {
-                builder.Append("<FirstAired>" + SecurityElement.Escape(series.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>");
-            }
-
-            if (series.AnimeSeriesIndex.HasValue)
-            {
-                builder.Append("<AnimeSeriesIndex>" + SecurityElement.Escape(series.AnimeSeriesIndex.Value.ToString(UsCulture)) + "</AnimeSeriesIndex>");
-            }
-
-            XmlSaverHelpers.AddCommonNodes(series, _libraryManager, builder);
-
-            builder.Append("</Series>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    "id", 
-                    "Status",
-                    "Network",
-                    "Airs_Time",
-                    "Airs_DayOfWeek",
-                    "FirstAired",
-
-                    // Don't preserve old series node
-                    "Series",
-
-                    "SeriesName",
-
-                    // Deprecated. No longer saving in this field.
-                    "AnimeSeriesIndex"
-                }, _config, _fileSystem);
-        }
-
-        /// <summary>
-        /// Gets the save path.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public string GetSavePath(IHasMetadata item)
-        {
-            return Path.Combine(item.Path, "series.xml");
-        }
-    }
-}

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs

@@ -505,7 +505,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 return "libx264";
             }
-            if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
             {
                 return "libx265";
             }

+ 28 - 25
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -258,17 +258,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                     var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
 
-                    //var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+                    var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
 
-                    //if (videoStream != null)
-                    //{
-                    //    var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false);
+                    if (videoStream != null)
+                    {
+                        var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false);
 
-                    //    if (isInterlaced)
-                    //    {
-                    //        videoStream.IsInterlaced = true;
-                    //    }
-                    //}
+                        if (isInterlaced)
+                        {
+                            videoStream.IsInterlaced = true;
+                        }
+                    }
 
                     return mediaInfo;
                 }
@@ -292,16 +292,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 return false;
             }
 
-            var formats = (video.Container ?? string.Empty).Split(',').ToList();
-
-            // Take a shortcut and limit this to containers that are likely to have interlaced content
-            if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) &&
-                !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) &&
-                !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) &&
-                !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) &&
-                !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase))
+            // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content
+            if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) == -1)
             {
-                return false;
+                var formats = (video.Container ?? string.Empty).Split(',').ToList();
+
+                if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) &&
+                    !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) &&
+                    !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) &&
+                    !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) &&
+                    !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
             }
 
             var args = "{0} -i {1} -map 0:v:{2} -filter:v idet -frames:v 500 -an -f null /dev/null";
@@ -469,18 +472,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// </summary>
         protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
-        public Task<Stream> ExtractAudioImage(string path, CancellationToken cancellationToken)
+        public Task<Stream> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
         {
-            return ExtractImage(new[] { path }, MediaProtocol.File, true, null, null, cancellationToken);
+            return ExtractImage(new[] { path }, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
         }
 
         public Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat,
             TimeSpan? offset, CancellationToken cancellationToken)
         {
-            return ExtractImage(inputFiles, protocol, false, threedFormat, offset, cancellationToken);
+            return ExtractImage(inputFiles, null, protocol, false, threedFormat, offset, cancellationToken);
         }
 
-        private async Task<Stream> ExtractImage(string[] inputFiles, MediaProtocol protocol, bool isAudio,
+        private async Task<Stream> ExtractImage(string[] inputFiles, int? imageStreamIndex, MediaProtocol protocol, bool isAudio,
             Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
         {
             var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
@@ -491,7 +494,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 try
                 {
-                    return await ExtractImageInternal(inputArgument, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false);
+                    return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, true, resourcePool, cancellationToken).ConfigureAwait(false);
                 }
                 catch (ArgumentException)
                 {
@@ -503,10 +506,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
-            return await ExtractImageInternal(inputArgument, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false);
+            return await ExtractImageInternal(inputArgument, imageStreamIndex, protocol, threedFormat, offset, false, resourcePool, cancellationToken).ConfigureAwait(false);
         }
 
-        private async Task<Stream> ExtractImageInternal(string inputPath, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
+        private async Task<Stream> ExtractImageInternal(string inputPath, int? imageStreamIndex, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(inputPath))
             {

+ 2 - 1
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -141,6 +141,7 @@ namespace MediaBrowser.MediaEncoding.Probing
             if (streamInfo.tags != null)
             {
                 stream.Language = GetDictionaryValue(streamInfo.tags, "language");
+                stream.Comment = GetDictionaryValue(streamInfo.tags, "comment");
             }
 
             if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase))
@@ -864,7 +865,7 @@ namespace MediaBrowser.MediaEncoding.Probing
             }
         }
 
-        private void ExtractTimestamp(Model.MediaInfo.MediaInfo video)
+        private void ExtractTimestamp(MediaInfo video)
         {
             if (video.VideoType == VideoType.VideoFile)
             {

+ 20 - 3
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -122,10 +122,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
                         .ConfigureAwait(false);
 
-            using (var stream = subtitle.Item1)
+            var inputFormat = subtitle.Item2;
+
+            if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null)
             {
-                var inputFormat = subtitle.Item2;
+                return subtitle.Item1;
+            }
 
+            using (var stream = subtitle.Item1)
+            {
                 return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, cancellationToken).ConfigureAwait(false);
             }
         }
@@ -288,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             return null;
         }
 
-        private ISubtitleWriter GetWriter(string format)
+        private ISubtitleWriter TryGetWriter(string format)
         {
             if (string.IsNullOrEmpty(format))
             {
@@ -312,6 +317,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 return new TtmlWriter();
             }
 
+            return null;
+        }
+
+        private ISubtitleWriter GetWriter(string format)
+        {
+            var writer = TryGetWriter(format);
+
+            if (writer != null)
+            {
+                return writer;
+            }
+
             throw new ArgumentException("Unsupported format: " + format);
         }
 

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -827,6 +827,9 @@
     <Compile Include="..\MediaBrowser.Model\MediaInfo\VideoCodec.cs">
       <Link>MediaInfo\VideoCodec.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Net\EndPointInfo.cs">
+      <Link>Net\EndPointInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Net\HttpException.cs">
       <Link>Net\HttpException.cs</Link>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -783,6 +783,9 @@
     <Compile Include="..\MediaBrowser.Model\MediaInfo\VideoCodec.cs">
       <Link>MediaInfo\VideoCodec.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Net\EndPointInfo.cs">
+      <Link>Net\EndPointInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Net\HttpException.cs">
       <Link>Net\HttpException.cs</Link>
     </Compile>

+ 3 - 0
MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs

@@ -11,16 +11,19 @@ namespace MediaBrowser.Model.Configuration
         public bool EnableIntrosParentalControl { get; set; }
         public bool EnableIntrosFromSimilarMovies { get; set; }
         public string CustomIntroPath { get; set; }
+        public string MediaInfoIntroPath { get; set; }
         public bool EnableIntrosFromUpcomingDvdMovies { get; set; }
         public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }
 
         public int TrailerLimit { get; set; }
+        public string[] Tags { get; set; }
         
         public CinemaModeConfiguration()
         {
             EnableIntrosParentalControl = true;
             EnableIntrosFromSimilarMovies = true;
             TrailerLimit = 2;
+            Tags = new[] { "thx" };
         }
     }
 }

+ 9 - 22
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -62,6 +62,12 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value>
         public bool IsPortAuthorized { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [enable high quality image scaling].
+        /// </summary>
+        /// <value><c>true</c> if [enable high quality image scaling]; otherwise, <c>false</c>.</value>
+        public bool EnableHighQualityImageScaling { get; set; }
+
         /// <summary>
         /// Gets or sets the item by name path.
         /// </summary>
@@ -92,24 +98,6 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if [enable localized guids]; otherwise, <c>false</c>.</value>
         public bool EnableLocalizedGuids { get; set; }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether [disable startup scan].
-        /// </summary>
-        /// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value>
-        public bool DisableStartupScan { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable user views].
-        /// </summary>
-        /// <value><c>true</c> if [enable user views]; otherwise, <c>false</c>.</value>
-        public bool EnableUserViews { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable library metadata sub folder].
-        /// </summary>
-        /// <value><c>true</c> if [enable library metadata sub folder]; otherwise, <c>false</c>.</value>
-        public bool EnableLibraryMetadataSubFolder { get; set; }
-
         /// <summary>
         /// Gets or sets the preferred metadata language.
         /// </summary>
@@ -219,21 +207,20 @@ namespace MediaBrowser.Model.Configuration
 
         public int SharingExpirationDays { get; set; }
 
-        public bool DisableXmlSavers { get; set; }
         public bool EnableWindowsShortcuts { get; set; }
 
-        public bool EnableVideoFrameByFrameAnalysis { get; set; }
-
         public bool EnableDateLastRefresh { get; set; }
 
         public string[] Migrations { get; set; }
 
+        public int MigrationVersion { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
         public ServerConfiguration()
         {
-            Migrations = new string[] {};
+            Migrations = new string[] { };
 
             ImageSavingConvention = ImageSavingConvention.Compatible;
             PublicPort = 8096;

+ 1 - 0
MediaBrowser.Model/Connect/ConnectUser.cs

@@ -8,5 +8,6 @@ namespace MediaBrowser.Model.Connect
         public string Email { get; set; }
         public bool IsActive { get; set; }
         public string ImageUrl { get; set; }
+        public bool IsSupporter { get; set; }
     }
 }

+ 6 - 0
MediaBrowser.Model/Entities/MediaStream.cs

@@ -30,6 +30,12 @@ namespace MediaBrowser.Model.Entities
         /// <value>The language.</value>
         public string Language { get; set; }
 
+        /// <summary>
+        /// Gets or sets the comment.
+        /// </summary>
+        /// <value>The comment.</value>
+        public string Comment { get; set; }
+        
         /// <summary>
         /// Gets or sets a value indicating whether this instance is interlaced.
         /// </summary>

Some files were not shown because too many files changed in this diff