瀏覽代碼

Merge branch 'master' of https://github.com/MediaBrowser/Emby

Luke Pulverenti 9 年之前
父節點
當前提交
d9108f69f3
共有 100 個文件被更改,包括 1710 次插入1290 次删除
  1. 1 1
      Emby.Drawing/Emby.Drawing.csproj
  2. 1 1
      Emby.Drawing/packages.config
  3. 19 4
      MediaBrowser.Api/ApiEntryPoint.cs
  4. 13 5
      MediaBrowser.Api/BaseApiService.cs
  5. 3 3
      MediaBrowser.Api/GamesService.cs
  6. 1 0
      MediaBrowser.Api/Images/ImageService.cs
  7. 36 95
      MediaBrowser.Api/Library/LibraryService.cs
  8. 4 4
      MediaBrowser.Api/Library/LibraryStructureService.cs
  9. 36 6
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  10. 1 1
      MediaBrowser.Api/MediaBrowser.Api.csproj
  11. 23 40
      MediaBrowser.Api/Movies/MoviesService.cs
  12. 21 68
      MediaBrowser.Api/Movies/TrailersService.cs
  13. 9 37
      MediaBrowser.Api/Music/AlbumsService.cs
  14. 15 4
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  15. 24 16
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  16. 2 1
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  17. 6 2
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  18. 33 7
      MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
  19. 2 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  20. 38 243
      MediaBrowser.Api/Reports/ReportsService.cs
  21. 7 19
      MediaBrowser.Api/SimilarItemsHelper.cs
  22. 2 0
      MediaBrowser.Api/StartupWizardService.cs
  23. 5 2
      MediaBrowser.Api/Subtitles/SubtitleService.cs
  24. 6 6
      MediaBrowser.Api/TvShowsService.cs
  25. 50 310
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  26. 1 6
      MediaBrowser.Api/UserLibrary/PlaystateService.cs
  27. 1 1
      MediaBrowser.Api/packages.config
  28. 19 0
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  29. 1 1
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  30. 11 0
      MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
  31. 1 1
      MediaBrowser.Common.Implementations/packages.config
  32. 1 0
      MediaBrowser.Common/Net/HttpRequestOptions.cs
  33. 3 0
      MediaBrowser.Common/ScheduledTasks/ITaskManager.cs
  34. 15 0
      MediaBrowser.Controller/Channels/Channel.cs
  35. 1 1
      MediaBrowser.Controller/Channels/ChannelAudioItem.cs
  36. 1 1
      MediaBrowser.Controller/Channels/ChannelFolderItem.cs
  37. 2 16
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  38. 2 2
      MediaBrowser.Controller/Channels/IChannelManager.cs
  39. 1 0
      MediaBrowser.Controller/Dto/DtoOptions.cs
  40. 60 1
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  41. 12 0
      MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
  42. 21 2
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  43. 149 8
      MediaBrowser.Controller/Entities/BaseItem.cs
  44. 2 0
      MediaBrowser.Controller/Entities/Book.cs
  45. 470 106
      MediaBrowser.Controller/Entities/Folder.cs
  46. 26 1
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  47. 8 12
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  48. 10 0
      MediaBrowser.Controller/Entities/SourceType.cs
  49. 51 28
      MediaBrowser.Controller/Entities/Trailer.cs
  50. 4 5
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  51. 136 10
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  52. 60 0
      MediaBrowser.Controller/Entities/Video.cs
  53. 7 0
      MediaBrowser.Controller/IServerApplicationHost.cs
  54. 10 11
      MediaBrowser.Controller/Library/ILibraryManager.cs
  55. 3 0
      MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
  56. 0 10
      MediaBrowser.Controller/LiveTv/ILiveTvItem.cs
  57. 17 23
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  58. 3 1
      MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
  59. 12 2
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  60. 8 7
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  61. 6 6
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  62. 12 2
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  63. 9 1
      MediaBrowser.Controller/LiveTv/RecordingGroup.cs
  64. 7 6
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  65. 1 2
      MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
  66. 0 23
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  67. 0 11
      MediaBrowser.Controller/Providers/ChannelItemLookupInfo.cs
  68. 2 0
      MediaBrowser.Controller/Providers/TrailerInfo.cs
  69. 31 0
      MediaBrowser.Controller/Sorting/SortHelper.cs
  70. 1 1
      MediaBrowser.Controller/packages.config
  71. 2 2
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  72. 10 10
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  73. 1 1
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  74. 1 1
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  75. 1 1
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  76. 4 4
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  77. 42 0
      MediaBrowser.Dlna/Profiles/KodiProfile.cs
  78. 3 3
      MediaBrowser.Dlna/Profiles/LgTvProfile.cs
  79. 4 4
      MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml
  80. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Default.xml
  81. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
  82. 4 4
      MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
  83. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  84. 6 0
      MediaBrowser.Dlna/Profiles/Xml/Kodi.xml
  85. 4 4
      MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
  86. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
  87. 4 4
      MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
  88. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
  89. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
  90. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
  91. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  92. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  93. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  94. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  95. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  96. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  97. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  98. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  99. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  100. 4 4
      MediaBrowser.Dlna/Profiles/Xml/Vlc.xml

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

@@ -33,7 +33,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>

+ 1 - 1
Emby.Drawing/packages.config

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

+ 19 - 4
MediaBrowser.Api/ApiEntryPoint.cs

@@ -63,6 +63,15 @@ namespace MediaBrowser.Api
             _mediaSourceManager = mediaSourceManager;
             _mediaSourceManager = mediaSourceManager;
 
 
             Instance = this;
             Instance = this;
+            _sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
+        }
+
+        void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
+        {
+            if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
+            {
+                PingTranscodingJob(e.PlaySessionId, e.IsPaused);
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -300,26 +309,31 @@ namespace MediaBrowser.Api
                 PingTimer(job, false);
                 PingTimer(job, false);
             }
             }
         }
         }
-        internal void PingTranscodingJob(string playSessionId)
+        internal void PingTranscodingJob(string playSessionId, bool? isUserPaused)
         {
         {
             if (string.IsNullOrEmpty(playSessionId))
             if (string.IsNullOrEmpty(playSessionId))
             {
             {
                 throw new ArgumentNullException("playSessionId");
                 throw new ArgumentNullException("playSessionId");
             }
             }
 
 
-            //Logger.Debug("PingTranscodingJob PlaySessionId={0}", playSessionId);
+            //Logger.Debug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused);
 
 
-            var jobs = new List<TranscodingJob>();
+            List<TranscodingJob> jobs;
 
 
             lock (_activeTranscodingJobs)
             lock (_activeTranscodingJobs)
             {
             {
                 // This is really only needed for HLS. 
                 // This is really only needed for HLS. 
                 // Progressive streams can stop on their own reliably
                 // Progressive streams can stop on their own reliably
-                jobs = jobs.Where(j => string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)).ToList();
+                jobs = _activeTranscodingJobs.Where(j => string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)).ToList();
             }
             }
 
 
             foreach (var job in jobs)
             foreach (var job in jobs)
             {
             {
+                if (isUserPaused.HasValue)
+                {
+                    //Logger.Debug("Setting job.IsUserPaused to {0}. jobId: {1}", isUserPaused, job.Id);
+                    job.IsUserPaused = isUserPaused.Value;
+                }
                 PingTimer(job, true);
                 PingTimer(job, true);
             }
             }
         }
         }
@@ -655,6 +669,7 @@ namespace MediaBrowser.Api
         public object ProcessLock = new object();
         public object ProcessLock = new object();
 
 
         public bool HasExited { get; set; }
         public bool HasExited { get; set; }
+        public bool IsUserPaused { get; set; }
 
 
         public string Id { get; set; }
         public string Id { get; set; }
 
 

+ 13 - 5
MediaBrowser.Api/BaseApiService.cs

@@ -196,9 +196,13 @@ namespace MediaBrowser.Api
                 return name;
                 return name;
             }
             }
 
 
-            return libraryManager.RootFolder
-                .GetRecursiveChildren(i => i is IHasArtist)
-                .Cast<IHasArtist>()
+            var items = libraryManager.GetItemList(new InternalItemsQuery
+            {
+                IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name }
+            });
+
+            return items
+                .OfType<IHasArtist>()
                 .SelectMany(i => i.AllArtists)
                 .SelectMany(i => i.AllArtists)
                 .DistinctNames()
                 .DistinctNames()
                 .FirstOrDefault(i =>
                 .FirstOrDefault(i =>
@@ -239,8 +243,12 @@ namespace MediaBrowser.Api
                 return name;
                 return name;
             }
             }
 
 
-            return libraryManager.RootFolder
-                .GetRecursiveChildren(i => i is Game)
+            var items = libraryManager.GetItemList(new InternalItemsQuery
+            {
+                IncludeItemTypes = new[] { typeof(Game).Name }
+            });
+
+            return items
                 .SelectMany(i => i.Genres)
                 .SelectMany(i => i.Genres)
                 .DistinctNames()
                 .DistinctNames()
                 .FirstOrDefault(i =>
                 .FirstOrDefault(i =>

+ 3 - 3
MediaBrowser.Api/GamesService.cs

@@ -108,7 +108,7 @@ namespace MediaBrowser.Api
                 IncludeItemTypes = new[] { typeof(GameSystem).Name }
                 IncludeItemTypes = new[] { typeof(GameSystem).Name }
             };
             };
             var parentIds = new string[] { } ;
             var parentIds = new string[] { } ;
-            var gameSystems = _libraryManager.GetItems(query, parentIds)
+            var gameSystems = _libraryManager.GetItemList(query, parentIds)
                 .Cast<GameSystem>()
                 .Cast<GameSystem>()
                 .ToList();
                 .ToList();
 
 
@@ -129,7 +129,7 @@ namespace MediaBrowser.Api
                 IncludeItemTypes = new[] { typeof(Game).Name }
                 IncludeItemTypes = new[] { typeof(Game).Name }
             };
             };
             var parentIds = new string[] { };
             var parentIds = new string[] { };
-            var games = _libraryManager.GetItems(query, parentIds)
+            var games = _libraryManager.GetItemList(query, parentIds)
                 .Cast<Game>()
                 .Cast<Game>()
                 .ToList();
                 .ToList();
 
 
@@ -192,7 +192,7 @@ namespace MediaBrowser.Api
                 _userDataRepository,
                 _userDataRepository,
                 _dtoService,
                 _dtoService,
                 Logger,
                 Logger,
-                request, item => item is Game,
+                request, new[] { typeof(Game) },
                 SimilarItemsHelper.GetSimiliarityScore);
                 SimilarItemsHelper.GetSimiliarityScore);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);

+ 1 - 0
MediaBrowser.Api/Images/ImageService.cs

@@ -699,6 +699,7 @@ namespace MediaBrowser.Api.Images
 
 
         private ImageFormat[] GetClientSupportedFormats()
         private ImageFormat[] GetClientSupportedFormats()
         {
         {
+            //Logger.Debug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? new string[] { }));
             var supportsWebP = (Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
             var supportsWebP = (Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
 
 
             var userAgent = Request.UserAgent ?? string.Empty;
             var userAgent = Request.UserAgent ?? string.Empty;

+ 36 - 95
MediaBrowser.Api/Library/LibraryService.cs

@@ -289,7 +289,6 @@ namespace MediaBrowser.Api.Library
         private readonly IActivityManager _activityManager;
         private readonly IActivityManager _activityManager;
         private readonly ILocalizationManager _localization;
         private readonly ILocalizationManager _localization;
         private readonly ILiveTvManager _liveTv;
         private readonly ILiveTvManager _liveTv;
-        private readonly IChannelManager _channelManager;
         private readonly ITVSeriesManager _tvManager;
         private readonly ITVSeriesManager _tvManager;
         private readonly ILibraryMonitor _libraryMonitor;
         private readonly ILibraryMonitor _libraryMonitor;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
@@ -298,7 +297,7 @@ namespace MediaBrowser.Api.Library
         /// Initializes a new instance of the <see cref="LibraryService" /> class.
         /// Initializes a new instance of the <see cref="LibraryService" /> class.
         /// </summary>
         /// </summary>
         public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
         public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
-                              IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem)
+                              IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem)
         {
         {
             _itemRepo = itemRepo;
             _itemRepo = itemRepo;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
@@ -309,7 +308,6 @@ namespace MediaBrowser.Api.Library
             _activityManager = activityManager;
             _activityManager = activityManager;
             _localization = localization;
             _localization = localization;
             _liveTv = liveTv;
             _liveTv = liveTv;
-            _channelManager = channelManager;
             _tvManager = tvManager;
             _tvManager = tvManager;
             _libraryMonitor = libraryMonitor;
             _libraryMonitor = libraryMonitor;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
@@ -379,11 +377,10 @@ namespace MediaBrowser.Api.Library
             }
             }
 
 
             var program = item as IHasProgramAttributes;
             var program = item as IHasProgramAttributes;
-            var channelItem = item as ChannelVideoItem;
 
 
-            if (item is Movie || (program != null && program.IsMovie) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Movie) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.MovieExtra))
+            if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
             {
             {
-                return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _channelManager)
+                return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
                 {
                 {
                     AuthorizationContext = AuthorizationContext,
                     AuthorizationContext = AuthorizationContext,
                     Logger = Logger,
                     Logger = Logger,
@@ -400,7 +397,7 @@ namespace MediaBrowser.Api.Library
                 });
                 });
             }
             }
 
 
-            if (item is Series || (program != null && program.IsSeries) || (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Episode))
+            if (item is Series || (program != null && program.IsSeries))
             {
             {
                 return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager)
                 return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager)
                 {
                 {
@@ -447,13 +444,11 @@ namespace MediaBrowser.Api.Library
 
 
         public void Post(PostUpdatedSeries request)
         public void Post(PostUpdatedSeries request)
         {
         {
-            var series = _libraryManager.GetItems(new InternalItemsQuery
+            var series = _libraryManager.GetItemList(new InternalItemsQuery
             {
             {
                 IncludeItemTypes = new[] { typeof(Series).Name }
                 IncludeItemTypes = new[] { typeof(Series).Name }
 
 
-            }).Items;
-
-            series = series.Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+            }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
 
 
             if (series.Length > 0)
             if (series.Length > 0)
             {
             {
@@ -470,11 +465,11 @@ namespace MediaBrowser.Api.Library
 
 
         public void Post(PostUpdatedMovies request)
         public void Post(PostUpdatedMovies request)
         {
         {
-            var movies = _libraryManager.GetItems(new InternalItemsQuery
+            var movies = _libraryManager.GetItemList(new InternalItemsQuery
             {
             {
                 IncludeItemTypes = new[] { typeof(Movie).Name }
                 IncludeItemTypes = new[] { typeof(Movie).Name }
 
 
-            }).Items;
+            }).ToArray();
 
 
             if (!string.IsNullOrWhiteSpace(request.ImdbId))
             if (!string.IsNullOrWhiteSpace(request.ImdbId))
             {
             {
@@ -664,87 +659,38 @@ namespace MediaBrowser.Api.Library
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetItemCounts request)
         public object Get(GetItemCounts request)
         {
         {
-            var filteredItems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i.LocationType != LocationType.Virtual && FilterItem(i, request, request.UserId));
+            var user = string.IsNullOrWhiteSpace(request.UserId) ? null : _userManager.GetUserById(request.UserId);
 
 
             var counts = new ItemCounts
             var counts = new ItemCounts
             {
             {
-                AlbumCount = filteredItems.Count(i => i is MusicAlbum),
-                EpisodeCount = filteredItems.Count(i => i is Episode),
-                GameCount = filteredItems.Count(i => i is Game),
-                GameSystemCount = filteredItems.Count(i => i is GameSystem),
-                MovieCount = filteredItems.Count(i => i is Movie),
-                SeriesCount = filteredItems.Count(i => i is Series),
-                SongCount = filteredItems.Count(i => i is Audio),
-                MusicVideoCount = filteredItems.Count(i => i is MusicVideo),
-                BoxSetCount = filteredItems.Count(i => i is BoxSet),
-                BookCount = filteredItems.Count(i => i is Book),
-
-                UniqueTypes = filteredItems.Select(i => i.GetClientTypeName()).Distinct().ToList()
+                AlbumCount = GetCount(typeof(MusicAlbum), user, request),
+                EpisodeCount = GetCount(typeof(Episode), user, request),
+                GameCount = GetCount(typeof(Game), user, request),
+                GameSystemCount = GetCount(typeof(GameSystem), user, request),
+                MovieCount = GetCount(typeof(Movie), user, request),
+                SeriesCount = GetCount(typeof(Series), user, request),
+                SongCount = GetCount(typeof(Audio), user, request),
+                MusicVideoCount = GetCount(typeof(MusicVideo), user, request),
+                BoxSetCount = GetCount(typeof(BoxSet), user, request),
+                BookCount = GetCount(typeof(Book), user, request)
             };
             };
 
 
             return ToOptimizedSerializedResultUsingCache(counts);
             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)
+        private int GetCount(Type type, User user, GetItemCounts request)
         {
         {
-            if (!string.IsNullOrWhiteSpace(userId))
-            {
-                if (request.IsFavorite.HasValue)
-                {
-                    var val = request.IsFavorite.Value;
-
-                    if (_userDataManager.GetUserData(userId, item.GetUserDataKey()).IsFavorite != val)
-                    {
-                        return false;
-                    }
-                }
-            }
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { type.Name },
+                Limit = 0,
+                Recursive = true,
+                ExcludeLocationTypes = new[] { LocationType.Virtual },
+                SourceTypes = new[] { SourceType.Library },
+                IsFavorite = request.IsFavorite
+            };
 
 
-            return true;
+            return _libraryManager.GetItemsResult(query).TotalRecordCount;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -985,20 +931,15 @@ namespace MediaBrowser.Api.Library
              ? new string[] { }
              ? new string[] { }
              : request.IncludeItemTypes.Split(',');
              : request.IncludeItemTypes.Split(',');
 
 
-            Func<BaseItem, bool> filter = i =>
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+            
+            var query = new InternalItemsQuery(user)
             {
             {
-                if (includeTypes.Length > 0)
-                {
-                    if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
-                    {
-                        return false;
-                    }
-                }
-
-                return true;
+                IncludeItemTypes = includeTypes,
+                Recursive = true
             };
             };
 
 
-            IEnumerable<BaseItem> items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, filter);
+            var items = _libraryManager.GetItemList(query);
 
 
             var lookup = items
             var lookup = items
                 .ToLookup(i => i.ProductionYear ?? -1)
                 .ToLookup(i => i.ProductionYear ?? -1)

+ 4 - 4
MediaBrowser.Api/Library/LibraryStructureService.cs

@@ -201,10 +201,10 @@ namespace MediaBrowser.Api.Library
             var rootFolderPath = _appPaths.DefaultUserViewsPath;
             var rootFolderPath = _appPaths.DefaultUserViewsPath;
 
 
             var virtualFolderPath = Path.Combine(rootFolderPath, name);
             var virtualFolderPath = Path.Combine(rootFolderPath, name);
-
-            if (_fileSystem.DirectoryExists(virtualFolderPath))
+            while (_fileSystem.DirectoryExists(virtualFolderPath))
             {
             {
-                throw new ArgumentException("There is already a media library with the name " + name + ".");
+                name += "1";
+                virtualFolderPath = Path.Combine(rootFolderPath, name);
             }
             }
 
 
             if (request.Paths != null)
             if (request.Paths != null)
@@ -236,7 +236,7 @@ namespace MediaBrowser.Api.Library
                 {
                 {
                     foreach (var path in request.Paths)
                     foreach (var path in request.Paths)
                     {
                     {
-                        LibraryHelpers.AddMediaPath(_fileSystem, request.Name, path, _appPaths);
+                        LibraryHelpers.AddMediaPath(_fileSystem, name, path, _appPaths);
                     }
                     }
                 }
                 }
             }
             }

+ 36 - 6
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -478,19 +478,30 @@ namespace MediaBrowser.Api.LiveTv
         public string Feature { get; set; }
         public string Feature { get; set; }
     }
     }
 
 
+    [Route("/LiveTv/TunerHosts/Satip/IniMappings", "GET", Summary = "Gets available mappings")]
+    [Authenticated(AllowBeforeStartupWizard = true)]
+    public class GetSatIniMappings : IReturn<List<NameValuePair>>
+    {
+        
+    }
+
     public class LiveTvService : BaseApiService
     public class LiveTvService : BaseApiService
     {
     {
         private readonly ILiveTvManager _liveTvManager;
         private readonly ILiveTvManager _liveTvManager;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
         private readonly IConfigurationManager _config;
         private readonly IConfigurationManager _config;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
+        private readonly ILibraryManager _libraryManager;
+        private readonly IDtoService _dtoService;
 
 
-        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient)
+        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
         {
         {
             _liveTvManager = liveTvManager;
             _liveTvManager = liveTvManager;
             _userManager = userManager;
             _userManager = userManager;
             _config = config;
             _config = config;
             _httpClient = httpClient;
             _httpClient = httpClient;
+            _libraryManager = libraryManager;
+            _dtoService = dtoService;
         }
         }
 
 
         public async Task<object> Get(GetLiveTvRegistrationInfo request)
         public async Task<object> Get(GetLiveTvRegistrationInfo request)
@@ -500,6 +511,11 @@ namespace MediaBrowser.Api.LiveTv
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
 
 
+        public object Get(GetSatIniMappings request)
+        {
+            return ToOptimizedResult(_liveTvManager.GetSatIniMappings());
+        }
+
         public async Task<object> Get(GetSchedulesDirectCountries request)
         public async Task<object> Get(GetSchedulesDirectCountries request)
         {
         {
             // https://json.schedulesdirect.org/20141201/available/countries
             // https://json.schedulesdirect.org/20141201/available/countries
@@ -581,7 +597,7 @@ namespace MediaBrowser.Api.LiveTv
 
 
         public async Task<object> Get(GetChannels request)
         public async Task<object> Get(GetChannels request)
         {
         {
-            var result = await _liveTvManager.GetChannels(new LiveTvChannelQuery
+            var channelResult = await _liveTvManager.GetInternalChannels(new LiveTvChannelQuery
             {
             {
                 ChannelType = request.Type,
                 ChannelType = request.Type,
                 UserId = request.UserId,
                 UserId = request.UserId,
@@ -593,16 +609,30 @@ namespace MediaBrowser.Api.LiveTv
                 EnableFavoriteSorting = request.EnableFavoriteSorting,
                 EnableFavoriteSorting = request.EnableFavoriteSorting,
                 AddCurrentProgram = request.AddCurrentProgram
                 AddCurrentProgram = request.AddCurrentProgram
 
 
-            }, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
+            }, CancellationToken.None).ConfigureAwait(false);
+
+            var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+
+            var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, GetDtoOptions(Request), user).ToArray();
 
 
+            var result = new QueryResult<BaseItemDto>
+            {
+                Items = returnArray,
+                TotalRecordCount = channelResult.TotalRecordCount
+            };
+            
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
-        public async Task<object> Get(GetChannel request)
+        public object Get(GetChannel request)
         {
         {
-            var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+            var user = string.IsNullOrWhiteSpace(request.UserId) ? null : _userManager.GetUserById(request.UserId);
+
+            var item = _libraryManager.GetItemById(request.Id);
+
+            var dtoOptions = GetDtoOptions(request);
 
 
-            var result = await _liveTvManager.GetChannel(request.Id, CancellationToken.None, user).ConfigureAwait(false);
+            var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }

+ 1 - 1
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -47,7 +47,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="MoreLinq">
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>

+ 23 - 40
MediaBrowser.Api/Movies/MoviesService.cs

@@ -91,22 +91,21 @@ namespace MediaBrowser.Api.Movies
         private readonly IItemRepository _itemRepo;
         private readonly IItemRepository _itemRepo;
         private readonly IDtoService _dtoService;
         private readonly IDtoService _dtoService;
 
 
-        private readonly IChannelManager _channelManager;
-
         /// <summary>
         /// <summary>
-        /// Initializes a new instance of the <see cref="MoviesService"/> class.
+        /// Initializes a new instance of the <see cref="MoviesService" /> class.
         /// </summary>
         /// </summary>
         /// <param name="userManager">The user manager.</param>
         /// <param name="userManager">The user manager.</param>
         /// <param name="userDataRepository">The user data repository.</param>
         /// <param name="userDataRepository">The user data repository.</param>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="libraryManager">The library manager.</param>
-        public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IChannelManager channelManager)
+        /// <param name="itemRepo">The item repo.</param>
+        /// <param name="dtoService">The dto service.</param>
+        public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
         {
         {
             _userManager = userManager;
             _userManager = userManager;
             _userDataRepository = userDataRepository;
             _userDataRepository = userDataRepository;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _itemRepo = itemRepo;
             _itemRepo = itemRepo;
             _dtoService = dtoService;
             _dtoService = dtoService;
-            _channelManager = channelManager;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -138,8 +137,16 @@ namespace MediaBrowser.Api.Movies
             {
             {
                 IncludeItemTypes = new[] { typeof(Movie).Name }
                 IncludeItemTypes = new[] { typeof(Movie).Name }
             };
             };
+
+            if (user.Configuration.IncludeTrailersInSuggestions)
+            {
+                var includeList = query.IncludeItemTypes.ToList();
+                includeList.Add(typeof(Trailer).Name);
+                query.IncludeItemTypes = includeList.ToArray();
+            }
+
             var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
             var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
-            var movies = _libraryManager.GetItems(query, parentIds);
+            var movies = _libraryManager.GetItemList(query, parentIds);
             movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
             movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
 
 
             var listEligibleForCategories = new List<BaseItem>();
             var listEligibleForCategories = new List<BaseItem>();
@@ -150,19 +157,6 @@ namespace MediaBrowser.Api.Movies
             listEligibleForCategories.AddRange(list);
             listEligibleForCategories.AddRange(list);
             listEligibleForSuggestion.AddRange(list);
             listEligibleForSuggestion.AddRange(list);
 
 
-            if (user.Configuration.IncludeTrailersInSuggestions)
-            {
-                var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
-                {
-                    ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
-                    ExtraTypes = new[] { ExtraType.Trailer },
-                    UserId = user.Id.ToString("N")
-
-                }, CancellationToken.None).ConfigureAwait(false);
-
-                listEligibleForSuggestion.AddRange(trailerResult.Items);
-            }
-
             listEligibleForCategories = listEligibleForCategories
             listEligibleForCategories = listEligibleForCategories
                 .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
                 .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
                 .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase)
                 .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase)
@@ -194,36 +188,25 @@ namespace MediaBrowser.Api.Movies
             {
             {
                 IncludeItemTypes = new[] { typeof(Movie).Name }
                 IncludeItemTypes = new[] { typeof(Movie).Name }
             };
             };
+
+            if (user == null || user.Configuration.IncludeTrailersInSuggestions)
+            {
+                var includeList = query.IncludeItemTypes.ToList();
+                includeList.Add(typeof(Trailer).Name);
+                query.IncludeItemTypes = includeList.ToArray();
+            }
+
             var parentIds = new string[] { };
             var parentIds = new string[] { };
-            var list = _libraryManager.GetItems(query, parentIds)
+            var list = _libraryManager.GetItemList(query, parentIds)
                 .Where(i =>
                 .Where(i =>
                 {
                 {
                     // Strip out secondary versions
                     // Strip out secondary versions
                     var v = i as Video;
                     var v = i as Video;
                     return v != null && !v.PrimaryVersionId.HasValue;
                     return v != null && !v.PrimaryVersionId.HasValue;
                 })
                 })
+                .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
                 .ToList();
                 .ToList();
 
 
-            if (user != null && user.Configuration.IncludeTrailersInSuggestions)
-            {
-                var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
-                {
-                    ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
-                    ExtraTypes = new[] { ExtraType.Trailer },
-                    UserId = user.Id.ToString("N")
-
-                }, CancellationToken.None).ConfigureAwait(false);
-
-                var newTrailers = trailerResult.Items;
-
-                list.AddRange(newTrailers);
-
-                list = list
-                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
-                    .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString(), StringComparer.OrdinalIgnoreCase)
-                    .ToList();
-            }
-
             if (item is Video)
             if (item is Video)
             {
             {
                 var imdbId = item.GetProviderId(MetadataProviders.Imdb);
                 var imdbId = item.GetProviderId(MetadataProviders.Imdb);

+ 21 - 68
MediaBrowser.Api/Movies/TrailersService.cs

@@ -12,6 +12,9 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Localization;
+using MediaBrowser.Model.Serialization;
 
 
 namespace MediaBrowser.Api.Movies
 namespace MediaBrowser.Api.Movies
 {
 {
@@ -41,87 +44,37 @@ namespace MediaBrowser.Api.Movies
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
 
 
         private readonly IDtoService _dtoService;
         private readonly IDtoService _dtoService;
-        private readonly IChannelManager _channelManager;
+        private readonly ICollectionManager _collectionManager;
+        private readonly ILocalizationManager _localizationManager;
+        private readonly IJsonSerializer _json;
 
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="TrailersService"/> class.
-        /// </summary>
-        /// <param name="userManager">The user manager.</param>
-        /// <param name="userDataRepository">The user data repository.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IChannelManager channelManager)
+        public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, ICollectionManager collectionManager, ILocalizationManager localizationManager, IJsonSerializer json)
         {
         {
             _userManager = userManager;
             _userManager = userManager;
             _userDataRepository = userDataRepository;
             _userDataRepository = userDataRepository;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _dtoService = dtoService;
             _dtoService = dtoService;
-            _channelManager = channelManager;
+            _collectionManager = collectionManager;
+            _localizationManager = localizationManager;
+            _json = json;
         }
         }
 
 
-        public async Task<object> Get(Getrailers request)
+        public object Get(Getrailers request)
         {
         {
-            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-            var result = await GetAllTrailers(user).ConfigureAwait(false);
-
-            IEnumerable<BaseItem> items = result.Items;
-
-            // Apply filters
-            // Run them starting with the ones that are likely to reduce the list the most
-            foreach (var filter in request.GetFilters().OrderByDescending(f => (int)f))
-            {
-                items = ItemsService.ApplyFilter(items, filter, user, _userDataRepository);
-            }
-
-            items = _libraryManager.Sort(items, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
+            var json = _json.SerializeToString(request);
+            var getItems = _json.DeserializeFromString<GetItems>(json);
 
 
-            var itemsArray = items.ToList();
+            getItems.IncludeItemTypes = "Trailer";
 
 
-            var pagedItems = ApplyPaging(request, itemsArray);
-
-            var dtoOptions = GetDtoOptions(request);
-
-            var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
-
-            return new ItemsResult
+            return new ItemsService(_userManager, _libraryManager, _userDataRepository, _localizationManager, _dtoService, _collectionManager)
             {
             {
-                TotalRecordCount = itemsArray.Count,
-                Items = returnItems
-            };
-        }
+                AuthorizationContext = AuthorizationContext,
+                Logger = Logger,
+                Request = Request,
+                ResultFactory = ResultFactory,
+                SessionContext = SessionContext
 
 
-        private IEnumerable<BaseItem> ApplyPaging(Getrailers request, IEnumerable<BaseItem> items)
-        {
-            // Start at
-            if (request.StartIndex.HasValue)
-            {
-                items = items.Skip(request.StartIndex.Value);
-            }
-
-            // Return limit
-            if (request.Limit.HasValue)
-            {
-                items = items.Take(request.Limit.Value);
-            }
-
-            return items;
-        }
-
-        private async Task<QueryResult<BaseItem>> GetAllTrailers(User user)
-        {
-            var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
-            {
-                ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
-                ExtraTypes = new[] { ExtraType.Trailer },
-                UserId = user.Id.ToString("N")
-
-            }, CancellationToken.None).ConfigureAwait(false);
-
-
-            return new QueryResult<BaseItem>
-            {
-                Items = trailerResult.Items,
-                TotalRecordCount = trailerResult.TotalRecordCount
-            };
+            }.Get(getItems);
         }
         }
     }
     }
 }
 }

+ 9 - 37
MediaBrowser.Api/Music/AlbumsService.cs

@@ -52,10 +52,15 @@ namespace MediaBrowser.Api.Music
 
 
         public object Get(GetSimilarArtists request)
         public object Get(GetSimilarArtists request)
         {
         {
-            var result = GetSimilarItemsResult(
-
-                request, 
+            var dtoOptions = GetDtoOptions(request);
 
 
+            var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
+                _itemRepo,
+                _libraryManager,
+                _userDataRepository,
+                _dtoService,
+                Logger,
+                request, new[] { typeof(MusicArtist) },
                 SimilarItemsHelper.GetSimiliarityScore);
                 SimilarItemsHelper.GetSimiliarityScore);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
@@ -76,44 +81,11 @@ namespace MediaBrowser.Api.Music
                 _userDataRepository,
                 _userDataRepository,
                 _dtoService,
                 _dtoService,
                 Logger,
                 Logger,
-                request, item => item is MusicAlbum,
+                request, new[] { typeof(MusicAlbum) },
                 GetAlbumSimilarityScore);
                 GetAlbumSimilarityScore);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
-
-        private 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);
-
-            var inputItems = _libraryManager.GetArtists(user.RootFolder.GetRecursiveChildren(user, i => i is IHasArtist).OfType<IHasArtist>());
-
-            var list = inputItems.ToList();
-
-            var items = SimilarItemsHelper.GetSimilaritems(item, _libraryManager, list, getSimilarityScore).ToList();
-
-            IEnumerable<BaseItem> returnItems = items;
-
-            if (request.Limit.HasValue)
-            {
-                returnItems = returnItems.Take(request.Limit.Value);
-            }
-
-            var dtoOptions = GetDtoOptions(request);
-
-            var result = new ItemsResult
-            {
-                Items = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ToArray(),
-
-                TotalRecordCount = items.Count
-            };
-
-            return result;
-        }
         
         
         /// <summary>
         /// <summary>
         /// Gets the album similarity score.
         /// Gets the album similarity score.

+ 15 - 4
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -214,7 +214,7 @@ namespace MediaBrowser.Api.Playback
                 args += " -map -0:a";
                 args += " -map -0:a";
             }
             }
 
 
-            if (state.SubtitleStream == null)
+            if (state.SubtitleStream == null || state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Hls)
             {
             {
                 args += " -map -0:s";
                 args += " -map -0:s";
             }
             }
@@ -477,7 +477,7 @@ namespace MediaBrowser.Api.Playback
 
 
             var pts = string.Empty;
             var pts = string.Empty;
 
 
-            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && !state.VideoRequest.CopyTimestamps)
+            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode && !state.VideoRequest.CopyTimestamps)
             {
             {
                 var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds;
                 var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds;
 
 
@@ -575,7 +575,7 @@ namespace MediaBrowser.Api.Playback
 
 
             var output = string.Empty;
             var output = string.Empty;
 
 
-            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream)
+            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode)
             {
             {
                 var subParam = GetTextSubtitleParam(state);
                 var subParam = GetTextSubtitleParam(state);
 
 
@@ -865,7 +865,7 @@ namespace MediaBrowser.Api.Playback
         {
         {
             var arg = string.Format("-i {0}", GetInputPathArgument(state));
             var arg = string.Format("-i {0}", GetInputPathArgument(state));
 
 
-            if (state.SubtitleStream != null)
+            if (state.SubtitleStream != null && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode)
             {
             {
                 if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
                 if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)
                 {
                 {
@@ -1482,6 +1482,17 @@ namespace MediaBrowser.Api.Playback
                         videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
                         videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
                     }
                     }
                 }
                 }
+                else if (i == 25)
+                {
+                    if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
+                    {
+                        SubtitleDeliveryMethod method;
+                        if (Enum.TryParse(val, out method))
+                        {
+                            videoRequest.SubtitleMethod = method;
+                        }
+                    }
+                }
             }
             }
         }
         }
 
 

+ 24 - 16
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -529,7 +529,12 @@ namespace MediaBrowser.Api.Playback.Hls
                 "subs" :
                 "subs" :
                 null;
                 null;
 
 
-            AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup);
+            if (!string.IsNullOrWhiteSpace(subtitleGroup))
+            {
+                AddSubtitles(state, subtitleStreams, builder);
+            }
+
+            AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
 
 
             if (EnableAdaptiveBitrateStreaming(state, isLiveStream))
             if (EnableAdaptiveBitrateStreaming(state, isLiveStream))
             {
             {
@@ -540,17 +545,12 @@ namespace MediaBrowser.Api.Playback.Hls
 
 
                 var newBitrate = totalBitrate - variation;
                 var newBitrate = totalBitrate - variation;
                 var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, (requestedVideoBitrate - variation));
                 var variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, (requestedVideoBitrate - variation));
-                AppendPlaylist(builder, variantUrl, newBitrate, subtitleGroup);
+                AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
 
 
                 variation *= 2;
                 variation *= 2;
                 newBitrate = totalBitrate - variation;
                 newBitrate = totalBitrate - variation;
                 variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, (requestedVideoBitrate - variation));
                 variantUrl = ReplaceBitrate(playlistUrl, requestedVideoBitrate, (requestedVideoBitrate - variation));
-                AppendPlaylist(builder, variantUrl, newBitrate, subtitleGroup);
-            }
-
-            if (!string.IsNullOrWhiteSpace(subtitleGroup))
-            {
-                AddSubtitles(state, subtitleStreams, builder);
+                AppendPlaylist(builder, state, variantUrl, newBitrate, subtitleGroup);
             }
             }
 
 
             return builder.ToString();
             return builder.ToString();
@@ -566,11 +566,11 @@ namespace MediaBrowser.Api.Playback.Hls
 
 
         private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder)
         private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder)
         {
         {
-            var selectedIndex = state.SubtitleStream == null ? (int?)null : state.SubtitleStream.Index;
+            var selectedIndex = state.SubtitleStream == null || state.VideoRequest.SubtitleMethod != SubtitleDeliveryMethod.Hls ? (int?)null : state.SubtitleStream.Index;
 
 
             foreach (var stream in subtitles)
             foreach (var stream in subtitles)
             {
             {
-                const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},URI=\"{3}\",LANGUAGE=\"{4}\"";
+                const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},AUTOSELECT=YES,URI=\"{3}\",LANGUAGE=\"{4}\"";
 
 
                 var name = stream.Language;
                 var name = stream.Language;
 
 
@@ -579,10 +579,11 @@ namespace MediaBrowser.Api.Playback.Hls
 
 
                 if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown";
                 if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown";
 
 
-                var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}",
+                var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}&api_key={3}",
                     state.Request.MediaSourceId,
                     state.Request.MediaSourceId,
                     stream.Index.ToString(UsCulture),
                     stream.Index.ToString(UsCulture),
-                    30.ToString(UsCulture));
+                    30.ToString(UsCulture),
+                    AuthorizationContext.GetAuthorizationInfo(Request).Token);
 
 
                 var line = string.Format(format,
                 var line = string.Format(format,
                     name,
                     name,
@@ -635,9 +636,15 @@ namespace MediaBrowser.Api.Playback.Hls
             //return state.VideoRequest.VideoBitRate.HasValue;
             //return state.VideoRequest.VideoBitRate.HasValue;
         }
         }
 
 
-        private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
+        private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup)
         {
         {
-            var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(UsCulture);
+            var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(UsCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(UsCulture);
+
+            // tvos wants resolution, codecs, framerate
+            //if (state.TargetFramerate.HasValue)
+            //{
+            //    header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture));
+            //}
 
 
             if (!string.IsNullOrWhiteSpace(subtitleGroup))
             if (!string.IsNullOrWhiteSpace(subtitleGroup))
             {
             {
@@ -694,6 +701,7 @@ namespace MediaBrowser.Api.Playback.Hls
             var builder = new StringBuilder();
             var builder = new StringBuilder();
 
 
             builder.AppendLine("#EXTM3U");
             builder.AppendLine("#EXTM3U");
+            builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
             builder.AppendLine("#EXT-X-VERSION:3");
             builder.AppendLine("#EXT-X-VERSION:3");
             builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
             builder.AppendLine("#EXT-X-TARGETDURATION:" + Math.Ceiling((segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)).ToString(UsCulture));
             builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
             builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
@@ -820,7 +828,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
                 var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
                     state.SegmentLength.ToString(UsCulture));
                     state.SegmentLength.ToString(UsCulture));
 
 
-                var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
+                var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
 
 
                 args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
                 args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
 
 
@@ -846,7 +854,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
 
         private bool EnableCopyTs(StreamState state)
         private bool EnableCopyTs(StreamState state)
         {
         {
-            return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream;
+            return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
         }
         }
 
 
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)

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

@@ -9,6 +9,7 @@ using MediaBrowser.Model.Serialization;
 using ServiceStack;
 using ServiceStack;
 using System;
 using System;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Model.Dlna;
 
 
 namespace MediaBrowser.Api.Playback.Hls
 namespace MediaBrowser.Api.Playback.Hls
 {
 {
@@ -104,7 +105,7 @@ namespace MediaBrowser.Api.Playback.Hls
             var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
             var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
                 state.SegmentLength.ToString(UsCulture));
                 state.SegmentLength.ToString(UsCulture));
 
 
-            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
+            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
 
 
             args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
             args += " " + GetVideoQualityParam(state, GetH264Encoder(state)) + keyFrameArg;
 
 

+ 6 - 2
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -73,9 +73,13 @@ namespace MediaBrowser.Api.Playback.Progressive
                 audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
                 audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture));
             }
             }
 
 
-            if (state.OutputAudioSampleRate.HasValue)
+            // opus will fail on 44100
+            if (!string.Equals(state.OutputAudioCodec, "opus", global::System.StringComparison.OrdinalIgnoreCase))
             {
             {
-                audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
+                if (state.OutputAudioSampleRate.HasValue)
+                {
+                    audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture));
+                }
             }
             }
 
 
             const string vn = " -vn";
             const string vn = " -vn";

+ 33 - 7
MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs

@@ -61,8 +61,9 @@ namespace MediaBrowser.Api.Playback.Progressive
         {
         {
             try
             try
             {
             {
-                new ProgressiveFileCopier(_fileSystem, _job)
-                    .StreamFile(Path, responseStream);
+                var task = new ProgressiveFileCopier(_fileSystem, _job, Logger).StreamFile(Path, responseStream);
+
+                Task.WaitAll(task);
             }
             }
             catch (IOException)
             catch (IOException)
             {
             {
@@ -91,19 +92,21 @@ namespace MediaBrowser.Api.Playback.Progressive
     {
     {
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly TranscodingJob _job;
         private readonly TranscodingJob _job;
+        private readonly ILogger _logger;
 
 
         // 256k
         // 256k
         private const int BufferSize = 262144;
         private const int BufferSize = 262144;
-        
+
         private long _bytesWritten = 0;
         private long _bytesWritten = 0;
 
 
-        public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job)
+        public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job, ILogger logger)
         {
         {
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _job = job;
             _job = job;
+            _logger = logger;
         }
         }
 
 
-        public void StreamFile(string path, Stream outputStream)
+        public async Task StreamFile(string path, Stream outputStream)
         {
         {
             var eofCount = 0;
             var eofCount = 0;
             long position = 0;
             long position = 0;
@@ -126,8 +129,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                         {
                         {
                             eofCount++;
                             eofCount++;
                         }
                         }
-                        var task = Task.Delay(100);
-                        Task.WaitAll(task);
+                        await Task.Delay(100).ConfigureAwait(false);
                     }
                     }
                     else
                     else
                     {
                     {
@@ -145,6 +147,30 @@ namespace MediaBrowser.Api.Playback.Progressive
             int count;
             int count;
             while ((count = source.Read(array, 0, array.Length)) != 0)
             while ((count = source.Read(array, 0, array.Length)) != 0)
             {
             {
+                //if (_job != null)
+                //{
+                //    var didPause = false;
+                //    var totalPauseTime = 0;
+
+                //    if (_job.IsUserPaused)
+                //    {
+                //        _logger.Debug("Pausing writing to network stream while user has paused playback.");
+
+                //        while (_job.IsUserPaused && totalPauseTime < 30000)
+                //        {
+                //            didPause = true;
+                //            var pauseTime = 500;
+                //            totalPauseTime += pauseTime;
+                //            await Task.Delay(pauseTime).ConfigureAwait(false);
+                //        }
+                //    }
+
+                //    if (didPause)
+                //    {
+                //        _logger.Debug("Resuming writing to network stream due to user unpausing playback.");
+                //    }
+                //}
+
                 destination.Write(array, 0, count);
                 destination.Write(array, 0, count);
 
 
                 _bytesWritten += count;
                 _bytesWritten += count;

+ 2 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -13,6 +13,7 @@ using System;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Model.Dlna;
 
 
 namespace MediaBrowser.Api.Playback.Progressive
 namespace MediaBrowser.Api.Playback.Progressive
 {
 {
@@ -161,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
 
             args += keyFrameArg;
             args += keyFrameArg;
 
 
-            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
+            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
 
 
             var hasCopyTs = false;
             var hasCopyTs = false;
             // Add resolution params, if specified
             // Add resolution params, if specified

+ 38 - 243
MediaBrowser.Api/Reports/ReportsService.cs

@@ -213,8 +213,6 @@ namespace MediaBrowser.Api.Reports
                 SortBy = request.GetOrderBy(),
                 SortBy = request.GetOrderBy(),
                 SortOrder = request.SortOrder ?? SortOrder.Ascending,
                 SortOrder = request.SortOrder ?? SortOrder.Ascending,
 
 
-                Filter = i => ApplyAdditionalFilters(request, i, user, _libraryManager),
-
                 IsFavorite = request.IsFavorite,
                 IsFavorite = request.IsFavorite,
                 Limit = request.Limit,
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,
                 StartIndex = request.StartIndex,
@@ -258,7 +256,10 @@ namespace MediaBrowser.Api.Reports
                 MinPlayers = request.MinPlayers,
                 MinPlayers = request.MinPlayers,
                 MaxPlayers = request.MaxPlayers,
                 MaxPlayers = request.MaxPlayers,
                 MinCommunityRating = request.MinCommunityRating,
                 MinCommunityRating = request.MinCommunityRating,
-                MinCriticRating = request.MinCriticRating
+                MinCriticRating = request.MinCriticRating,
+                ParentIndexNumber = request.ParentIndexNumber,
+                AiredDuringSeason = request.AiredDuringSeason,
+                AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater
             };
             };
 
 
             if (!string.IsNullOrWhiteSpace(request.Ids))
             if (!string.IsNullOrWhiteSpace(request.Ids))
@@ -302,285 +303,79 @@ namespace MediaBrowser.Api.Reports
                 }
                 }
             }
             }
 
 
-            if (request.HasQueryLimit == false)
-            {
-                query.StartIndex = null;
-                query.Limit = null;
-            }
-
-            return query;
-        }
-
-        private bool ApplyAdditionalFilters(BaseReportRequest request, BaseItem i, User user, ILibraryManager libraryManager)
-        {
-            // Artists
-            if (!string.IsNullOrEmpty(request.ArtistIds))
-            {
-                var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
-
-                var audio = i as IHasArtist;
-
-                if (!(audio != null && artistIds.Any(id =>
-                {
-                    var artistItem = libraryManager.GetItemById(id);
-                    return artistItem != null && audio.HasAnyArtist(artistItem.Name);
-                })))
-                {
-                    return false;
-                }
-            }
-
-            // Artists
-            if (!string.IsNullOrEmpty(request.Artists))
-            {
-                var artists = request.Artists.Split('|');
-
-                var audio = i as IHasArtist;
-
-                if (!(audio != null && artists.Any(audio.HasAnyArtist)))
-                {
-                    return false;
-                }
-            }
-
-            // Albums
-            if (!string.IsNullOrEmpty(request.Albums))
-            {
-                var albums = request.Albums.Split('|');
-
-                var audio = i as Audio;
-
-                if (audio != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                var album = i as MusicAlbum;
-
-                if (album != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                var musicVideo = i as MusicVideo;
-
-                if (musicVideo != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                return false;
-            }
-
-            // Min index number
-            if (request.MinIndexNumber.HasValue)
+            if (!string.IsNullOrEmpty(request.MinPremiereDate))
             {
             {
-                if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
-                {
-                    return false;
-                }
+                query.MinPremiereDate = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
             }
             }
 
 
-            // Min official rating
-            if (!string.IsNullOrEmpty(request.MinOfficialRating))
+            if (!string.IsNullOrEmpty(request.MaxPremiereDate))
             {
             {
-                var level = _localization.GetRatingLevel(request.MinOfficialRating);
-
-                if (level.HasValue)
-                {
-                    var rating = i.CustomRating;
-
-                    if (string.IsNullOrEmpty(rating))
-                    {
-                        rating = i.OfficialRating;
-                    }
-
-                    if (!string.IsNullOrEmpty(rating))
-                    {
-                        var itemLevel = _localization.GetRatingLevel(rating);
-
-                        if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value))
-                        {
-                            return false;
-                        }
-                    }
-                }
+                query.MaxPremiereDate = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
             }
             }
 
 
-            // Max official rating
-            if (!string.IsNullOrEmpty(request.MaxOfficialRating))
+            // Filter by Series Status
+            if (!string.IsNullOrEmpty(request.SeriesStatus))
             {
             {
-                var level = _localization.GetRatingLevel(request.MaxOfficialRating);
-
-                if (level.HasValue)
-                {
-                    var rating = i.CustomRating;
-
-                    if (string.IsNullOrEmpty(rating))
-                    {
-                        rating = i.OfficialRating;
-                    }
-
-                    if (!string.IsNullOrEmpty(rating))
-                    {
-                        var itemLevel = _localization.GetRatingLevel(rating);
-
-                        if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value))
-                        {
-                            return false;
-                        }
-                    }
-                }
+                query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray();
             }
             }
 
 
-            // LocationTypes
-            if (!string.IsNullOrEmpty(request.LocationTypes))
+            // Filter by Series AirDays
+            if (!string.IsNullOrEmpty(request.AirDays))
             {
             {
-                var vals = request.LocationTypes.Split(',');
-                if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
+                query.AirDays = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)).ToArray();
             }
             }
 
 
             // ExcludeLocationTypes
             // ExcludeLocationTypes
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             {
             {
-                var vals = request.ExcludeLocationTypes.Split(',');
-                if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
+                query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
-            {
-                var ok = new[] { i }.OfType<IHasAlbumArtist>()
-                    .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1);
-
-                if (!ok)
-                {
-                    return false;
-                }
-            }
-
-            // Filter by Series Status
-            if (!string.IsNullOrEmpty(request.SeriesStatus))
+            if (!string.IsNullOrEmpty(request.LocationTypes))
             {
             {
-                var vals = request.SeriesStatus.Split(',');
-
-                var ok = new[] { i }.OfType<Series>().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase));
-
-                if (!ok)
-                {
-                    return false;
-                }
+                query.LocationTypes = request.LocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
             }
             }
 
 
-            // Filter by Series AirDays
-            if (!string.IsNullOrEmpty(request.AirDays))
+            // Min official rating
+            if (!string.IsNullOrEmpty(request.MinOfficialRating))
             {
             {
-                var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true));
-
-                var ok = new[] { i }.OfType<Series>().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d)));
-
-                if (!ok)
-                {
-                    return false;
-                }
+                query.MinParentalRating = _localization.GetRatingLevel(request.MinOfficialRating);
             }
             }
 
 
-            if (request.ParentIndexNumber.HasValue)
-            {
-                var filterValue = request.ParentIndexNumber.Value;
-
-                var episode = i as Episode;
-
-                if (episode != null)
-                {
-                    if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue)
-                    {
-                        return false;
-                    }
-                }
-
-                var song = i as Audio;
-
-                if (song != null)
-                {
-                    if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue)
-                    {
-                        return false;
-                    }
-                }
-            }
-
-            if (request.AiredDuringSeason.HasValue)
+            // Max official rating
+            if (!string.IsNullOrEmpty(request.MaxOfficialRating))
             {
             {
-                var episode = i as Episode;
-
-                if (episode == null)
-                {
-                    return false;
-                }
-
-                if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any())
-                {
-                    return false;
-                }
+                query.MaxParentalRating = _localization.GetRatingLevel(request.MinOfficialRating);
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.MinPremiereDate))
+            // Artists
+            if (!string.IsNullOrEmpty(request.ArtistIds))
             {
             {
-                var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
+                var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
 
 
-                if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date))
-                {
-                    return false;
-                }
+                var artistItems = artistIds.Select(_libraryManager.GetItemById).Where(i => i != null).ToList();
+                query.ArtistNames = artistItems.Select(i => i.Name).ToArray();
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.MaxPremiereDate))
+            // Artists
+            if (!string.IsNullOrEmpty(request.Artists))
             {
             {
-                var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
-
-                if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date))
-                {
-                    return false;
-                }
+                query.ArtistNames = request.Artists.Split('|');
             }
             }
 
 
-            return true;
-        }
-
-        /// <summary> Applies the paging. </summary>
-        /// <param name="request"> The request. </param>
-        /// <param name="items"> The items. </param>
-        /// <returns> IEnumerable{BaseItem}. </returns>
-        private IEnumerable<BaseItem> ApplyPaging(BaseReportRequest request, IEnumerable<BaseItem> items)
-        {
-            // Start at
-            if (request.StartIndex.HasValue)
+            // Albums
+            if (!string.IsNullOrEmpty(request.Albums))
             {
             {
-                items = items.Skip(request.StartIndex.Value);
+                query.AlbumNames = request.Albums.Split('|');
             }
             }
 
 
-            // Return limit
-            if (request.Limit.HasValue)
+            if (request.HasQueryLimit == false)
             {
             {
-                items = items.Take(request.Limit.Value);
+                query.StartIndex = null;
+                query.Limit = null;
             }
             }
 
 
-            return items;
+            return query;
         }
         }
 
 
         /// <summary> Gets query result. </summary>
         /// <summary> Gets query result. </summary>

+ 7 - 19
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -54,21 +54,7 @@ namespace MediaBrowser.Api
     /// </summary>
     /// </summary>
     public static class SimilarItemsHelper
     public static class SimilarItemsHelper
     {
     {
-        /// <summary>
-        /// Gets the similar items.
-        /// </summary>
-        /// <param name="dtoOptions">The dto options.</param>
-        /// <param name="userManager">The user manager.</param>
-        /// <param name="itemRepository">The item repository.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <param name="userDataRepository">The user data repository.</param>
-        /// <param name="dtoService">The dto service.</param>
-        /// <param name="logger">The logger.</param>
-        /// <param name="request">The request.</param>
-        /// <param name="includeInSearch">The include in search.</param>
-        /// <param name="getSimilarityScore">The get similarity score.</param>
-        /// <returns>ItemsResult.</returns>
-        internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+        internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
         {
         {
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null;
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null;
 
 
@@ -76,11 +62,13 @@ namespace MediaBrowser.Api
                 (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
                 libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
 
 
-            Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
+                Recursive = true
+            };
 
 
-            var inputItems = user == null
-                                 ? libraryManager.RootFolder.GetRecursiveChildren(filter)
-                                 : user.RootFolder.GetRecursiveChildren(user, filter);
+            var inputItems = libraryManager.GetItemList(query);
 
 
             var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore)
             var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore)
                 .ToList();
                 .ToList();

+ 2 - 0
MediaBrowser.Api/StartupWizardService.cs

@@ -68,6 +68,8 @@ namespace MediaBrowser.Api
             _config.Configuration.EnableLocalizedGuids = true;
             _config.Configuration.EnableLocalizedGuids = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
             _config.Configuration.EnableDateLastRefresh = true;
             _config.Configuration.EnableDateLastRefresh = true;
+            _config.Configuration.EnableStandaloneMusicKeys = true;
+            _config.Configuration.EnableCaseSensitiveItemIds = true;
             _config.SaveConfiguration();
             _config.SaveConfiguration();
         }
         }
 
 

+ 5 - 2
MediaBrowser.Api/Subtitles/SubtitleService.cs

@@ -164,6 +164,8 @@ namespace MediaBrowser.Api.Subtitles
             long positionTicks = 0;
             long positionTicks = 0;
             var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
             var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
 
 
+            var accessToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;
+
             while (positionTicks < runtime)
             while (positionTicks < runtime)
             {
             {
                 var remaining = runtime - positionTicks;
                 var remaining = runtime - positionTicks;
@@ -173,9 +175,10 @@ namespace MediaBrowser.Api.Subtitles
 
 
                 var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
                 var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
 
 
-                var url = string.Format("stream.srt?StartPositionTicks={0}&EndPositionTicks={1}",
+                var url = string.Format("stream.vtt?StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
                     positionTicks.ToString(CultureInfo.InvariantCulture),
                     positionTicks.ToString(CultureInfo.InvariantCulture),
-                    endPositionTicks.ToString(CultureInfo.InvariantCulture));
+                    endPositionTicks.ToString(CultureInfo.InvariantCulture),
+                    accessToken);
 
 
                 builder.AppendLine(url);
                 builder.AppendLine(url);
 
 

+ 6 - 6
MediaBrowser.Api/TvShowsService.cs

@@ -263,7 +263,7 @@ namespace MediaBrowser.Api
                 _userDataManager,
                 _userDataManager,
                 _dtoService,
                 _dtoService,
                 Logger,
                 Logger,
-                request, item => item is Series,
+                request, new[] { typeof(Series) },
                 SimilarItemsHelper.GetSimiliarityScore);
                 SimilarItemsHelper.GetSimiliarityScore);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
@@ -273,11 +273,11 @@ namespace MediaBrowser.Api
         {
         {
             var user = _userManager.GetUserById(request.UserId);
             var user = _userManager.GetUserById(request.UserId);
 
 
-            var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
+            var minPremiereDate = DateTime.Now.Date.ToUniversalTime();
 
 
             var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
             var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
 
 
-            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+            var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 IncludeItemTypes = new[] { typeof(Episode).Name },
                 SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
                 SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
@@ -286,15 +286,15 @@ namespace MediaBrowser.Api
                 StartIndex = request.StartIndex,
                 StartIndex = request.StartIndex,
                 Limit = request.Limit
                 Limit = request.Limit
 
 
-            }, parentIds);
+            }, parentIds).ToList();
 
 
             var options = GetDtoOptions(request);
             var options = GetDtoOptions(request);
 
 
-            var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray();
+            var returnItems = _dtoService.GetBaseItemDtos(itemsResult, options, user).ToArray();
 
 
             var result = new ItemsResult
             var result = new ItemsResult
             {
             {
-                TotalRecordCount = itemsResult.TotalRecordCount,
+                TotalRecordCount = itemsResult.Count,
                 Items = returnItems
                 Items = returnItems
             };
             };
 
 

+ 50 - 310
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -85,17 +85,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{ItemsResult}.</returns>
         /// <returns>Task{ItemsResult}.</returns>
         private async Task<ItemsResult> GetItems(GetItems request)
         private async Task<ItemsResult> GetItems(GetItems request)
         {
         {
-            var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
 
-            var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
+            var result = await GetItemsToSerialize(request, user).ConfigureAwait(false);
 
 
             var dtoOptions = GetDtoOptions(request);
             var dtoOptions = GetDtoOptions(request);
 
 
             return new ItemsResult
             return new ItemsResult
             {
             {
-                TotalRecordCount = result.Item1.TotalRecordCount,
-                Items = _dtoService.GetBaseItemDtos(result.Item1.Items, dtoOptions, user).ToArray()
+                TotalRecordCount = result.TotalRecordCount,
+                Items = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ToArray()
             };
             };
         }
         }
 
 
@@ -104,17 +103,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// </summary>
         /// <param name="request">The request.</param>
         /// <param name="request">The request.</param>
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
-        /// <param name="parentItem">The parent item.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        private async Task<Tuple<QueryResult<BaseItem>, bool>> GetItemsToSerialize(GetItems request, User user, BaseItem parentItem)
+        private async Task<QueryResult<BaseItem>> GetItemsToSerialize(GetItems request, User user)
         {
         {
             var item = string.IsNullOrEmpty(request.ParentId) ?
             var item = string.IsNullOrEmpty(request.ParentId) ?
                 user == null ? _libraryManager.RootFolder : user.RootFolder :
                 user == null ? _libraryManager.RootFolder : user.RootFolder :
-                parentItem;
+                _libraryManager.GetItemById(request.ParentId);
 
 
             if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
             {
             {
-                item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+                //item = user == null ? _libraryManager.RootFolder : user.RootFolder;
             }
             }
             else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
             else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -137,21 +135,21 @@ namespace MediaBrowser.Api.UserLibrary
                     result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
                     result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
                 }
                 }
 
 
-                return new Tuple<QueryResult<BaseItem>, bool>(result, true);
+                return result;
             }
             }
 
 
             if (request.Recursive)
             if (request.Recursive)
             {
             {
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
 
 
-                return new Tuple<QueryResult<BaseItem>, bool>(result, true);
+                return result;
             }
             }
 
 
             if (user == null)
             if (user == null)
             {
             {
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
 
 
-                return new Tuple<QueryResult<BaseItem>, bool>(result, true);
+                return result;
             }
             }
 
 
             var userRoot = item as UserRootFolder;
             var userRoot = item as UserRootFolder;
@@ -160,26 +158,24 @@ namespace MediaBrowser.Api.UserLibrary
             {
             {
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
                 var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
 
 
-                return new Tuple<QueryResult<BaseItem>, bool>(result, true);
+                return result;
             }
             }
 
 
             IEnumerable<BaseItem> items = ((Folder)item).GetChildren(user, true);
             IEnumerable<BaseItem> items = ((Folder)item).GetChildren(user, true);
 
 
             var itemsArray = items.ToArray();
             var itemsArray = items.ToArray();
 
 
-            return new Tuple<QueryResult<BaseItem>, bool>(new QueryResult<BaseItem>
+            return new QueryResult<BaseItem>
             {
             {
                 Items = itemsArray,
                 Items = itemsArray,
                 TotalRecordCount = itemsArray.Length
                 TotalRecordCount = itemsArray.Length
-
-            }, false);
+            };
         }
         }
 
 
         private InternalItemsQuery GetItemsQuery(GetItems request, User user)
         private InternalItemsQuery GetItemsQuery(GetItems request, User user)
         {
         {
-            var query = new InternalItemsQuery
+            var query = new InternalItemsQuery(user)
             {
             {
-                User = user,
                 IsPlayed = request.IsPlayed,
                 IsPlayed = request.IsPlayed,
                 MediaTypes = request.GetMediaTypes(),
                 MediaTypes = request.GetMediaTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
@@ -188,8 +184,6 @@ namespace MediaBrowser.Api.UserLibrary
                 SortBy = request.GetOrderBy(),
                 SortBy = request.GetOrderBy(),
                 SortOrder = request.SortOrder ?? SortOrder.Ascending,
                 SortOrder = request.SortOrder ?? SortOrder.Ascending,
 
 
-                Filter = i => ApplyAdditionalFilters(request, i, user, _libraryManager),
-
                 IsFavorite = request.IsFavorite,
                 IsFavorite = request.IsFavorite,
                 Limit = request.Limit,
                 Limit = request.Limit,
                 StartIndex = request.StartIndex,
                 StartIndex = request.StartIndex,
@@ -234,7 +228,11 @@ namespace MediaBrowser.Api.UserLibrary
                 MinPlayers = request.MinPlayers,
                 MinPlayers = request.MinPlayers,
                 MaxPlayers = request.MaxPlayers,
                 MaxPlayers = request.MaxPlayers,
                 MinCommunityRating = request.MinCommunityRating,
                 MinCommunityRating = request.MinCommunityRating,
-                MinCriticRating = request.MinCriticRating
+                MinCriticRating = request.MinCriticRating,
+                ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId),
+                ParentIndexNumber = request.ParentIndexNumber,
+                AiredDuringSeason = request.AiredDuringSeason,
+                AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater
             };
             };
 
 
             if (!string.IsNullOrWhiteSpace(request.Ids))
             if (!string.IsNullOrWhiteSpace(request.Ids))
@@ -278,331 +276,73 @@ namespace MediaBrowser.Api.UserLibrary
                 }
                 }
             }
             }
 
 
-            return query;
-        }
-
-        /// <summary>
-        /// Applies filtering
-        /// </summary>
-        /// <param name="items">The items.</param>
-        /// <param name="filter">The filter.</param>
-        /// <param name="user">The user.</param>
-        /// <param name="repository">The repository.</param>
-        /// <returns>IEnumerable{BaseItem}.</returns>
-        internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user, IUserDataManager repository)
-        {
-            // Avoid implicitly captured closure
-            var currentUser = user;
-
-            switch (filter)
-            {
-                case ItemFilter.IsFavoriteOrLikes:
-                    return items.Where(item =>
-                    {
-                        var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
-                        if (userdata == null)
-                        {
-                            return false;
-                        }
-
-                        var likes = userdata.Likes ?? false;
-                        var favorite = userdata.IsFavorite;
-
-                        return likes || favorite;
-                    });
-
-                case ItemFilter.Likes:
-                    return items.Where(item =>
-                    {
-                        var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
-                        return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
-                    });
-
-                case ItemFilter.Dislikes:
-                    return items.Where(item =>
-                    {
-                        var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
-                        return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
-                    });
-
-                case ItemFilter.IsFavorite:
-                    return items.Where(item =>
-                    {
-                        var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
-                        return userdata != null && userdata.IsFavorite;
-                    });
-
-                case ItemFilter.IsResumable:
-                    return items.Where(item =>
-                    {
-                        var userdata = repository.GetUserData(user.Id, item.GetUserDataKey());
-
-                        return userdata != null && userdata.PlaybackPositionTicks > 0;
-                    });
-
-                case ItemFilter.IsPlayed:
-                    return items.Where(item => item.IsPlayed(currentUser));
-
-                case ItemFilter.IsUnplayed:
-                    return items.Where(item => item.IsUnplayed(currentUser));
-
-                case ItemFilter.IsFolder:
-                    return items.Where(item => item.IsFolder);
-
-                case ItemFilter.IsNotFolder:
-                    return items.Where(item => !item.IsFolder);
-
-                case ItemFilter.IsRecentlyAdded:
-                    return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10);
-            }
-
-            return items;
-        }
-
-        private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, ILibraryManager libraryManager)
-        {
-            // Artists
-            if (!string.IsNullOrEmpty(request.ArtistIds))
-            {
-                var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
-
-                var audio = i as IHasArtist;
-
-                if (!(audio != null && artistIds.Any(id =>
-                {
-                    var artistItem = libraryManager.GetItemById(id);
-                    return artistItem != null && audio.HasAnyArtist(artistItem.Name);
-                })))
-                {
-                    return false;
-                }
-            }
-
-            // Artists
-            if (!string.IsNullOrEmpty(request.Artists))
-            {
-                var artists = request.Artists.Split('|');
-
-                var audio = i as IHasArtist;
-
-                if (!(audio != null && artists.Any(audio.HasAnyArtist)))
-                {
-                    return false;
-                }
-            }
-
-            // Albums
-            if (!string.IsNullOrEmpty(request.Albums))
+            if (!string.IsNullOrEmpty(request.MinPremiereDate))
             {
             {
-                var albums = request.Albums.Split('|');
-
-                var audio = i as Audio;
-
-                if (audio != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                var album = i as MusicAlbum;
-
-                if (album != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                var musicVideo = i as MusicVideo;
-
-                if (musicVideo != null)
-                {
-                    if (!albums.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-                }
-
-                return false;
+                query.MinPremiereDate = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
             }
             }
 
 
-            // Min official rating
-            if (!string.IsNullOrEmpty(request.MinOfficialRating))
+            if (!string.IsNullOrEmpty(request.MaxPremiereDate))
             {
             {
-                var level = _localization.GetRatingLevel(request.MinOfficialRating);
-
-                if (level.HasValue)
-                {
-                    var rating = i.CustomRating;
-
-                    if (string.IsNullOrEmpty(rating))
-                    {
-                        rating = i.OfficialRating;
-                    }
-
-                    if (!string.IsNullOrEmpty(rating))
-                    {
-                        var itemLevel = _localization.GetRatingLevel(rating);
-
-                        if (!(!itemLevel.HasValue || itemLevel.Value >= level.Value))
-                        {
-                            return false;
-                        }
-                    }
-                }
+                query.MaxPremiereDate = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
             }
             }
 
 
-            // Max official rating
-            if (!string.IsNullOrEmpty(request.MaxOfficialRating))
+            // Filter by Series Status
+            if (!string.IsNullOrEmpty(request.SeriesStatus))
             {
             {
-                var level = _localization.GetRatingLevel(request.MaxOfficialRating);
-
-                if (level.HasValue)
-                {
-                    var rating = i.CustomRating;
-
-                    if (string.IsNullOrEmpty(rating))
-                    {
-                        rating = i.OfficialRating;
-                    }
-
-                    if (!string.IsNullOrEmpty(rating))
-                    {
-                        var itemLevel = _localization.GetRatingLevel(rating);
-
-                        if (!(!itemLevel.HasValue || itemLevel.Value <= level.Value))
-                        {
-                            return false;
-                        }
-                    }
-                }
+                query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray();
             }
             }
 
 
-            // LocationTypes
-            if (!string.IsNullOrEmpty(request.LocationTypes))
+            // Filter by Series AirDays
+            if (!string.IsNullOrEmpty(request.AirDays))
             {
             {
-                var vals = request.LocationTypes.Split(',');
-                if (!vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
+                query.AirDays = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true)).ToArray();
             }
             }
 
 
             // ExcludeLocationTypes
             // ExcludeLocationTypes
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             {
             {
-                var vals = request.ExcludeLocationTypes.Split(',');
-                if (vals.Contains(i.LocationType.ToString(), StringComparer.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
-            }
-
-            if (!string.IsNullOrEmpty(request.AlbumArtistStartsWithOrGreater))
-            {
-                var ok = new[] { i }.OfType<IHasAlbumArtist>()
-                    .Any(p => string.Compare(request.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1);
-
-                if (!ok)
-                {
-                    return false;
-                }
+                query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
             }
             }
 
 
-            // Filter by Series Status
-            if (!string.IsNullOrEmpty(request.SeriesStatus))
+            if (!string.IsNullOrEmpty(request.LocationTypes))
             {
             {
-                var vals = request.SeriesStatus.Split(',');
-
-                var ok = new[] { i }.OfType<Series>().Any(p => p.Status.HasValue && vals.Contains(p.Status.Value.ToString(), StringComparer.OrdinalIgnoreCase));
-
-                if (!ok)
-                {
-                    return false;
-                }
+                query.LocationTypes = request.LocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
             }
             }
-
-            // Filter by Series AirDays
-            if (!string.IsNullOrEmpty(request.AirDays))
+            
+            // Min official rating
+            if (!string.IsNullOrEmpty(request.MinOfficialRating))
             {
             {
-                var days = request.AirDays.Split(',').Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d, true));
-
-                var ok = new[] { i }.OfType<Series>().Any(p => p.AirDays != null && days.Any(d => p.AirDays.Contains(d)));
-
-                if (!ok)
-                {
-                    return false;
-                }
+                query.MinParentalRating = _localization.GetRatingLevel(request.MinOfficialRating);
             }
             }
 
 
-            if (request.ParentIndexNumber.HasValue)
+            // Max official rating
+            if (!string.IsNullOrEmpty(request.MaxOfficialRating))
             {
             {
-                var filterValue = request.ParentIndexNumber.Value;
-
-                var episode = i as Episode;
-
-                if (episode != null)
-                {
-                    if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value != filterValue)
-                    {
-                        return false;
-                    }
-                }
-
-                var song = i as Audio;
-
-                if (song != null)
-                {
-                    if (song.ParentIndexNumber.HasValue && song.ParentIndexNumber.Value != filterValue)
-                    {
-                        return false;
-                    }
-                }
+                query.MaxParentalRating = _localization.GetRatingLevel(request.MinOfficialRating);
             }
             }
 
 
-            if (request.AiredDuringSeason.HasValue)
+            // Artists
+            if (!string.IsNullOrEmpty(request.ArtistIds))
             {
             {
-                var episode = i as Episode;
-
-                if (episode == null)
-                {
-                    return false;
-                }
+                var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
 
 
-                if (!Series.FilterEpisodesBySeason(new[] { episode }, request.AiredDuringSeason.Value, true).Any())
-                {
-                    return false;
-                }
+                var artistItems = artistIds.Select(_libraryManager.GetItemById).Where(i => i != null).ToList();
+                query.ArtistNames = artistItems.Select(i => i.Name).ToArray();
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.MinPremiereDate))
+            // Artists
+            if (!string.IsNullOrEmpty(request.Artists))
             {
             {
-                var date = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
-
-                if (!(i.PremiereDate.HasValue && i.PremiereDate.Value >= date))
-                {
-                    return false;
-                }
+                query.ArtistNames = request.Artists.Split('|');
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.MaxPremiereDate))
+            // Albums
+            if (!string.IsNullOrEmpty(request.Albums))
             {
             {
-                var date = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
-
-                if (!(i.PremiereDate.HasValue && i.PremiereDate.Value <= date))
-                {
-                    return false;
-                }
+                query.AlbumNames = request.Albums.Split('|');
             }
             }
 
 
-            return true;
+            return query;
         }
         }
     }
     }
 
 

+ 1 - 6
MediaBrowser.Api/UserLibrary/PlaystateService.cs

@@ -335,11 +335,6 @@ namespace MediaBrowser.Api.UserLibrary
 
 
         public void Post(ReportPlaybackProgress request)
         public void Post(ReportPlaybackProgress request)
         {
         {
-            if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
-            {
-                ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId);
-            }
-
             request.SessionId = GetSession().Result.Id;
             request.SessionId = GetSession().Result.Id;
 
 
             var task = _sessionManager.OnPlaybackProgress(request);
             var task = _sessionManager.OnPlaybackProgress(request);
@@ -349,7 +344,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
         public void Post(PingPlaybackSession request)
         public void Post(PingPlaybackSession request)
         {
         {
-            ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId);
+            ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId, null);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
MediaBrowser.Api/packages.config

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

+ 19 - 0
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -126,6 +126,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
             }
             }
         }
         }
 
 
+        private void AddIpv4Option(HttpWebRequest request, HttpRequestOptions options)
+        {
+            if (!options.PreferIpv4)
+            {
+                return;
+            }
+
+            request.ServicePoint.BindIPEndPointDelegate = (servicePount, remoteEndPoint, retryCount) =>
+            {
+                if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
+                {
+                    return new IPEndPoint(IPAddress.Any, 0);
+                }
+                throw new InvalidOperationException("no IPv4 address");
+            };
+        }
+
         private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
         private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
         {
         {
             var request = CreateWebRequest(options.Url);
             var request = CreateWebRequest(options.Url);
@@ -133,6 +150,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
 
             if (httpWebRequest != null)
             if (httpWebRequest != null)
             {
             {
+                AddIpv4Option(httpWebRequest, options);
+
                 AddRequestHeaders(httpWebRequest, options);
                 AddRequestHeaders(httpWebRequest, options);
 
 
                 httpWebRequest.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None;
                 httpWebRequest.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None;

+ 1 - 1
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -49,7 +49,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="MoreLinq">
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>

+ 11 - 0
MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs

@@ -170,6 +170,17 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             QueueScheduledTask<T>(new TaskExecutionOptions());
             QueueScheduledTask<T>(new TaskExecutionOptions());
         }
         }
 
 
+        public void QueueIfNotRunning<T>()
+            where T : IScheduledTask
+        {
+            var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
+
+            if (task.State != TaskState.Running)
+            {
+                QueueScheduledTask<T>(new TaskExecutionOptions());
+            }
+        }
+
         public void Execute<T>()
         public void Execute<T>()
             where T : IScheduledTask
             where T : IScheduledTask
         {
         {

+ 1 - 1
MediaBrowser.Common.Implementations/packages.config

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

+ 1 - 0
MediaBrowser.Common/Net/HttpRequestOptions.cs

@@ -96,6 +96,7 @@ namespace MediaBrowser.Common.Net
         public TimeSpan CacheLength { get; set; }
         public TimeSpan CacheLength { get; set; }
 
 
         public int TimeoutMs { get; set; }
         public int TimeoutMs { get; set; }
+        public bool PreferIpv4 { get; set; }
 
 
         private string GetHeaderValue(string name)
         private string GetHeaderValue(string name)
         {
         {

+ 3 - 0
MediaBrowser.Common/ScheduledTasks/ITaskManager.cs

@@ -50,6 +50,9 @@ namespace MediaBrowser.Common.ScheduledTasks
         void QueueScheduledTask<T>()
         void QueueScheduledTask<T>()
             where T : IScheduledTask;
             where T : IScheduledTask;
 
 
+        void QueueIfNotRunning<T>()
+            where T : IScheduledTask;
+        
         /// <summary>
         /// <summary>
         /// Queues the scheduled task.
         /// Queues the scheduled task.
         /// </summary>
         /// </summary>

+ 15 - 0
MediaBrowser.Controller/Channels/Channel.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Channels;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -30,6 +31,13 @@ namespace MediaBrowser.Controller.Channels
             return base.IsVisible(user);
             return base.IsVisible(user);
         }
         }
 
 
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get { return SourceType.Channel; }
+            set { }
+        }
+
         public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         public override async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
         {
             try
             try
@@ -75,5 +83,12 @@ namespace MediaBrowser.Controller.Channels
         {
         {
             return false;
             return false;
         }
         }
+
+        internal static bool IsChannelVisible(BaseItem channelItem, User user)
+        {
+            var channel = ChannelManager.GetChannel(channelItem.ChannelId);
+
+            return channel.IsVisible(user);
+        }
     }
     }
 }
 }

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

@@ -12,7 +12,7 @@ using System.Threading;
 
 
 namespace MediaBrowser.Controller.Channels
 namespace MediaBrowser.Controller.Channels
 {
 {
-    public class ChannelAudioItem : Audio, IChannelMediaItem
+    public class ChannelAudioItem : Audio
     {
     {
         public ChannelMediaContentType ContentType { get; set; }
         public ChannelMediaContentType ContentType { get; set; }
 
 

+ 1 - 1
MediaBrowser.Controller/Channels/ChannelFolderItem.cs

@@ -11,7 +11,7 @@ using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Controller.Channels
 namespace MediaBrowser.Controller.Channels
 {
 {
-    public class ChannelFolderItem : Folder, IChannelItem
+    public class ChannelFolderItem : Folder
     {
     {
         public ChannelFolderType ChannelFolderType { get; set; }
         public ChannelFolderType ChannelFolderType { get; set; }
 
 

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

@@ -13,7 +13,7 @@ using System.Threading;
 
 
 namespace MediaBrowser.Controller.Channels
 namespace MediaBrowser.Controller.Channels
 {
 {
-    public class ChannelVideoItem : Video, IChannelMediaItem, IHasLookupInfo<ChannelItemLookupInfo>
+    public class ChannelVideoItem : Video
     {
     {
         public ChannelMediaContentType ContentType { get; set; }
         public ChannelMediaContentType ContentType { get; set; }
 
 
@@ -103,20 +103,6 @@ namespace MediaBrowser.Controller.Channels
             return list;
             return list;
         }
         }
 
 
-        public ChannelItemLookupInfo GetLookupInfo()
-        {
-            var info = GetItemLookupInfo<ChannelItemLookupInfo>();
-
-            info.ContentType = ContentType;
-
-            if (ExtraType.HasValue)
-            {
-                info.ExtraType = ExtraType.Value;
-            }
-
-            return info;
-        }
-
         protected override string GetInternalMetadataPath(string basePath)
         protected override string GetInternalMetadataPath(string basePath)
         {
         {
             return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
             return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
@@ -132,7 +118,7 @@ namespace MediaBrowser.Controller.Channels
             return IsVisibleStandaloneInternal(user, false) && IsChannelVisible(this, user);
             return IsVisibleStandaloneInternal(user, false) && IsChannelVisible(this, user);
         }
         }
 
 
-        internal static bool IsChannelVisible(IChannelItem item, User user)
+        internal static bool IsChannelVisible(BaseItem item, User user)
         {
         {
             var channel = ChannelManager.GetChannel(item.ChannelId);
             var channel = ChannelManager.GetChannel(item.ChannelId);
 
 

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

@@ -116,7 +116,7 @@ namespace MediaBrowser.Controller.Channels
         /// <param name="includeCachedVersions">if set to <c>true</c> [include cached versions].</param>
         /// <param name="includeCachedVersions">if set to <c>true</c> [include cached versions].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
         /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
-        Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken);
+        Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(BaseItem item, bool includeCachedVersions, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Gets the channel folder.
         /// Gets the channel folder.
@@ -141,6 +141,6 @@ namespace MediaBrowser.Controller.Channels
         /// <param name="progress">The progress.</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        Task DownloadChannelItem(IChannelMediaItem item, string destinationPath, IProgress<double> progress, CancellationToken cancellationToken);
+        Task DownloadChannelItem(BaseItem item, string destinationPath, IProgress<double> progress, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 1 - 0
MediaBrowser.Controller/Dto/DtoOptions.cs

@@ -17,6 +17,7 @@ namespace MediaBrowser.Controller.Dto
         public List<ImageType> ImageTypes { get; set; }
         public List<ImageType> ImageTypes { get; set; }
         public int ImageTypeLimit { get; set; }
         public int ImageTypeLimit { get; set; }
         public bool EnableImages { get; set; }
         public bool EnableImages { get; set; }
+        public bool AddProgramRecordingInfo { get; set; }
         public string DeviceId { get; set; }
         public string DeviceId { get; set; }
 
 
         public DtoOptions()
         public DtoOptions()

+ 60 - 1
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -8,6 +8,8 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using System.Threading;
+using MediaBrowser.Controller.Channels;
 
 
 namespace MediaBrowser.Controller.Entities.Audio
 namespace MediaBrowser.Controller.Entities.Audio
 {
 {
@@ -24,6 +26,8 @@ namespace MediaBrowser.Controller.Entities.Audio
         IThemeMedia,
         IThemeMedia,
         IArchivable
         IArchivable
     {
     {
+        public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
+        
         public long? Size { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
         public int? TotalBitrate { get; set; }
@@ -153,6 +157,31 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         protected override string CreateUserDataKey()
         protected override string CreateUserDataKey()
         {
         {
+            if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
+            {
+                var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
+
+
+                if (ParentIndexNumber.HasValue)
+                {
+                    songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;
+                }
+                songKey+= Name;
+
+                if (!string.IsNullOrWhiteSpace(Album))
+                {
+                    songKey = Album + "-" + songKey;
+                }
+
+                var albumArtist = AlbumArtists.FirstOrDefault();
+                if (!string.IsNullOrWhiteSpace(albumArtist))
+                {
+                    songKey = albumArtist + "-" + songKey;
+                }
+
+                return songKey;
+            }
+
             var parent = AlbumEntity;
             var parent = AlbumEntity;
 
 
             if (parent != null)
             if (parent != null)
@@ -173,7 +202,11 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
         public override UnratedItem GetBlockUnratedType()
         public override UnratedItem GetBlockUnratedType()
         {
         {
-            return UnratedItem.Music;
+            if (SourceType == SourceType.Library)
+            {
+                return UnratedItem.Music;
+            }
+            return base.GetBlockUnratedType();
         }
         }
 
 
         public SongInfo GetLookupInfo()
         public SongInfo GetLookupInfo()
@@ -189,6 +222,32 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
         public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
+                           .Result.ToList();
+
+                if (sources.Count > 0)
+                {
+                    return sources;
+                }
+
+                var list = new List<MediaSourceInfo>
+                {
+                    GetVersionInfo(this, enablePathSubstitution)
+                };
+
+                foreach (var mediaSource in list)
+                {
+                    if (string.IsNullOrWhiteSpace(mediaSource.Path))
+                    {
+                        mediaSource.Type = MediaSourceType.Placeholder;
+                    }
+                }
+
+                return list;
+            }
+
             var result = new List<MediaSourceInfo>
             var result = new List<MediaSourceInfo>
             {
             {
                 GetVersionInfo(this, enablePathSubstitution)
                 GetVersionInfo(this, enablePathSubstitution)

+ 12 - 0
MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Entities.Audio
+{
+    public class AudioPodcast : Audio
+    {
+    }
+}

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

@@ -34,7 +34,17 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
         {
             get
             get
             {
             {
-                return GetParents().OfType<MusicArtist>().FirstOrDefault();
+                var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
+
+                if (artist == null)
+                {
+                    var name = AlbumArtist;
+                    if (!string.IsNullOrWhiteSpace(name))
+                    {
+                        artist = LibraryManager.GetArtist(name);
+                    }
+                }
+                return artist;
             }
             }
         }
         }
 
 
@@ -106,6 +116,15 @@ namespace MediaBrowser.Controller.Entities.Audio
                 return "MusicAlbum-Musicbrainz-" + id;
                 return "MusicAlbum-Musicbrainz-" + id;
             }
             }
 
 
+            if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
+            {
+                var albumArtist = AlbumArtist;
+                if (!string.IsNullOrWhiteSpace(albumArtist))
+                {
+                    return albumArtist + "-" + Name;
+                }
+            }
+
             return base.CreateUserDataKey();
             return base.CreateUserDataKey();
         }
         }
 
 
@@ -125,7 +144,7 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
             id.AlbumArtists = AlbumArtists;
             id.AlbumArtists = AlbumArtists;
 
 
-            var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
+            var artist = MusicArtist;
 
 
             if (artist != null)
             if (artist != null)
             {
             {

+ 149 - 8
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -20,9 +20,11 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Controller.Sorting;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
@@ -57,7 +59,9 @@ namespace MediaBrowser.Controller.Entities
         public static string ThemeSongFilename = "theme";
         public static string ThemeSongFilename = "theme";
         public static string ThemeVideosFolderName = "backdrops";
         public static string ThemeVideosFolderName = "backdrops";
 
 
+        [IgnoreDataMember]
         public string PreferredMetadataCountryCode { get; set; }
         public string PreferredMetadataCountryCode { get; set; }
+        [IgnoreDataMember]
         public string PreferredMetadataLanguage { get; set; }
         public string PreferredMetadataLanguage { get; set; }
 
 
         public List<ItemImageInfo> ImageInfos { get; set; }
         public List<ItemImageInfo> ImageInfos { get; set; }
@@ -88,6 +92,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets a value indicating whether this instance is in mixed folder.
         /// Gets a value indicating whether this instance is in mixed folder.
         /// </summary>
         /// </summary>
         /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
         public bool IsInMixedFolder { get; set; }
         public bool IsInMixedFolder { get; set; }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -166,6 +171,9 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsOffline { get; set; }
         public bool IsOffline { get; set; }
 
 
+        [IgnoreDataMember]
+        public virtual SourceType SourceType { get; set; }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself
@@ -184,6 +192,13 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Gets or sets the name of the service.
+        /// </summary>
+        /// <value>The name of the service.</value>
+        [IgnoreDataMember]
+        public string ServiceName { get; set; }
+
         /// <summary>
         /// <summary>
         /// If this content came from an external service, the id of the content on that service
         /// If this content came from an external service, the id of the content on that service
         /// </summary>
         /// </summary>
@@ -252,6 +267,11 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             get
             get
             {
             {
+                if (SourceType == SourceType.Channel)
+                {
+                    return false;
+                }
+
                 var locationType = LocationType;
                 var locationType = LocationType;
 
 
                 return locationType != LocationType.Remote && locationType != LocationType.Virtual;
                 return locationType != LocationType.Remote && locationType != LocationType.Virtual;
@@ -281,6 +301,40 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        private List<Tuple<StringBuilder,bool>> GetSortChunks(string s1)
+        {
+            var list = new List<Tuple<StringBuilder, bool>>();
+
+            int thisMarker = 0, thisNumericChunk = 0;
+
+            while ((thisMarker < s1.Length))
+            {
+                if (thisMarker >= s1.Length)
+                {
+                    break;
+                }
+                char thisCh = s1[thisMarker];
+
+                StringBuilder thisChunk = new StringBuilder();
+
+                while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0])))
+                {
+                    thisChunk.Append(thisCh);
+                    thisMarker++;
+
+                    if (thisMarker < s1.Length)
+                    {
+                        thisCh = s1[thisMarker];
+                    }
+                }
+
+                var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]);
+                list.Add(new Tuple<StringBuilder, bool>(thisChunk, isNumeric));
+            }
+
+            return list;
+        }
+
         /// <summary>
         /// <summary>
         /// This is just a helper for convenience
         /// This is just a helper for convenience
         /// </summary>
         /// </summary>
@@ -298,6 +352,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual bool CanDelete()
         public virtual bool CanDelete()
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                return false;
+            }
+
             var locationType = LocationType;
             var locationType = LocationType;
             return locationType != LocationType.Remote &&
             return locationType != LocationType.Remote &&
                    locationType != LocationType.Virtual;
                    locationType != LocationType.Virtual;
@@ -342,6 +401,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         [IgnoreDataMember]
         public DateTime DateModified { get; set; }
         public DateTime DateModified { get; set; }
 
 
+        [IgnoreDataMember]
         public DateTime DateLastSaved { get; set; }
         public DateTime DateLastSaved { get; set; }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -380,6 +440,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the locked fields.
         /// Gets or sets the locked fields.
         /// </summary>
         /// </summary>
         /// <value>The locked fields.</value>
         /// <value>The locked fields.</value>
+        [IgnoreDataMember]
         public List<MetadataFields> LockedFields { get; set; }
         public List<MetadataFields> LockedFields { get; set; }
 
 
         /// <summary>
         /// <summary>
@@ -433,11 +494,6 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             get
             get
             {
             {
-                if (!string.IsNullOrWhiteSpace(ForcedSortName))
-                {
-                    return ForcedSortName;
-                }
-
                 return _sortName ?? (_sortName = CreateSortName());
                 return _sortName ?? (_sortName = CreateSortName());
             }
             }
             set
             set
@@ -455,6 +511,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         protected virtual string GetInternalMetadataPath(string basePath)
         protected virtual string GetInternalMetadataPath(string basePath)
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
+            }
+
             var idString = Id.ToString("N");
             var idString = Id.ToString("N");
 
 
             basePath = System.IO.Path.Combine(basePath, "library");
             basePath = System.IO.Path.Combine(basePath, "library");
@@ -468,6 +529,11 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         protected virtual string CreateSortName()
         protected virtual string CreateSortName()
         {
         {
+            if (!string.IsNullOrWhiteSpace(ForcedSortName))
+            {
+                return ModifySortChunks(ForcedSortName).ToLower();
+            }
+
             if (Name == null) return null; //some items may not have name filled in properly
             if (Name == null) return null; //some items may not have name filled in properly
 
 
             if (!EnableAlphaNumericSorting)
             if (!EnableAlphaNumericSorting)
@@ -497,7 +563,32 @@ namespace MediaBrowser.Controller.Entities
                     sortable = sortable.Remove(sortable.Length - (searchLower.Length + 1));
                     sortable = sortable.Remove(sortable.Length - (searchLower.Length + 1));
                 }
                 }
             }
             }
-            return sortable;
+            return ModifySortChunks(sortable);
+        }
+
+        private string ModifySortChunks(string name)
+        {
+            var chunks = GetSortChunks(name);
+
+            var builder = new StringBuilder();
+
+            foreach (var chunk in chunks)
+            {
+                var chunkBuilder = chunk.Item1;
+
+                // This chunk is numeric
+                if (chunk.Item2)
+                {
+                    while (chunkBuilder.Length < 10)
+                    {
+                        chunkBuilder.Insert(0, '0');
+                    }
+                }
+
+                builder.Append(chunkBuilder);
+            }
+            //Logger.Debug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
+            return builder.ToString();
         }
         }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -595,6 +686,18 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         [IgnoreDataMember]
         public string OfficialRating { get; set; }
         public string OfficialRating { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets the critic rating.
+        /// </summary>
+        /// <value>The critic rating.</value>
+        public float? CriticRating { get; set; }
+
+        /// <summary>
+        /// Gets or sets the critic rating summary.
+        /// </summary>
+        /// <value>The critic rating summary.</value>
+        public string CriticRatingSummary { get; set; }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the official rating description.
         /// Gets or sets the official rating description.
         /// </summary>
         /// </summary>
@@ -620,6 +723,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the studios.
         /// Gets or sets the studios.
         /// </summary>
         /// </summary>
         /// <value>The studios.</value>
         /// <value>The studios.</value>
+        [IgnoreDataMember]
         public List<string> Studios { get; set; }
         public List<string> Studios { get; set; }
 
 
         /// <summary>
         /// <summary>
@@ -633,6 +737,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the tags.
         /// Gets or sets the tags.
         /// </summary>
         /// </summary>
         /// <value>The tags.</value>
         /// <value>The tags.</value>
+        [IgnoreDataMember]
         public List<string> Tags { get; set; }
         public List<string> Tags { get; set; }
 
 
         /// <summary>
         /// <summary>
@@ -1025,6 +1130,13 @@ namespace MediaBrowser.Controller.Entities
 
 
         protected virtual string CreateUserDataKey()
         protected virtual string CreateUserDataKey()
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                if (!string.IsNullOrWhiteSpace(ExternalId))
+                {
+                    return ExternalId;
+                }
+            }
             return Id.ToString();
             return Id.ToString();
         }
         }
 
 
@@ -1103,6 +1215,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual bool IsSaveLocalMetadataEnabled()
         public virtual bool IsSaveLocalMetadataEnabled()
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                return false;
+            }
+
             return ConfigurationManager.Configuration.SaveLocalMeta;
             return ConfigurationManager.Configuration.SaveLocalMeta;
         }
         }
 
 
@@ -1218,6 +1335,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual UnratedItem GetBlockUnratedType()
         public virtual UnratedItem GetBlockUnratedType()
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                return UnratedItem.ChannelContent;
+            }
+
             return UnratedItem.Other;
             return UnratedItem.Other;
         }
         }
 
 
@@ -1261,6 +1383,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual bool IsVisibleStandalone(User user)
         public virtual bool IsVisibleStandalone(User user)
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                return IsVisibleStandaloneInternal(user, false) && Channel.IsChannelVisible(this, user);
+            }
+
             return IsVisibleStandaloneInternal(user, true);
             return IsVisibleStandaloneInternal(user, true);
         }
         }
 
 
@@ -1312,6 +1439,11 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual string GetClientTypeName()
         public virtual string GetClientTypeName()
         {
         {
+            if (IsFolder && SourceType == SourceType.Channel)
+            {
+                return "ChannelFolderItem";
+            }
+
             return GetType().Name;
             return GetType().Name;
         }
         }
 
 
@@ -1835,8 +1967,8 @@ namespace MediaBrowser.Controller.Entities
                 ProviderIds = ProviderIds,
                 ProviderIds = ProviderIds,
                 IndexNumber = IndexNumber,
                 IndexNumber = IndexNumber,
                 ParentIndexNumber = ParentIndexNumber,
                 ParentIndexNumber = ParentIndexNumber,
-				Year = ProductionYear,
-				PremiereDate = PremiereDate
+                Year = ProductionYear,
+                PremiereDate = PremiereDate
             };
             };
         }
         }
 
 
@@ -1985,5 +2117,14 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             return LibraryManager.DeleteItem(this, options);
             return LibraryManager.DeleteItem(this, options);
         }
         }
+
+        public virtual Task OnFileDeleted()
+        {
+            // Remove from database
+            return Delete(new DeleteOptions
+            {
+                DeleteFileLocation = false
+            });
+        }
     }
     }
 }
 }

+ 2 - 0
MediaBrowser.Controller/Entities/Book.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Runtime.Serialization;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Users;
 using MediaBrowser.Model.Users;
 
 
@@ -9,6 +10,7 @@ namespace MediaBrowser.Controller.Entities
 {
 {
     public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries
     public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries
     {
     {
+        [IgnoreDataMember]
         public override string MediaType
         public override string MediaType
         {
         {
             get
             get

+ 470 - 106
MediaBrowser.Controller/Entities/Folder.cs

@@ -15,6 +15,9 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.Channels;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
@@ -145,60 +148,38 @@ namespace MediaBrowser.Controller.Entities
                 item.DateModified = DateTime.UtcNow;
                 item.DateModified = DateTime.UtcNow;
             }
             }
 
 
-            AddChildInternal(item);
+            AddChildInternal(item.Id);
 
 
             await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
             await LibraryManager.CreateItem(item, 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)
+        protected void AddChildrenInternal(List<Guid> children)
         {
         {
-            var actualChildren = ActualChildren;
-
             lock (_childrenSyncLock)
             lock (_childrenSyncLock)
             {
             {
-                var newChildren = actualChildren.ToList();
+                var newChildren = ChildIds.ToList();
                 newChildren.AddRange(children);
                 newChildren.AddRange(children);
-                _children = newChildren;
+                _children = newChildren.ToList();
             }
             }
         }
         }
-        protected void AddChildInternal(BaseItem child)
+        protected void AddChildInternal(Guid child)
         {
         {
-            var actualChildren = ActualChildren;
-
             lock (_childrenSyncLock)
             lock (_childrenSyncLock)
             {
             {
-                var newChildren = actualChildren.ToList();
-                newChildren.Add(child);
-                _children = newChildren;
-            }
-        }
-
-        protected void RemoveChildrenInternal(IEnumerable<BaseItem> children)
-        {
-            var ids = children.Select(i => i.Id).ToList();
-            var actualChildren = ActualChildren;
-
-            lock (_childrenSyncLock)
-            {
-                _children = actualChildren.Where(i => !ids.Contains(i.Id)).ToList();
+                var childIds = ChildIds.ToList();
+                if (!childIds.Contains(child))
+                {
+                    childIds.Add(child);
+                    _children = childIds.ToList();
+                }
             }
             }
         }
         }
 
 
-        protected void ClearChildrenInternal()
+        protected void RemoveChildrenInternal(List<Guid> children)
         {
         {
             lock (_childrenSyncLock)
             lock (_childrenSyncLock)
             {
             {
-                _children = new List<BaseItem>();
+                _children = ChildIds.Except(children).ToList();
             }
             }
         }
         }
 
 
@@ -206,40 +187,11 @@ namespace MediaBrowser.Controller.Entities
         /// Removes the child.
         /// Removes the child.
         /// </summary>
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="System.InvalidOperationException">Unable to remove  + item.Name</exception>
-        public Task RemoveChild(BaseItem item, CancellationToken cancellationToken)
+        public void RemoveChild(BaseItem item)
         {
         {
-            RemoveChildrenInternal(new[] { item });
+            RemoveChildrenInternal(new[] { item.Id }.ToList());
 
 
             item.SetParent(null);
             item.SetParent(null);
-
-            if (!EnableNewFolderQuerying())
-            {
-                return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
-            }
-
-            return Task.FromResult(true);
-        }
-
-        /// <summary>
-        /// Clears the children.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public Task ClearChildren(CancellationToken cancellationToken)
-        {
-            var items = ActualChildren.ToList();
-
-            ClearChildrenInternal();
-
-            foreach (var item in items)
-            {
-                LibraryManager.ReportItemRemoved(item);
-            }
-
-            return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
         }
         }
 
 
         #region Indexing
         #region Indexing
@@ -276,7 +228,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// <summary>
         /// The children
         /// The children
         /// </summary>
         /// </summary>
-        private IReadOnlyList<BaseItem> _children;
+        private IReadOnlyList<Guid> _children;
         /// <summary>
         /// <summary>
         /// The _children sync lock
         /// The _children sync lock
         /// </summary>
         /// </summary>
@@ -285,21 +237,30 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the actual children.
         /// Gets or sets the actual children.
         /// </summary>
         /// </summary>
         /// <value>The actual children.</value>
         /// <value>The actual children.</value>
-        protected virtual IEnumerable<BaseItem> ActualChildren
+        protected virtual IEnumerable<Guid> ChildIds
         {
         {
             get
             get
             {
             {
-                if (_children == null)
+                lock (_childrenSyncLock)
                 {
                 {
-                    lock (_childrenSyncLock)
+                    if (_children == null)
                     {
                     {
-                        if (_children == null)
-                        {
-                            _children = LoadChildren().ToList();
-                        }
+                        _children = LoadChildren().ToList();
                     }
                     }
+                    return _children.ToList();
                 }
                 }
-                return _children;
+            }
+        }
+
+        /// <summary>
+        /// Gets the actual children.
+        /// </summary>
+        /// <value>The actual children.</value>
+        protected virtual IEnumerable<BaseItem> ActualChildren
+        {
+            get
+            {
+                return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null);
             }
             }
         }
         }
 
 
@@ -353,7 +314,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads our children.  Validation will occur externally.
         /// Loads our children.  Validation will occur externally.
         /// We want this sychronous.
         /// We want this sychronous.
         /// </summary>
         /// </summary>
-        protected virtual IEnumerable<BaseItem> LoadChildren()
+        protected virtual IEnumerable<Guid> LoadChildren()
         {
         {
             //just load our children from the repo - the library will be validated and maintained in other processes
             //just load our children from the repo - the library will be validated and maintained in other processes
             return GetCachedChildren();
             return GetCachedChildren();
@@ -503,7 +464,7 @@ namespace MediaBrowser.Controller.Entities
 
 
                     if (actualRemovals.Count > 0)
                     if (actualRemovals.Count > 0)
                     {
                     {
-                        RemoveChildrenInternal(actualRemovals);
+                        RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList());
 
 
                         foreach (var item in actualRemovals)
                         foreach (var item in actualRemovals)
                         {
                         {
@@ -518,12 +479,7 @@ namespace MediaBrowser.Controller.Entities
 
 
                     await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
                     await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
 
 
-                    AddChildrenInternal(newItems);
-
-                    if (!EnableNewFolderQuerying())
-                    {
-                        await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
-                    }
+                    AddChildrenInternal(newItems.Select(i => i.Id).ToList());
                 }
                 }
             }
             }
 
 
@@ -751,51 +707,459 @@ namespace MediaBrowser.Controller.Entities
         /// Get our children from the repo - stubbed for now
         /// Get our children from the repo - stubbed for now
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected IEnumerable<BaseItem> GetCachedChildren()
+        protected IEnumerable<Guid> GetCachedChildren()
         {
         {
-            if (EnableNewFolderQuerying())
+            return ItemRepository.GetItemIdsList(new InternalItemsQuery
             {
             {
-                return ItemRepository.GetItemList(new InternalItemsQuery
+                ParentId = Id
+
+            });
+        }
+
+        public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query)
+        {
+            var user = query.User;
+
+            if (RequiresPostFiltering(query))
+            {
+                IEnumerable<BaseItem> items;
+                Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+
+                if (query.User == null)
                 {
                 {
-                    ParentId = Id
+                    items = GetRecursiveChildren(filter);
+                }
+                else
+                {
+                    items = GetRecursiveChildren(user, filter);
+                }
+
+                return PostFilterAndSort(items, query);
+            }
 
 
-                }).Select(RetrieveChild).Where(i => i != null);
+            if (!(this is UserRootFolder) && !(this is AggregateFolder))
+            {
+                query.ParentId = query.ParentId ?? Id;
             }
             }
 
 
-            return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
+            return LibraryManager.GetItemsResult(query);
         }
         }
 
 
-        private BaseItem RetrieveChild(BaseItem child)
+        private bool RequiresPostFiltering(InternalItemsQuery query)
         {
         {
-            if (child == null || child.Id == Guid.Empty)
+            if (LinkedChildren.Count > 0)
             {
             {
-                Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name));
-                return null;
+                if (!(this is ICollectionFolder))
+                {
+                    Logger.Debug("Query requires post-filtering due to LinkedChildren");
+                    return true;
+                }
             }
             }
 
 
-            var item = LibraryManager.GetMemoryItemById(child.Id);
-
-            if (item != null)
+            if (query.SortBy != null && query.SortBy.Length > 0)
             {
             {
-                if (item is IByReferenceItem)
+                if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.DatePlayed");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
                 {
                 {
-                    return LibraryManager.GetOrAddByReferenceItem(item);
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked");
+                    return true;
                 }
                 }
+                if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.AiredEpisodeOrder, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Album");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Artist, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Artist");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Budget, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Metascore, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Metascore");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.OfficialRating, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.OfficialRating");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Players, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Players");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Revenue, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Revenue");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.SeriesSortName, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.StartDate, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.StartDate");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio");
+                    return true;
+                }
+                if (query.SortBy.Contains(ItemSortBy.VideoBitRate, StringComparer.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Query requires post-filtering due to ItemSortBy.VideoBitRate");
+                    return true;
+                }
+            }
 
 
-                item.SetParent(this);
+            if (query.ItemIds.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to ItemIds");
+                return true;
             }
             }
-            else
+
+            if (query.PersonIds.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to PersonIds");
+                return true;
+            }
+
+            if (query.IsLiked.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsLiked");
+                return true;
+            }
+
+            if (query.IsFavoriteOrLiked.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked");
+                return true;
+            }
+
+            if (query.IsFavorite.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsFavorite");
+                return true;
+            }
+
+            if (query.IsResumable.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsResumable");
+                return true;
+            }
+
+            if (query.IsPlayed.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsPlayed");
+                return true;
+            }
+
+            if (query.IsInBoxSet.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsInBoxSet");
+                return true;
+            }
+
+            // Filter by Video3DFormat
+            if (query.Is3D.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to Is3D");
+                return true;
+            }
+
+            if (query.HasImdbId.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasImdbId");
+                return true;
+            }
+
+            if (query.HasTmdbId.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasTmdbId");
+                return true;
+            }
+
+            if (query.HasTvdbId.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasTvdbId");
+                return true;
+            }
+
+            if (query.IsYearMismatched.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsYearMismatched");
+                return true;
+            }
+
+            if (query.HasOfficialRating.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasOfficialRating");
+                return true;
+            }
+
+            if (query.IsPlaceHolder.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsPlaceHolder");
+                return true;
+            }
+
+            if (query.HasSpecialFeature.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasSpecialFeature");
+                return true;
+            }
+
+            if (query.HasSubtitles.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasSubtitles");
+                return true;
+            }
+
+            if (query.HasTrailer.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasTrailer");
+                return true;
+            }
+
+            if (query.HasThemeSong.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasThemeSong");
+                return true;
+            }
+
+            if (query.HasThemeVideo.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to HasThemeVideo");
+                return true;
+            }
+
+            // Filter by VideoType
+            if (query.VideoTypes.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to VideoTypes");
+                return true;
+            }
+
+            if (query.ImageTypes.Length > 0)
             {
             {
-                child.SetParent(this);
-                LibraryManager.RegisterItem(child);
-                item = child;
+                Logger.Debug("Query requires post-filtering due to ImageTypes");
+                return true;
+            }
+
+            // Apply studio filter
+            if (query.StudioIds.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to StudioIds");
+                return true;
+            }
+
+            // Apply genre filter
+            if (query.GenreIds.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to GenreIds");
+                return true;
             }
             }
 
 
-            return item;
+            // Apply person filter
+            if (query.ItemIdsFromPersonFilters != null)
+            {
+                Logger.Debug("Query requires post-filtering due to ItemIdsFromPersonFilters");
+                return true;
+            }
+
+            if (query.MinPlayers.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to MinPlayers");
+                return true;
+            }
+
+            if (query.MaxPlayers.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to MaxPlayers");
+                return true;
+            }
+
+            if (query.OfficialRatings.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to OfficialRatings");
+                return true;
+            }
+
+            if (query.IsMissing.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsMissing");
+                return true;
+            }
+
+            if (query.IsUnaired.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsUnaired");
+                return true;
+            }
+
+            if (query.IsVirtualUnaired.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to IsVirtualUnaired");
+                return true;
+            }
+
+            if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User))
+            {
+                Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.AdjacentTo))
+            {
+                Logger.Debug("Query requires post-filtering due to AdjacentTo");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.NameContains))
+            {
+                Logger.Debug("Query requires post-filtering due to NameContains");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.NameLessThan))
+            {
+                Logger.Debug("Query requires post-filtering due to NameLessThan");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
+            {
+                Logger.Debug("Query requires post-filtering due to NameStartsWith");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
+            {
+                Logger.Debug("Query requires post-filtering due to NameStartsWithOrGreater");
+                return true;
+            }
+
+            if (query.AirDays.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to AirDays");
+                return true;
+            }
+
+            if (query.SeriesStatuses.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to SeriesStatuses");
+                return true;
+            }
+
+            if (query.AiredDuringSeason.HasValue)
+            {
+                Logger.Debug("Query requires post-filtering due to AiredDuringSeason");
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(query.AlbumArtistStartsWithOrGreater))
+            {
+                Logger.Debug("Query requires post-filtering due to AlbumArtistStartsWithOrGreater");
+                return true;
+            }
+
+            if (query.AlbumNames.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to AlbumNames");
+                return true;
+            }
+
+            if (query.ArtistNames.Length > 0)
+            {
+                Logger.Debug("Query requires post-filtering due to ArtistNames");
+                return true;
+            }
+
+            return false;
         }
         }
 
 
-        public virtual Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
+        public virtual async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                try
+                {
+                    // Don't blow up here because it could cause parent screens with other content to fail
+                    return await ChannelManager.GetChannelItemsInternal(new ChannelItemQuery
+                    {
+                        ChannelId = ChannelId,
+                        FolderId = Id.ToString("N"),
+                        Limit = query.Limit,
+                        StartIndex = query.StartIndex,
+                        UserId = query.User.Id.ToString("N"),
+                        SortBy = query.SortBy,
+                        SortOrder = query.SortOrder
+
+                    }, new Progress<double>(), CancellationToken.None);
+                }
+                catch
+                {
+                    // Already logged at lower levels
+                    return new QueryResult<BaseItem>
+                    {
+
+                    };
+                }
+            }
+
+            if (query.Recursive)
+            {
+                return QueryRecursive(query);
+            }
+
             var user = query.User;
             var user = query.User;
 
 
             Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
             Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
@@ -817,7 +1181,7 @@ namespace MediaBrowser.Controller.Entities
 
 
             var result = PostFilterAndSort(items, query);
             var result = PostFilterAndSort(items, query);
 
 
-            return Task.FromResult(result);
+            return result;
         }
         }
 
 
         protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
         protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)

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

@@ -83,6 +83,7 @@ namespace MediaBrowser.Controller.Entities
         public string[] OfficialRatings { get; set; }
         public string[] OfficialRatings { get; set; }
 
 
         public DateTime? MinPremiereDate { get; set; }
         public DateTime? MinPremiereDate { get; set; }
+        public DateTime? MaxPremiereDate { get; set; }
         public DateTime? MinStartDate { get; set; }
         public DateTime? MinStartDate { get; set; }
         public DateTime? MaxStartDate { get; set; }
         public DateTime? MaxStartDate { get; set; }
         public DateTime? MinEndDate { get; set; }
         public DateTime? MinEndDate { get; set; }
@@ -96,28 +97,45 @@ namespace MediaBrowser.Controller.Entities
         public int? MinPlayers { get; set; }
         public int? MinPlayers { get; set; }
         public int? MaxPlayers { get; set; }
         public int? MaxPlayers { get; set; }
         public int? MinIndexNumber { get; set; }
         public int? MinIndexNumber { get; set; }
+        public int? AiredDuringSeason { get; set; }
         public double? MinCriticRating { get; set; }
         public double? MinCriticRating { get; set; }
         public double? MinCommunityRating { get; set; }
         public double? MinCommunityRating { get; set; }
 
 
         public string[] ChannelIds { get; set; }
         public string[] ChannelIds { get; set; }
 
 
         internal List<Guid> ItemIdsFromPersonFilters { get; set; }
         internal List<Guid> ItemIdsFromPersonFilters { get; set; }
+        public int? ParentIndexNumber { get; set; }
+        public int? MinParentalRating { get; set; }
         public int? MaxParentalRating { get; set; }
         public int? MaxParentalRating { get; set; }
 
 
         public bool? IsCurrentSchema { get; set; }
         public bool? IsCurrentSchema { get; set; }
         public bool? HasDeadParentId { get; set; }
         public bool? HasDeadParentId { get; set; }
         public bool? IsOffline { get; set; }
         public bool? IsOffline { get; set; }
-        public LocationType? LocationType { get; set; }
 
 
         public Guid? ParentId { get; set; }
         public Guid? ParentId { get; set; }
         public string[] AncestorIds { get; set; }
         public string[] AncestorIds { get; set; }
         public string[] TopParentIds { get; set; }
         public string[] TopParentIds { get; set; }
 
 
+        public LocationType[] LocationTypes { get; set; }
         public LocationType[] ExcludeLocationTypes { get; set; }
         public LocationType[] ExcludeLocationTypes { get; set; }
         public string[] PresetViews { get; set; }
         public string[] PresetViews { get; set; }
+        public SourceType[] SourceTypes { get; set; }
+        public SourceType[] ExcludeSourceTypes { get; set; }
+        public TrailerType[] TrailerTypes { get; set; }
+        public TrailerType[] ExcludeTrailerTypes { get; set; }
 
 
+        public DayOfWeek[] AirDays { get; set; }
+        public SeriesStatus[] SeriesStatuses { get; set; }
+        public string AlbumArtistStartsWithOrGreater { get; set; }
+
+        public string[] AlbumNames { get; set; }
+        public string[] ArtistNames { get; set; }
+        
         public InternalItemsQuery()
         public InternalItemsQuery()
         {
         {
+            AlbumNames = new string[] { };
+            ArtistNames = new string[] { };
+            
             BlockUnratedItems = new UnratedItem[] { };
             BlockUnratedItems = new UnratedItem[] { };
             Tags = new string[] { };
             Tags = new string[] { };
             OfficialRatings = new string[] { };
             OfficialRatings = new string[] { };
@@ -139,8 +157,15 @@ namespace MediaBrowser.Controller.Entities
             AncestorIds = new string[] { };
             AncestorIds = new string[] { };
             TopParentIds = new string[] { };
             TopParentIds = new string[] { };
             ExcludeTags = new string[] { };
             ExcludeTags = new string[] { };
+            LocationTypes = new LocationType[] { };
             ExcludeLocationTypes = new LocationType[] { };
             ExcludeLocationTypes = new LocationType[] { };
             PresetViews = new string[] { };
             PresetViews = new string[] { };
+            SourceTypes = new SourceType[] { };
+            ExcludeSourceTypes = new SourceType[] { };
+            TrailerTypes = new TrailerType[] { };
+            ExcludeTrailerTypes = new TrailerType[] { };
+            AirDays = new DayOfWeek[] { };
+            SeriesStatuses = new SeriesStatus[] { };
         }
         }
 
 
         public InternalItemsQuery(User user)
         public InternalItemsQuery(User user)

+ 8 - 12
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
@@ -67,24 +68,19 @@ namespace MediaBrowser.Controller.Entities.Movies
         /// <value>The revenue.</value>
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
         public double? Revenue { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the critic rating.
-        /// </summary>
-        /// <value>The critic rating.</value>
-        public float? CriticRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the critic rating summary.
-        /// </summary>
-        /// <value>The critic rating summary.</value>
-        public string CriticRatingSummary { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the name of the TMDB collection.
         /// Gets or sets the name of the TMDB collection.
         /// </summary>
         /// </summary>
         /// <value>The name of the TMDB collection.</value>
         /// <value>The name of the TMDB collection.</value>
         public string TmdbCollectionName { get; set; }
         public string TmdbCollectionName { get; set; }
 
 
+        [IgnoreDataMember]
+        public string CollectionName
+        {
+            get { return TmdbCollectionName; }
+            set { TmdbCollectionName = value; }
+        }
+        
         /// <summary>
         /// <summary>
         /// Gets the trailer ids.
         /// Gets the trailer ids.
         /// </summary>
         /// </summary>

+ 10 - 0
MediaBrowser.Controller/Entities/SourceType.cs

@@ -0,0 +1,10 @@
+
+namespace MediaBrowser.Controller.Entities
+{
+    public enum SourceType
+    {
+        Library = 0,
+        Channel = 1,
+        LiveTV = 2
+    }
+}

+ 51 - 28
MediaBrowser.Controller/Entities/Trailer.cs

@@ -14,7 +14,6 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// <summary>
     /// Class Trailer
     /// Class Trailer
     /// </summary>
     /// </summary>
-    [Obsolete]
     public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
     public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
     {
     {
         public List<string> ProductionLocations { get; set; }
         public List<string> ProductionLocations { get; set; }
@@ -25,14 +24,23 @@ namespace MediaBrowser.Controller.Entities
             Taglines = new List<string>();
             Taglines = new List<string>();
             Keywords = new List<string>();
             Keywords = new List<string>();
             ProductionLocations = new List<string>();
             ProductionLocations = new List<string>();
+            TrailerTypes = new List<TrailerType>();
         }
         }
 
 
+        public List<TrailerType> TrailerTypes { get; set; }
+        
         public float? Metascore { get; set; }
         public float? Metascore { get; set; }
 
 
         public List<MediaUrl> RemoteTrailers { get; set; }
         public List<MediaUrl> RemoteTrailers { get; set; }
 
 
         public List<string> Keywords { get; set; }
         public List<string> Keywords { get; set; }
 
 
+        [IgnoreDataMember]
+        public bool IsLocalTrailer
+        {
+            get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the taglines.
         /// Gets or sets the taglines.
         /// </summary>
         /// </summary>
@@ -51,32 +59,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The revenue.</value>
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
         public double? Revenue { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the critic rating.
-        /// </summary>
-        /// <value>The critic rating.</value>
-        public float? CriticRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the critic rating summary.
-        /// </summary>
-        /// <value>The critic rating summary.</value>
-        public string CriticRatingSummary { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether this instance is local trailer.
-        /// </summary>
-        /// <value><c>true</c> if this instance is local trailer; otherwise, <c>false</c>.</value>
-        [IgnoreDataMember]
-        public bool IsLocalTrailer
-        {
-            get
-            {
-                // Local trailers are not part of children
-                return GetParent() == null;
-            }
-        }
-
         protected override string CreateUserDataKey()
         protected override string CreateUserDataKey()
         {
         {
             var key = Movie.GetMovieUserDataKey(this);
             var key = Movie.GetMovieUserDataKey(this);
@@ -106,9 +88,50 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var info = GetItemLookupInfo<TrailerInfo>();
             var info = GetItemLookupInfo<TrailerInfo>();
 
 
-            info.IsLocalTrailer = IsLocalTrailer;
+            info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer);
+            
+            if (!IsInMixedFolder)
+            {
+                info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
+            }
 
 
             return info;
             return info;
         }
         }
+
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            if (!ProductionYear.HasValue)
+            {
+                var info = LibraryManager.ParseName(Name);
+
+                var yearInName = info.Year;
+
+                if (yearInName.HasValue)
+                {
+                    ProductionYear = yearInName;
+                    hasChanges = true;
+                }
+                else
+                {
+                    // Try to get the year from the folder name
+                    if (!IsInMixedFolder)
+                    {
+                        info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
+
+                        yearInName = info.Year;
+
+                        if (yearInName.HasValue)
+                        {
+                            ProductionYear = yearInName;
+                            hasChanges = true;
+                        }
+                    }
+                }
+            }
+
+            return hasChanges;
+        }
     }
     }
 }
 }

+ 4 - 5
MediaBrowser.Controller/Entities/UserRootFolder.cs

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

+ 136 - 10
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -50,15 +50,15 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var user = query.User;
             var user = query.User;
 
 
-            if (query.IncludeItemTypes != null &&
-                query.IncludeItemTypes.Length == 1 &&
-                string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
-            {
-                if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
-                {
-                    return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
-                }
-            }
+            //if (query.IncludeItemTypes != null &&
+            //    query.IncludeItemTypes.Length == 1 &&
+            //    string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
+            //{
+            //    if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+            //    {
+            //        return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
+            //    }
+            //}
 
 
             switch (viewType)
             switch (viewType)
             {
             {
@@ -766,7 +766,7 @@ namespace MediaBrowser.Controller.Entities
             return items;
             return items;
         }
         }
 
 
-        private static bool CollapseBoxSetItems(InternalItemsQuery query,
+        public static bool CollapseBoxSetItems(InternalItemsQuery query,
             BaseItem queryParent,
             BaseItem queryParent,
             User user)
             User user)
         {
         {
@@ -1199,6 +1199,11 @@ namespace MediaBrowser.Controller.Entities
                 return false;
                 return false;
             }
             }
 
 
+            if (query.ExcludeLocationTypes.Length > 0 && query.ExcludeLocationTypes.Contains(item.LocationType))
+            {
+                return false;
+            }
+
             if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
             if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
             {
             {
                 return false;
                 return false;
@@ -1689,6 +1694,127 @@ namespace MediaBrowser.Controller.Entities
                 }
                 }
             }
             }
 
 
+            if (query.MinPremiereDate.HasValue)
+            {
+                var val = query.MinPremiereDate.Value;
+
+                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
+                {
+                    return false;
+                }
+            }
+
+            if (query.MaxPremiereDate.HasValue)
+            {
+                var val = query.MaxPremiereDate.Value;
+
+                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
+                {
+                    return false;
+                }
+            }
+
+            if (query.ParentIndexNumber.HasValue)
+            {
+                var filterValue = query.ParentIndexNumber.Value;
+
+                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
+                {
+                    return false;
+                }
+            }
+
+            if (query.AirDays.Length > 0)
+            {
+                var ok = new[] { item }.OfType<Series>().Any(p => p.AirDays != null && query.AirDays.Any(d => p.AirDays.Contains(d)));
+                if (!ok)
+                {
+                    return false;
+                }
+            }
+
+            if (query.SeriesStatuses.Length > 0)
+            {
+                var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value)); 
+                if (!ok)
+                {
+                    return false;
+                }
+            }
+
+            if (query.AiredDuringSeason.HasValue)
+            {
+                var episode = item as Episode;
+
+                if (episode == null)
+                {
+                    return false;
+                }
+
+                if (!Series.FilterEpisodesBySeason(new[] { episode }, query.AiredDuringSeason.Value, true).Any())
+                {
+                    return false;
+                }
+            }
+
+            if (!string.IsNullOrEmpty(query.AlbumArtistStartsWithOrGreater))
+            {
+                var ok = new[] { item }.OfType<IHasAlbumArtist>()
+                    .Any(p => string.Compare(query.AlbumArtistStartsWithOrGreater, p.AlbumArtists.FirstOrDefault(), StringComparison.CurrentCultureIgnoreCase) < 1);
+
+                if (!ok)
+                {
+                    return false;
+                }
+            }
+
+            // Artists
+            if (query.ArtistNames.Length > 0)
+            {
+                var audio = item as IHasArtist;
+
+                if (!(audio != null && query.ArtistNames.Any(audio.HasAnyArtist)))
+                {
+                    return false;
+                }
+            }
+
+            // Albums
+            if (query.AlbumNames.Length > 0)
+            {
+                var audio = item as Audio.Audio;
+
+                if (audio != null)
+                {
+                    if (!query.AlbumNames.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        return false;
+                    }
+                }
+
+                var album = item as MusicAlbum;
+
+                if (album != null)
+                {
+                    if (!query.AlbumNames.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        return false;
+                    }
+                }
+
+                var musicVideo = item as MusicVideo;
+
+                if (musicVideo != null)
+                {
+                    if (!query.AlbumNames.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        return false;
+                    }
+                }
+
+                return false;
+            }
+
             return true;
             return true;
         }
         }
 
 

+ 60 - 0
MediaBrowser.Controller/Entities/Video.cs

@@ -6,13 +6,16 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Net.Mime;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Channels;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
@@ -33,6 +36,7 @@ namespace MediaBrowser.Controller.Entities
         public List<string> AdditionalParts { get; set; }
         public List<string> AdditionalParts { get; set; }
         public List<string> LocalAlternateVersions { get; set; }
         public List<string> LocalAlternateVersions { get; set; }
         public List<LinkedChild> LinkedAlternateVersions { get; set; }
         public List<LinkedChild> LinkedAlternateVersions { get; set; }
+        public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsThemeMedia
         public bool IsThemeMedia
@@ -78,6 +82,23 @@ namespace MediaBrowser.Controller.Entities
                    locationType != LocationType.Virtual;
                    locationType != LocationType.Virtual;
         }
         }
 
 
+        [IgnoreDataMember]
+        public override LocationType LocationType
+        {
+            get
+            {
+                if (SourceType == SourceType.Channel)
+                {
+                    if (string.IsNullOrEmpty(Path))
+                    {
+                        return LocationType.Remote;
+                    }
+                }
+
+                return base.LocationType;
+            }
+        }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         public override bool SupportsAddingToPlaylist
         {
         {
@@ -130,6 +151,29 @@ namespace MediaBrowser.Controller.Entities
             return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
             return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
         }
         }
 
 
+        protected override string CreateUserDataKey()
+        {
+            if (ExtraType.HasValue)
+            {
+                var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb);
+
+                if (!string.IsNullOrWhiteSpace(key))
+                {
+                    key = key + "-" + ExtraType.ToString().ToLower();
+
+                    // Make sure different trailers have their own data.
+                    if (RunTimeTicks.HasValue)
+                    {
+                        key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
+                    }
+
+                    return key;
+                }
+            }
+
+            return base.CreateUserDataKey();
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the linked children.
         /// Gets the linked children.
         /// </summary>
         /// </summary>
@@ -441,6 +485,22 @@ namespace MediaBrowser.Controller.Entities
 
 
         public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         {
         {
+            if (SourceType == SourceType.Channel)
+            {
+                var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
+                           .Result.ToList();
+
+                if (sources.Count > 0)
+                {
+                    return sources;
+                }
+
+                return new List<MediaSourceInfo>
+                {
+                    GetVersionInfo(enablePathSubstitution, this, MediaSourceType.Placeholder)
+                };
+            }
+
             var item = this;
             var item = this;
 
 
             var result = item.GetAlternateVersions()
             var result = item.GetAlternateVersions()

+ 7 - 0
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -79,5 +79,12 @@ namespace MediaBrowser.Controller
         /// <param name="host">The host.</param>
         /// <param name="host">The host.</param>
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
         string GetLocalApiUrl(string host);
         string GetLocalApiUrl(string host);
+
+        /// <summary>
+        /// Gets the local API URL.
+        /// </summary>
+        /// <param name="ipAddress">The ip address.</param>
+        /// <returns>System.String.</returns>
+        string GetLocalApiUrl(IPAddress ipAddress);
     }
     }
 }
 }

+ 10 - 11
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -152,13 +152,6 @@ namespace MediaBrowser.Controller.Library
         /// <returns>BaseItem.</returns>
         /// <returns>BaseItem.</returns>
         BaseItem GetItemById(Guid id);
         BaseItem GetItemById(Guid id);
 
 
-        /// <summary>
-        /// Gets the items.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
-        QueryResult<BaseItem> GetItems(InternalItemsQuery query);
-
         /// <summary>
         /// <summary>
         /// Gets the memory item by identifier.
         /// Gets the memory item by identifier.
         /// </summary>
         /// </summary>
@@ -547,22 +540,28 @@ namespace MediaBrowser.Controller.Library
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
         Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
 
 
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
+
         /// <summary>
         /// <summary>
         /// Gets the items.
         /// Gets the items.
         /// </summary>
         /// </summary>
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
         /// <param name="parentIds">The parent ids.</param>
         /// <param name="parentIds">The parent ids.</param>
         /// <returns>List&lt;BaseItem&gt;.</returns>
         /// <returns>List&lt;BaseItem&gt;.</returns>
-        IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds);
+        IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds);
 
 
         /// <summary>
         /// <summary>
         /// Gets the items result.
         /// Gets the items result.
         /// </summary>
         /// </summary>
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
-        /// <param name="parentIds">The parent ids.</param>
         /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
         /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
-        QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds);
-
+        QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query);
+        
         /// <summary>
         /// <summary>
         /// Ignores the file.
         /// Ignores the file.
         /// </summary>
         /// </summary>

+ 3 - 0
MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs

@@ -15,11 +15,14 @@ namespace MediaBrowser.Controller.Library
         public BaseItem Item { get; set; }
         public BaseItem Item { get; set; }
         public BaseItemInfo MediaInfo { get; set; }
         public BaseItemInfo MediaInfo { get; set; }
         public string MediaSourceId { get; set; }
         public string MediaSourceId { get; set; }
+        public bool IsPaused { get; set; }
 
 
         public string DeviceId { get; set; }
         public string DeviceId { get; set; }
         public string DeviceName { get; set; }
         public string DeviceName { get; set; }
         public string ClientName { get; set; }
         public string ClientName { get; set; }
 
 
+        public string PlaySessionId { get; set; }
+      
         public PlaybackProgressEventArgs()
         public PlaybackProgressEventArgs()
         {
         {
             Users = new List<User>();
             Users = new List<User>();

+ 0 - 10
MediaBrowser.Controller/LiveTv/ILiveTvItem.cs

@@ -1,10 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.LiveTv
-{
-    public interface ILiveTvItem : IHasId
-    {
-        string ServiceName { get; set; }
-        string ExternalId { get; set; }
-    }
-}

+ 17 - 23
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -50,7 +50,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// </summary>
         /// <param name="recording">The recording.</param>
         /// <param name="recording">The recording.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        Task DeleteRecording(ILiveTvRecording recording);
+        Task DeleteRecording(BaseItem recording);
         
         
         /// <summary>
         /// <summary>
         /// Cancels the timer.
         /// Cancels the timer.
@@ -74,15 +74,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="listingProviders">The listing providers.</param>
         /// <param name="listingProviders">The listing providers.</param>
         void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
         void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders);
 
 
-        /// <summary>
-        /// Gets the channels.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <param name="options">The options.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>IEnumerable{Channel}.</returns>
-        Task<QueryResult<ChannelInfoDto>> GetChannels(LiveTvChannelQuery query, DtoOptions options, CancellationToken cancellationToken);
-
         /// <summary>
         /// <summary>
         /// Gets the recording.
         /// Gets the recording.
         /// </summary>
         /// </summary>
@@ -92,15 +83,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
         /// <returns>Task{RecordingInfoDto}.</returns>
         /// <returns>Task{RecordingInfoDto}.</returns>
         Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
         Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
-
-        /// <summary>
-        /// Gets the channel.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="user">The user.</param>
-        /// <returns>Task{RecordingInfoDto}.</returns>
-        Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null);
         
         
         /// <summary>
         /// <summary>
         /// Gets the timer.
         /// Gets the timer.
@@ -156,7 +138,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="id">The identifier.</param>
         /// <param name="id">The identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>LiveTvRecording.</returns>
         /// <returns>LiveTvRecording.</returns>
-        Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken);
+        Task<BaseItem> GetInternalRecording(string id, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Gets the recording stream.
         /// Gets the recording stream.
@@ -385,10 +367,22 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// <summary>
         /// Adds the channel information.
         /// Adds the channel information.
         /// </summary>
         /// </summary>
-        /// <param name="dto">The dto.</param>
-        /// <param name="channel">The channel.</param>
+        /// <param name="items">The items.</param>
         /// <param name="options">The options.</param>
         /// <param name="options">The options.</param>
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
-        void AddChannelInfo(BaseItemDto dto, LiveTvChannel channel, DtoOptions options, User user);
+        void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> items, DtoOptions options, User user);
+
+        /// <summary>
+        /// Called when [recording file deleted].
+        /// </summary>
+        /// <param name="recording">The recording.</param>
+        /// <returns>Task.</returns>
+        Task OnRecordingFileDeleted(BaseItem recording);
+
+        /// <summary>
+        /// Gets the sat ini mappings.
+        /// </summary>
+        /// <returns>List&lt;NameValuePair&gt;.</returns>
+        List<NameValuePair> GetSatIniMappings();
     }
     }
 }
 }

+ 3 - 1
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs

@@ -9,8 +9,10 @@ using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Controller.LiveTv
 namespace MediaBrowser.Controller.LiveTv
 {
 {
-    public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData, ILiveTvItem, IHasStartDate, IHasProgramAttributes
+    public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData, IHasStartDate, IHasProgramAttributes
     {
     {
+        string ServiceName { get; set; }
+        string ExternalId { get; set; }
         string ChannelId { get; }
         string ChannelId { get; }
         string MediaType { get; }
         string MediaType { get; }
 
 

+ 12 - 2
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -39,6 +39,13 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
         public bool IsPremiere { get; set; }
 
 
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get { return SourceType.LiveTV; }
+            set { }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the user data key.
         /// Gets the user data key.
         /// </summary>
         /// </summary>
@@ -50,8 +57,6 @@ namespace MediaBrowser.Controller.LiveTv
             return name + "-" + Name + (EpisodeTitle ?? string.Empty);
             return name + "-" + Name + (EpisodeTitle ?? string.Empty);
         }
         }
 
 
-        public string ServiceName { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether this instance is owned item.
         /// Gets a value indicating whether this instance is owned item.
         /// </summary>
         /// </summary>
@@ -151,5 +156,10 @@ namespace MediaBrowser.Controller.LiveTv
         {
         {
             return LiveTvManager.DeleteRecording(this);
             return LiveTvManager.DeleteRecording(this);
         }
         }
+
+        public override Task OnFileDeleted()
+        {
+            return LiveTvManager.OnRecordingFileDeleted(this);
+        }
     }
     }
 }
 }

+ 8 - 7
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -11,7 +11,7 @@ using System.Runtime.Serialization;
 
 
 namespace MediaBrowser.Controller.LiveTv
 namespace MediaBrowser.Controller.LiveTv
 {
 {
-    public class LiveTvChannel : BaseItem, IHasMediaSources, ILiveTvItem
+    public class LiveTvChannel : BaseItem, IHasMediaSources
     {
     {
         /// <summary>
         /// <summary>
         /// Gets the user data key.
         /// Gets the user data key.
@@ -40,6 +40,13 @@ namespace MediaBrowser.Controller.LiveTv
             }
             }
         }
         }
 
 
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get { return SourceType.LiveTV; }
+            set { }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the number.
         /// Gets or sets the number.
         /// </summary>
         /// </summary>
@@ -52,12 +59,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value>The type of the channel.</value>
         /// <value>The type of the channel.</value>
         public ChannelType ChannelType { get; set; }
         public ChannelType ChannelType { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override LocationType LocationType
         public override LocationType LocationType
         {
         {

+ 6 - 6
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -11,7 +11,7 @@ using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Controller.LiveTv
 namespace MediaBrowser.Controller.LiveTv
 {
 {
-    public class LiveTvProgram : BaseItem, ILiveTvItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
+    public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
     {
     {
         /// <summary>
         /// <summary>
         /// Gets the user data key.
         /// Gets the user data key.
@@ -39,12 +39,12 @@ namespace MediaBrowser.Controller.LiveTv
             return base.CreateUserDataKey();
             return base.CreateUserDataKey();
         }
         }
 
 
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
         [IgnoreDataMember]
         [IgnoreDataMember]
-        public string ServiceName { get; set; }
+        public override SourceType SourceType
+        {
+            get { return SourceType.LiveTV; }
+            set { }
+        }
 
 
         /// <summary>
         /// <summary>
         /// The start date of the program, in UTC.
         /// The start date of the program, in UTC.

+ 12 - 2
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -39,6 +39,13 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
         public bool IsPremiere { get; set; }
 
 
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get { return SourceType.LiveTV; }
+            set { }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the user data key.
         /// Gets the user data key.
         /// </summary>
         /// </summary>
@@ -65,8 +72,6 @@ namespace MediaBrowser.Controller.LiveTv
             return base.CreateUserDataKey();
             return base.CreateUserDataKey();
         }
         }
 
 
-        public string ServiceName { get; set; }
-
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override string MediaType
         public override string MediaType
         {
         {
@@ -166,5 +171,10 @@ namespace MediaBrowser.Controller.LiveTv
         {
         {
             return LiveTvManager.DeleteRecording(this);
             return LiveTvManager.DeleteRecording(this);
         }
         }
+
+        public override Task OnFileDeleted()
+        {
+            return LiveTvManager.OnRecordingFileDeleted(this);
+        }
     }
     }
 }
 }

+ 9 - 1
MediaBrowser.Controller/LiveTv/RecordingGroup.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Users;
 using MediaBrowser.Model.Users;
 
 
@@ -24,5 +25,12 @@ namespace MediaBrowser.Controller.LiveTv
                 return false;
                 return false;
             }
             }
         }
         }
+
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get { return SourceType.LiveTV; }
+            set { }
+        }
     }
     }
 }
 }

+ 7 - 6
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -46,7 +46,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="Interfaces.IO">
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
@@ -75,6 +75,7 @@
     </Compile>
     </Compile>
     <Compile Include="Activity\IActivityManager.cs" />
     <Compile Include="Activity\IActivityManager.cs" />
     <Compile Include="Activity\IActivityRepository.cs" />
     <Compile Include="Activity\IActivityRepository.cs" />
+    <Compile Include="Channels\ChannelAudioItem.cs" />
     <Compile Include="Channels\ChannelFolderItem.cs" />
     <Compile Include="Channels\ChannelFolderItem.cs" />
     <Compile Include="Channels\ChannelItemInfo.cs" />
     <Compile Include="Channels\ChannelItemInfo.cs" />
     <Compile Include="Channels\ChannelItemResult.cs" />
     <Compile Include="Channels\ChannelItemResult.cs" />
@@ -82,11 +83,10 @@
     <Compile Include="Channels\ChannelMediaInfo.cs" />
     <Compile Include="Channels\ChannelMediaInfo.cs" />
     <Compile Include="Channels\ChannelParentalRating.cs" />
     <Compile Include="Channels\ChannelParentalRating.cs" />
     <Compile Include="Channels\ChannelSearchInfo.cs" />
     <Compile Include="Channels\ChannelSearchInfo.cs" />
+    <Compile Include="Channels\ChannelVideoItem.cs" />
     <Compile Include="Channels\IChannel.cs" />
     <Compile Include="Channels\IChannel.cs" />
-    <Compile Include="Channels\IChannelManager.cs" />
     <Compile Include="Channels\IChannelItem.cs" />
     <Compile Include="Channels\IChannelItem.cs" />
-    <Compile Include="Channels\ChannelAudioItem.cs" />
-    <Compile Include="Channels\ChannelVideoItem.cs" />
+    <Compile Include="Channels\IChannelManager.cs" />
     <Compile Include="Channels\Channel.cs" />
     <Compile Include="Channels\Channel.cs" />
     <Compile Include="Channels\IChannelMediaItem.cs" />
     <Compile Include="Channels\IChannelMediaItem.cs" />
     <Compile Include="Channels\IHasCacheKey.cs" />
     <Compile Include="Channels\IHasCacheKey.cs" />
@@ -128,6 +128,7 @@
     <Compile Include="Drawing\ImageStream.cs" />
     <Compile Include="Drawing\ImageStream.cs" />
     <Compile Include="Dto\DtoOptions.cs" />
     <Compile Include="Dto\DtoOptions.cs" />
     <Compile Include="Dto\IDtoService.cs" />
     <Compile Include="Dto\IDtoService.cs" />
+    <Compile Include="Entities\Audio\AudioPodcast.cs" />
     <Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
     <Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
     <Compile Include="Entities\Audio\IHasMusicGenres.cs" />
     <Compile Include="Entities\Audio\IHasMusicGenres.cs" />
     <Compile Include="Entities\Book.cs" />
     <Compile Include="Entities\Book.cs" />
@@ -180,6 +181,7 @@
     <Compile Include="Entities\Photo.cs" />
     <Compile Include="Entities\Photo.cs" />
     <Compile Include="Entities\PhotoAlbum.cs" />
     <Compile Include="Entities\PhotoAlbum.cs" />
     <Compile Include="Entities\Share.cs" />
     <Compile Include="Entities\Share.cs" />
+    <Compile Include="Entities\SourceType.cs" />
     <Compile Include="Entities\UserView.cs" />
     <Compile Include="Entities\UserView.cs" />
     <Compile Include="Entities\UserViewBuilder.cs" />
     <Compile Include="Entities\UserViewBuilder.cs" />
     <Compile Include="FileOrganization\IFileOrganizationService.cs" />
     <Compile Include="FileOrganization\IFileOrganizationService.cs" />
@@ -202,7 +204,6 @@
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="LiveTv\IHasRegistrationInfo.cs" />
     <Compile Include="LiveTv\IHasRegistrationInfo.cs" />
     <Compile Include="LiveTv\IListingsProvider.cs" />
     <Compile Include="LiveTv\IListingsProvider.cs" />
-    <Compile Include="LiveTv\ILiveTvItem.cs" />
     <Compile Include="LiveTv\ITunerHost.cs" />
     <Compile Include="LiveTv\ITunerHost.cs" />
     <Compile Include="LiveTv\RecordingGroup.cs" />
     <Compile Include="LiveTv\RecordingGroup.cs" />
     <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
     <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
@@ -271,7 +272,6 @@
     <Compile Include="Providers\ArtistInfo.cs" />
     <Compile Include="Providers\ArtistInfo.cs" />
     <Compile Include="Providers\BookInfo.cs" />
     <Compile Include="Providers\BookInfo.cs" />
     <Compile Include="Providers\BoxSetInfo.cs" />
     <Compile Include="Providers\BoxSetInfo.cs" />
-    <Compile Include="Providers\ChannelItemLookupInfo.cs" />
     <Compile Include="Providers\DirectoryService.cs" />
     <Compile Include="Providers\DirectoryService.cs" />
     <Compile Include="Providers\DynamicImageInfo.cs" />
     <Compile Include="Providers\DynamicImageInfo.cs" />
     <Compile Include="Providers\DynamicImageResponse.cs" />
     <Compile Include="Providers\DynamicImageResponse.cs" />
@@ -331,6 +331,7 @@
     <Compile Include="Security\IEncryptionManager.cs" />
     <Compile Include="Security\IEncryptionManager.cs" />
     <Compile Include="Session\AuthenticationRequest.cs" />
     <Compile Include="Session\AuthenticationRequest.cs" />
     <Compile Include="Social\ISharingManager.cs" />
     <Compile Include="Social\ISharingManager.cs" />
+    <Compile Include="Sorting\SortHelper.cs" />
     <Compile Include="Subtitles\ISubtitleManager.cs" />
     <Compile Include="Subtitles\ISubtitleManager.cs" />
     <Compile Include="Subtitles\ISubtitleProvider.cs" />
     <Compile Include="Subtitles\ISubtitleProvider.cs" />
     <Compile Include="Providers\ItemIdentifier.cs" />
     <Compile Include="Providers\ItemIdentifier.cs" />

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

@@ -90,8 +90,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             Cabac = info.Cabac;
             Cabac = info.Cabac;
             Context = info.Context;
             Context = info.Context;
 
 
-            if (info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ||
-                info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed)
+            if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
             {
             {
                 SubtitleStreamIndex = info.SubtitleStreamIndex;
                 SubtitleStreamIndex = info.SubtitleStreamIndex;
             }
             }

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

@@ -42,13 +42,6 @@ namespace MediaBrowser.Controller.Persistence
         /// <returns>Task{IEnumerable{ItemReview}}.</returns>
         /// <returns>Task{IEnumerable{ItemReview}}.</returns>
         IEnumerable<ItemReview> GetCriticReviews(Guid itemId);
         IEnumerable<ItemReview> GetCriticReviews(Guid itemId);
 
 
-        /// <summary>
-        /// Gets the children items.
-        /// </summary>
-        /// <param name="parentId">The parent identifier.</param>
-        /// <returns>IEnumerable&lt;BaseItem&gt;.</returns>
-        IEnumerable<BaseItem> GetChildrenItems(Guid parentId);
-
         /// <summary>
         /// <summary>
         /// Saves the critic reviews.
         /// Saves the critic reviews.
         /// </summary>
         /// </summary>
@@ -96,22 +89,6 @@ namespace MediaBrowser.Controller.Persistence
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken);
         Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken);
 
 
-        /// <summary>
-        /// Gets the children.
-        /// </summary>
-        /// <param name="parentId">The parent id.</param>
-        /// <returns>IEnumerable{ChildDefinition}.</returns>
-        IEnumerable<Guid> GetChildren(Guid parentId);
-
-        /// <summary>
-        /// Saves the children.
-        /// </summary>
-        /// <param name="parentId">The parent id.</param>
-        /// <param name="children">The children.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task SaveChildren(Guid parentId, IEnumerable<Guid> children, CancellationToken cancellationToken);
-
         /// <summary>
         /// <summary>
         /// Gets the media streams.
         /// Gets the media streams.
         /// </summary>
         /// </summary>

+ 0 - 11
MediaBrowser.Controller/Providers/ChannelItemLookupInfo.cs

@@ -1,11 +0,0 @@
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Controller.Providers
-{
-    public class ChannelItemLookupInfo : ItemLookupInfo
-    {
-        public ChannelMediaContentType ContentType { get; set; }
-        public ExtraType ExtraType { get; set; }
-    }
-}

+ 2 - 0
MediaBrowser.Controller/Providers/TrailerInfo.cs

@@ -1,3 +1,5 @@
+using MediaBrowser.Model.Entities;
+
 namespace MediaBrowser.Controller.Providers
 namespace MediaBrowser.Controller.Providers
 {
 {
     public class TrailerInfo : ItemLookupInfo
     public class TrailerInfo : ItemLookupInfo

+ 31 - 0
MediaBrowser.Controller/Sorting/SortHelper.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Sorting
+{
+    public static class SortHelper
+    {
+        private enum ChunkType { Alphanumeric, Numeric };
+
+        public static bool InChunk(char ch, char otherCh)
+        {
+            var type = ChunkType.Alphanumeric;
+
+            if (char.IsDigit(otherCh))
+            {
+                type = ChunkType.Numeric;
+            }
+
+            if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
+                || (type == ChunkType.Numeric && !char.IsDigit(ch)))
+            {
+                return false;
+            }
+
+            return true;
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Controller/packages.config

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

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

@@ -488,12 +488,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
             var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
             {
                 Person = person.Name,
                 Person = person.Name,
-                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name },
+                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
                 SortBy = new[] { ItemSortBy.SortName },
                 SortBy = new[] { ItemSortBy.SortName },
                 Limit = limit,
                 Limit = limit,
                 StartIndex = startIndex
                 StartIndex = startIndex
 
 
-            }, new string[] { });
+            });
 
 
             var serverItems = itemsResult.Items.Select(i => new ServerItem
             var serverItems = itemsResult.Items.Select(i => new ServerItem
             {
             {

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

@@ -188,15 +188,15 @@ namespace MediaBrowser.Dlna.Didl
                 {
                 {
                     var subtitleAdded = AddSubtitleElement(container, subtitle);
                     var subtitleAdded = AddSubtitleElement(container, subtitle);
 
 
-					if (subtitleAdded && _profile.EnableSingleSubtitleLimit) 
-					{
-						break;
-					}
+                    if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
+                    {
+                        break;
+                    }
                 }
                 }
             }
             }
         }
         }
 
 
-		private bool AddSubtitleElement(XmlElement container, SubtitleStreamInfo info)
+        private bool AddSubtitleElement(XmlElement container, SubtitleStreamInfo info)
         {
         {
             var subtitleProfile = _profile.SubtitleProfiles
             var subtitleProfile = _profile.SubtitleProfiles
                 .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
                 .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
@@ -213,13 +213,13 @@ namespace MediaBrowser.Dlna.Didl
                 // <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
                 // <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
                 // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
                 // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
 
 
-                //var res = container.OwnerDocument.CreateElement("SEC", "CaptionInfoEx");
+                var res = container.OwnerDocument.CreateElement("CaptionInfoEx", "sec");
 
 
-                //res.InnerText = info.Url;
+                res.InnerText = info.Url;
 
 
                 //// TODO: attribute needs SEC:
                 //// TODO: attribute needs SEC:
-                //res.SetAttribute("type", info.Format.ToLower());
-                //container.AppendChild(res);
+                res.SetAttribute("type", "sec", info.Format.ToLower());
+                container.AppendChild(res);
             }
             }
             else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
             else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -243,7 +243,7 @@ namespace MediaBrowser.Dlna.Didl
                 container.AppendChild(res);
                 container.AppendChild(res);
             }
             }
 
 
-			return true;
+            return true;
         }
         }
 
 
         private void AddVideoResource(XmlElement container, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
         private void AddVideoResource(XmlElement container, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)

+ 1 - 1
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -161,7 +161,7 @@ namespace MediaBrowser.Dlna.Main
 
 
                 var descriptorURI = "/dlna/" + udn + "/description.xml";
                 var descriptorURI = "/dlna/" + udn + "/description.xml";
 
 
-                var uri = new Uri(_appHost.GetLocalApiUrl(addressString) + descriptorURI);
+                var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI);
 
 
                 var services = new List<string>
                 var services = new List<string>
                 {
                 {

+ 1 - 1
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -42,7 +42,7 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.8\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="MoreLinq">
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>

+ 1 - 1
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -171,7 +171,7 @@ namespace MediaBrowser.Dlna.PlayTo
 
 
         private string GetServerAddress(IPAddress localIp)
         private string GetServerAddress(IPAddress localIp)
         {
         {
-            return _appHost.GetLocalApiUrl(localIp.ToString());
+            return _appHost.GetLocalApiUrl(localIp);
         }
         }
 
 
         public void Dispose()
         public void Dispose()

+ 4 - 4
MediaBrowser.Dlna/Profiles/DefaultProfile.cs

@@ -31,10 +31,10 @@ namespace MediaBrowser.Dlna.Profiles
             MaxIconWidth = 48;
             MaxIconWidth = 48;
             MaxIconHeight = 48;
             MaxIconHeight = 48;
 
 
-            MaxStreamingBitrate = 12000000;
-            MaxStaticBitrate = 12000000;
-            MusicStreamingTranscodingBitrate = 128000;
-            MusicSyncBitrate = 128000;
+            MaxStreamingBitrate = 15000000;
+            MaxStaticBitrate = 15000000;
+            MusicStreamingTranscodingBitrate = 192000;
+            MusicSyncBitrate = 192000;
 
 
             EnableAlbumArtInDidl = false;
             EnableAlbumArtInDidl = false;
 
 

+ 42 - 0
MediaBrowser.Dlna/Profiles/KodiProfile.cs

@@ -99,6 +99,48 @@ namespace MediaBrowser.Dlna.Profiles
                     DidlMode = "",
                     DidlMode = "",
                 },
                 },
 
 
+                new SubtitleProfile
+                {
+                    Format = "ass",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "ssa",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "smi",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "dvdsub",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "pgs",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "pgssub",
+                    Method = SubtitleDeliveryMethod.Embed,
+                    DidlMode = "",
+                },
+
                 new SubtitleProfile
                 new SubtitleProfile
                 {
                 {
                     Format = "sub",
                     Format = "sub",

+ 3 - 3
MediaBrowser.Dlna/Profiles/LgTvProfile.cs

@@ -54,21 +54,21 @@ namespace MediaBrowser.Dlna.Profiles
                 new DirectPlayProfile
                 new DirectPlayProfile
                 {
                 {
                     Container = "ts",
                     Container = "ts",
-                    VideoCodec = "h264",
+                    VideoCodec = "h264,hevc",
                     AudioCodec = "aac,ac3,mp3",
                     AudioCodec = "aac,ac3,mp3",
                     Type = DlnaProfileType.Video
                     Type = DlnaProfileType.Video
                 },
                 },
                 new DirectPlayProfile
                 new DirectPlayProfile
                 {
                 {
                     Container = "mkv",
                     Container = "mkv",
-                    VideoCodec = "h264",
+                    VideoCodec = "h264,hevc",
                     AudioCodec = "aac,ac3,mp3",
                     AudioCodec = "aac,ac3,mp3",
                     Type = DlnaProfileType.Video
                     Type = DlnaProfileType.Video
                 },
                 },
                 new DirectPlayProfile
                 new DirectPlayProfile
                 {
                 {
                     Container = "mp4",
                     Container = "mp4",
-                    VideoCodec = "h264,mpeg4",
+                    VideoCodec = "h264,mpeg4,hevc",
                     AudioCodec = "aac,ac3,mp3",
                     AudioCodec = "aac,ac3,mp3",
                     Type = DlnaProfileType.Video
                     Type = DlnaProfileType.Video
                 },
                 },

File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Default.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml


+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -24,10 +24,10 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>12000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>12000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate>
-  <MusicSyncBitrate>128000</MusicSyncBitrate>
+  <MaxStreamingBitrate>15000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>15000000</MaxStaticBitrate>
+  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
+  <MusicSyncBitrate>192000</MusicSyncBitrate>
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
   <ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

+ 6 - 0
MediaBrowser.Dlna/Profiles/Xml/Kodi.xml

@@ -52,6 +52,12 @@
     <SubtitleProfile format="srt" method="External" />
     <SubtitleProfile format="srt" method="External" />
     <SubtitleProfile format="sub" method="External" />
     <SubtitleProfile format="sub" method="External" />
     <SubtitleProfile format="srt" method="Embed" didlMode="" />
     <SubtitleProfile format="srt" method="Embed" didlMode="" />
+    <SubtitleProfile format="ass" method="Embed" didlMode="" />
+    <SubtitleProfile format="ssa" method="Embed" didlMode="" />
+    <SubtitleProfile format="smi" method="Embed" didlMode="" />
+    <SubtitleProfile format="dvdsub" method="Embed" didlMode="" />
+    <SubtitleProfile format="pgs" method="Embed" didlMode="" />
+    <SubtitleProfile format="pgssub" method="Embed" didlMode="" />
     <SubtitleProfile format="sub" method="Embed" didlMode="" />
     <SubtitleProfile format="sub" method="Embed" didlMode="" />
   </SubtitleProfiles>
   </SubtitleProfiles>
 </Profile>
 </Profile>

File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml


+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -24,10 +24,10 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>12000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>12000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>128000</MusicStreamingTranscodingBitrate>
-  <MusicSyncBitrate>128000</MusicSyncBitrate>
+  <MaxStreamingBitrate>15000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>15000000</MaxStaticBitrate>
+  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
+  <MusicSyncBitrate>192000</MusicSyncBitrate>
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
   <ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>

File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml


File diff suppressed because it is too large
+ 4 - 4
MediaBrowser.Dlna/Profiles/Xml/Vlc.xml


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