Selaa lähdekoodia

Merge pull request #2224 from MediaBrowser/beta

Beta
Luke 8 vuotta sitten
vanhempi
sitoutus
25ef9777ca
100 muutettua tiedostoa jossa 1219 lisäystä ja 854 poistoa
  1. 1 12
      Emby.Drawing/ImageProcessor.cs
  2. 49 11
      MediaBrowser.Api/ApiEntryPoint.cs
  3. 6 2
      MediaBrowser.Api/Dlna/DlnaServerService.cs
  4. 8 1
      MediaBrowser.Api/FilterService.cs
  5. 4 3
      MediaBrowser.Api/GamesService.cs
  6. 2 1
      MediaBrowser.Api/Images/RemoteImageService.cs
  7. 3 28
      MediaBrowser.Api/ItemUpdateService.cs
  8. 0 30
      MediaBrowser.Api/Library/ChapterService.cs
  9. 4 28
      MediaBrowser.Api/Library/LibraryService.cs
  10. 44 2
      MediaBrowser.Api/Library/LibraryStructureService.cs
  11. 129 8
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  12. 7 3
      MediaBrowser.Api/MediaBrowser.Api.csproj
  13. 14 8
      MediaBrowser.Api/Movies/MoviesService.cs
  14. 4 4
      MediaBrowser.Api/PackageService.cs
  15. 63 23
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  16. 31 29
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  17. 5 3
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  18. 1 3
      MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
  19. 2 0
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  20. 2 2
      MediaBrowser.Api/Playback/MediaInfoService.cs
  21. 37 6
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  22. 33 3
      MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
  23. 23 2
      MediaBrowser.Api/Playback/StreamState.cs
  24. 0 4
      MediaBrowser.Api/Reports/Data/ReportBuilder.cs
  25. 0 25
      MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
  26. 2 1
      MediaBrowser.Api/SimilarItemsHelper.cs
  27. 4 7
      MediaBrowser.Api/StartupWizardService.cs
  28. 1 1
      MediaBrowser.Api/Subtitles/SubtitleService.cs
  29. 8 6
      MediaBrowser.Api/TvShowsService.cs
  30. 1 0
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  31. 22 14
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  32. 7 1
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  33. 11 11
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  34. 31 0
      MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs
  35. 10 0
      MediaBrowser.Common.Implementations/Logging/NLogger.cs
  36. 14 5
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  37. 2 2
      MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  38. 4 2
      MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
  39. 4 2
      MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
  40. 4 8
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  41. 3 2
      MediaBrowser.Common.Implementations/packages.config
  42. 11 0
      MediaBrowser.Common/IO/IMemoryStreamProvider.cs
  43. 1 0
      MediaBrowser.Common/MediaBrowser.Common.csproj
  44. 1 1
      MediaBrowser.Common/Updates/IInstallationManager.cs
  45. 0 10
      MediaBrowser.Controller/Channels/IChannelManager.cs
  46. 0 19
      MediaBrowser.Controller/Chapters/ChapterResponse.cs
  47. 0 31
      MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs
  48. 1 46
      MediaBrowser.Controller/Chapters/IChapterManager.cs
  49. 0 39
      MediaBrowser.Controller/Chapters/IChapterProvider.cs
  50. 12 2
      MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
  51. 0 1
      MediaBrowser.Controller/Dlna/ISsdpHandler.cs
  52. 15 0
      MediaBrowser.Controller/Entities/AggregateFolder.cs
  53. 12 19
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  54. 12 4
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  55. 10 8
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  56. 80 17
      MediaBrowser.Controller/Entities/BaseItem.cs
  57. 28 14
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  58. 36 52
      MediaBrowser.Controller/Entities/Folder.cs
  59. 8 7
      MediaBrowser.Controller/Entities/Game.cs
  60. 9 0
      MediaBrowser.Controller/Entities/GameSystem.cs
  61. 0 8
      MediaBrowser.Controller/Entities/IArchivable.cs
  62. 1 5
      MediaBrowser.Controller/Entities/IHasImages.cs
  63. 3 0
      MediaBrowser.Controller/Entities/IHasMetadata.cs
  64. 0 34
      MediaBrowser.Controller/Entities/IHasProductionLocations.cs
  65. 0 39
      MediaBrowser.Controller/Entities/IHasTaglines.cs
  66. 0 23
      MediaBrowser.Controller/Entities/IHasThemeMedia.cs
  67. 2 0
      MediaBrowser.Controller/Entities/IHasUserData.cs
  68. 49 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  69. 13 11
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  70. 11 4
      MediaBrowser.Controller/Entities/MusicVideo.cs
  71. 3 11
      MediaBrowser.Controller/Entities/Photo.cs
  72. 9 0
      MediaBrowser.Controller/Entities/PhotoAlbum.cs
  73. 0 2
      MediaBrowser.Controller/Entities/TV/Season.cs
  74. 1 4
      MediaBrowser.Controller/Entities/TV/Series.cs
  75. 3 13
      MediaBrowser.Controller/Entities/Trailer.cs
  76. 9 0
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  77. 9 1
      MediaBrowser.Controller/Entities/UserView.cs
  78. 3 15
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  79. 28 20
      MediaBrowser.Controller/Entities/Video.cs
  80. 5 2
      MediaBrowser.Controller/Library/ILibraryManager.cs
  81. 10 6
      MediaBrowser.Controller/Library/IMediaSourceManager.cs
  82. 3 3
      MediaBrowser.Controller/Library/IMediaSourceProvider.cs
  83. 15 23
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  84. 1 0
      MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
  85. 11 0
      MediaBrowser.Controller/LiveTv/ILiveTvService.cs
  86. 2 5
      MediaBrowser.Controller/LiveTv/ITunerHost.cs
  87. 45 0
      MediaBrowser.Controller/LiveTv/LiveStream.cs
  88. 2 1
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  89. 55 3
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  90. 20 1
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  91. 2 0
      MediaBrowser.Controller/LiveTv/ProgramInfo.cs
  92. 10 0
      MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
  93. 35 3
      MediaBrowser.Controller/LiveTv/TimerInfo.cs
  94. 1 11
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  95. 3 3
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  96. 1 0
      MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
  97. 2 2
      MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
  98. 0 8
      MediaBrowser.Controller/Net/IHttpResultFactory.cs
  99. 16 7
      MediaBrowser.Controller/Playlists/Playlist.cs
  100. 0 13
      MediaBrowser.Controller/Power/IPowerManagement.cs

+ 1 - 12
Emby.Drawing/ImageProcessor.cs

@@ -829,18 +829,7 @@ namespace Emby.Drawing
             // Run the enhancers sequentially in order of priority
             foreach (var enhancer in imageEnhancers)
             {
-                var typeName = enhancer.GetType().Name;
-
-                try
-                {
-                    await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("{0} failed enhancing {1}", ex, typeName, item.Name);
-
-                    throw;
-                }
+                await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false);
 
                 // Feed the output into the next enhancer as input
                 inputPath = outputPath;

+ 49 - 11
MediaBrowser.Api/ApiEntryPoint.cs

@@ -8,6 +8,7 @@ using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Session;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
@@ -15,6 +16,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Api
 {
@@ -43,7 +45,13 @@ namespace MediaBrowser.Api
         private readonly IFileSystem _fileSystem;
         private readonly IMediaSourceManager _mediaSourceManager;
 
-        public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
+        /// <summary>
+        /// The active transcoding jobs
+        /// </summary>
+        private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
+
+        private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks =
+            new Dictionary<string, SemaphoreSlim>();
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ApiEntryPoint" /> class.
@@ -66,6 +74,21 @@ namespace MediaBrowser.Api
             _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
         }
 
+        public SemaphoreSlim GetTranscodingLock(string outputPath)
+        {
+            lock (_transcodingLocks)
+            {
+                SemaphoreSlim result;
+                if (!_transcodingLocks.TryGetValue(outputPath, out result))
+                {
+                    result = new SemaphoreSlim(1, 1);
+                    _transcodingLocks[outputPath] = result;
+                }
+
+                return result;
+            }
+        }
+
         private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
         {
             if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
@@ -147,11 +170,6 @@ namespace MediaBrowser.Api
             }
         }
 
-        /// <summary>
-        /// The active transcoding jobs
-        /// </summary>
-        private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
-
         /// <summary>
         /// Called when [transcode beginning].
         /// </summary>
@@ -187,7 +205,8 @@ namespace MediaBrowser.Api
                     CancellationTokenSource = cancellationTokenSource,
                     Id = transcodingJobId,
                     PlaySessionId = playSessionId,
-                    LiveStreamId = liveStreamId
+                    LiveStreamId = liveStreamId,
+                    MediaSource = state.MediaSource
                 };
 
                 _activeTranscodingJobs.Add(job);
@@ -256,6 +275,11 @@ namespace MediaBrowser.Api
                 }
             }
 
+            lock (_transcodingLocks)
+            {
+                _transcodingLocks.Remove(path);
+            }
+
             if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
             {
                 _sessionManager.ClearTranscodingInfo(state.Request.DeviceId);
@@ -281,6 +305,14 @@ namespace MediaBrowser.Api
             }
         }
 
+        public TranscodingJob GetTranscodingJob(string playSessionId)
+        {
+            lock (_activeTranscodingJobs)
+            {
+                return _activeTranscodingJobs.FirstOrDefault(j => string.Equals(j.PlaySessionId, playSessionId, StringComparison.OrdinalIgnoreCase));
+            }
+        }
+
         /// <summary>
         /// Called when [transcode begin request].
         /// </summary>
@@ -487,6 +519,11 @@ namespace MediaBrowser.Api
                 }
             }
 
+            lock (_transcodingLocks)
+            {
+                _transcodingLocks.Remove(job.Path);
+            }
+
             lock (job.ProcessLock)
             {
                 if (job.TranscodingThrottler != null)
@@ -530,7 +567,7 @@ namespace MediaBrowser.Api
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -656,6 +693,7 @@ namespace MediaBrowser.Api
         /// Gets or sets the path.
         /// </summary>
         /// <value>The path.</value>
+        public MediaSourceInfo MediaSource { get; set; }
         public string Path { get; set; }
         /// <summary>
         /// Gets or sets the type.
@@ -751,12 +789,12 @@ namespace MediaBrowser.Api
             {
                 if (KillTimer == null)
                 {
-                    Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite);
                 }
                 else
                 {
-                    Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer.Change(intervalMs, Timeout.Infinite);
                 }
             }
@@ -775,7 +813,7 @@ namespace MediaBrowser.Api
                 {
                     var intervalMs = PingTimeout;
 
-                    Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer.Change(intervalMs, Timeout.Infinite);
                 }
             }

+ 6 - 2
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -7,6 +7,8 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
 
 namespace MediaBrowser.Api.Dlna
 {
@@ -109,13 +111,15 @@ namespace MediaBrowser.Api.Dlna
         private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
 
         private const string XMLContentType = "text/xml; charset=UTF-8";
+        private readonly IMemoryStreamProvider _memoryStreamProvider;
 
-        public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
+        public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamProvider memoryStreamProvider)
         {
             _dlnaManager = dlnaManager;
             _contentDirectory = contentDirectory;
             _connectionManager = connectionManager;
             _mediaReceiverRegistrar = mediaReceiverRegistrar;
+            _memoryStreamProvider = memoryStreamProvider;
         }
 
         public object Get(GetDescriptionXml request)
@@ -201,7 +205,7 @@ namespace MediaBrowser.Api.Dlna
         {
             using (var response = _dlnaManager.GetIcon(request.Filename))
             {
-                using (var ms = new MemoryStream())
+                using (var ms = _memoryStreamProvider.CreateNew())
                 {
                     response.Stream.CopyTo(ms);
 

+ 8 - 1
MediaBrowser.Api/FilterService.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Querying;
 using ServiceStack;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 
@@ -104,7 +105,13 @@ namespace MediaBrowser.Api
                 MediaTypes = request.GetMediaTypes(),
                 IncludeItemTypes = request.GetIncludeItemTypes(),
                 Recursive = true,
-                EnableTotalRecordCount = false
+                EnableTotalRecordCount = false,
+                DtoOptions = new Controller.Dto.DtoOptions
+                {
+                    Fields = new List<ItemFields> { ItemFields.Genres, ItemFields.Tags },
+                    EnableImages = false,
+                    EnableUserData = false
+                }
             };
 
             return query;

+ 4 - 3
MediaBrowser.Api/GamesService.cs

@@ -200,6 +200,8 @@ namespace MediaBrowser.Api
                 (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
 
+            var dtoOptions = GetDtoOptions(request);
+
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
                 Limit = request.Limit,
@@ -207,12 +209,11 @@ namespace MediaBrowser.Api
                 {
                         typeof(Game).Name
                 },
-                SimilarTo = item
+                SimilarTo = item,
+                DtoOptions = dtoOptions
 
             }).ToList();
 
-            var dtoOptions = GetDtoOptions(request);
-
             var result = new QueryResult<BaseItemDto>
             {
                 Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),

+ 2 - 1
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -273,7 +273,8 @@ namespace MediaBrowser.Api.Images
         {
             var result = await _httpClient.GetResponse(new HttpRequestOptions
             {
-                Url = url
+                Url = url,
+                BufferContent = false
 
             }).ConfigureAwait(false);
 

+ 3 - 28
MediaBrowser.Api/ItemUpdateService.cs

@@ -243,11 +243,7 @@ namespace MediaBrowser.Api
                 hasBudget.Revenue = request.Revenue;
             }
 
-            var hasOriginalTitle = item as IHasOriginalTitle;
-            if (hasOriginalTitle != null)
-            {
-                hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle;
-            }
+            item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
 
             var hasCriticRating = item as IHasCriticRating;
             if (hasCriticRating != null)
@@ -278,10 +274,9 @@ namespace MediaBrowser.Api
 
             item.Tags = request.Tags;
 
-            var hasTaglines = item as IHasTaglines;
-            if (hasTaglines != null)
+            if (request.Taglines != null)
             {
-                hasTaglines.Taglines = request.Taglines;
+                item.Tagline = request.Taglines.FirstOrDefault();
             }
 
             var hasShortOverview = item as IHasShortOverview;
@@ -308,8 +303,6 @@ namespace MediaBrowser.Api
             item.OfficialRating = string.IsNullOrWhiteSpace(request.OfficialRating) ? null : request.OfficialRating;
             item.CustomRating = request.CustomRating;
 
-            SetProductionLocations(item, request);
-
             item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
             item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
 
@@ -417,23 +410,5 @@ namespace MediaBrowser.Api
                 series.AirTime = request.AirTime;
             }
         }
-
-        private void SetProductionLocations(BaseItem item, BaseItemDto request)
-        {
-            var hasProductionLocations = item as IHasProductionLocations;
-
-            if (hasProductionLocations != null)
-            {
-                hasProductionLocations.ProductionLocations = request.ProductionLocations;
-            }
-
-            var person = item as Person;
-            if (person != null)
-            {
-                person.PlaceOfBirth = request.ProductionLocations == null
-                    ? null
-                    : request.ProductionLocations.FirstOrDefault();
-            }
-        }
     }
 }

+ 0 - 30
MediaBrowser.Api/Library/ChapterService.cs

@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Chapters;
-using MediaBrowser.Controller.Net;
-using ServiceStack;
-using System.Linq;
-
-namespace MediaBrowser.Api.Library
-{
-    [Route("/Providers/Chapters", "GET")]
-    public class GetChapterProviders : IReturnVoid
-    {
-    }
-
-    [Authenticated]
-    public class ChapterService : BaseApiService
-    {
-        private readonly IChapterManager _chapterManager;
-
-        public ChapterService(IChapterManager chapterManager)
-        {
-            _chapterManager = chapterManager;
-        }
-
-        public object Get(GetChapterProviders request)
-        {
-            var result = _chapterManager.GetProviders().ToList();
-
-            return ToOptimizedResult(result);
-        }
-    }
-}

+ 4 - 28
MediaBrowser.Api/Library/LibraryService.cs

@@ -835,14 +835,14 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+            while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
                 item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);
 
-            var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById)
+            var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
                             .Where(i => i != null)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -879,14 +879,14 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+            while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
                 item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);
 
-            var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById)
+            var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
                             .Where(i => i != null)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -901,30 +901,6 @@ namespace MediaBrowser.Api.Library
             };
         }
 
-        private List<Guid> GetThemeVideoIds(BaseItem item)
-        {
-            var i = item as IHasThemeMedia;
-
-            if (i != null)
-            {
-                return i.ThemeVideoIds;
-            }
-
-            return new List<Guid>();
-        }
-
-        private List<Guid> GetThemeSongIds(BaseItem item)
-        {
-            var i = item as IHasThemeMedia;
-
-            if (i != null)
-            {
-                return i.ThemeSongIds;
-            }
-
-            return new List<Guid>();
-        }
-
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
         public object Get(GetYearIndex request)

+ 44 - 2
MediaBrowser.Api/Library/LibraryStructureService.cs

@@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library
         /// <value>The name.</value>
         public string Path { get; set; }
 
+        public MediaPathInfo PathInfo { get; set; }
+
         /// <summary>
         /// Gets or sets a value indicating whether [refresh library].
         /// </summary>
@@ -119,6 +121,18 @@ namespace MediaBrowser.Api.Library
         public bool RefreshLibrary { get; set; }
     }
 
+    [Route("/Library/VirtualFolders/Paths/Update", "POST")]
+    public class UpdateMediaPath : IReturnVoid
+    {
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string Name { get; set; }
+
+        public MediaPathInfo PathInfo { get; set; }
+    }
+
     [Route("/Library/VirtualFolders/Paths", "DELETE")]
     public class RemoveMediaPath : IReturnVoid
     {
@@ -212,7 +226,12 @@ namespace MediaBrowser.Api.Library
         {
             var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
 
-            _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary);
+            if (request.Paths != null && request.Paths.Length > 0)
+            {
+                libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
+            }
+
+            _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
         }
 
         /// <summary>
@@ -308,7 +327,16 @@ namespace MediaBrowser.Api.Library
 
             try
             {
-                _libraryManager.AddMediaPath(request.Name, request.Path);
+                var mediaPath = request.PathInfo;
+
+                if (mediaPath == null)
+                {
+                    mediaPath = new MediaPathInfo
+                    {
+                        Path = request.Path
+                    };
+                }
+                _libraryManager.AddMediaPath(request.Name, mediaPath);
             }
             finally
             {
@@ -332,6 +360,20 @@ namespace MediaBrowser.Api.Library
             }
         }
 
+        /// <summary>
+        /// Posts the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        public void Post(UpdateMediaPath request)
+        {
+            if (string.IsNullOrWhiteSpace(request.Name))
+            {
+                throw new ArgumentNullException("request");
+            }
+
+            _libraryManager.UpdateMediaPath(request.Name, request.PathInfo);
+        }
+
         /// <summary>
         /// Deletes the specified request.
         /// </summary>

+ 129 - 8
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -12,9 +12,14 @@ using ServiceStack;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Api.Playback.Progressive;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
 
 namespace MediaBrowser.Api.LiveTv
 {
@@ -44,6 +49,21 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? StartIndex { get; set; }
 
+        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsMovie { get; set; }
+
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
+
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
+        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsKids { get; set; }
+
+        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSports { get; set; }
+
         /// <summary>
         /// The maximum number of items to return
         /// </summary>
@@ -85,6 +105,26 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableUserData { get; set; }
 
+        public string SortBy { get; set; }
+
+        public SortOrder? SortOrder { get; set; }
+
+        /// <summary>
+        /// Gets the order by.
+        /// </summary>
+        /// <returns>IEnumerable{ItemSortBy}.</returns>
+        public string[] GetOrderBy()
+        {
+            var val = SortBy;
+
+            if (string.IsNullOrEmpty(val))
+            {
+                return new string[] { };
+            }
+
+            return val.Split(',');
+        }
+
         public GetChannels()
         {
             AddCurrentProgram = true;
@@ -159,6 +199,7 @@ namespace MediaBrowser.Api.LiveTv
         public bool? IsSeries { get; set; }
         public bool? IsKids { get; set; }
         public bool? IsSports { get; set; }
+        public bool? IsNews { get; set; }
 
         public GetRecordings()
         {
@@ -275,6 +316,8 @@ namespace MediaBrowser.Api.LiveTv
         public string SeriesTimerId { get; set; }
 
         public bool? IsActive { get; set; }
+
+        public bool? IsScheduled { get; set; }
     }
 
     [Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
@@ -305,6 +348,12 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsMovie { get; set; }
 
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
+
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
         [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsKids { get; set; }
 
@@ -340,6 +389,8 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableUserData { get; set; }
 
+        public string SeriesTimerId { get; set; }
+
         /// <summary>
         /// Fields to return within the items, in addition to basic information
         /// </summary>
@@ -376,15 +427,21 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool? HasAired { get; set; }
 
-        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
-        public bool? IsSports { get; set; }
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
 
-        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsMovie { get; set; }
 
-        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
+        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsKids { get; set; }
 
+        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSports { get; set; }
+
         [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableImages { get; set; }
 
@@ -613,16 +670,30 @@ namespace MediaBrowser.Api.LiveTv
 
     }
 
+    [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
+    public class GetLiveStreamFile
+    {
+        public string Id { get; set; }
+        public string Container { get; set; }
+    }
+
+    [Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")]
+    public class GetLiveRecordingFile
+    {
+        public string Id { get; set; }
+    }
+
     public class LiveTvService : BaseApiService
     {
         private readonly ILiveTvManager _liveTvManager;
         private readonly IUserManager _userManager;
-        private readonly IConfigurationManager _config;
+        private readonly IServerConfigurationManager _config;
         private readonly IHttpClient _httpClient;
         private readonly ILibraryManager _libraryManager;
         private readonly IDtoService _dtoService;
+        private readonly IFileSystem _fileSystem;
 
-        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService)
+        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
         {
             _liveTvManager = liveTvManager;
             _userManager = userManager;
@@ -630,6 +701,41 @@ namespace MediaBrowser.Api.LiveTv
             _httpClient = httpClient;
             _libraryManager = libraryManager;
             _dtoService = dtoService;
+            _fileSystem = fileSystem;
+        }
+
+        public async Task<object> Get(GetLiveRecordingFile request)
+        {
+            var path = EmbyTV.Current.GetActiveRecordingPath(request.Id);
+
+            if (path == null)
+            {
+                throw new FileNotFoundException();
+            }
+
+            var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
+
+            var streamSource = new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
+            {
+                AllowEndOfFile = false
+            };
+            return ResultFactory.GetAsyncStreamWriter(streamSource);
+        }
+
+        public async Task<object> Get(GetLiveStreamFile request)
+        {
+            var directStreamProvider = (await EmbyTV.Current.GetLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
+            var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
+
+            var streamSource = new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+            {
+                AllowEndOfFile = false
+            };
+            return ResultFactory.GetAsyncStreamWriter(streamSource);
         }
 
         public object Get(GetDefaultListingProvider request)
@@ -702,7 +808,8 @@ namespace MediaBrowser.Api.LiveTv
 
             var response = await _httpClient.Get(new HttpRequestOptions
             {
-                Url = "https://json.schedulesdirect.org/20141201/available/countries"
+                Url = "https://json.schedulesdirect.org/20141201/available/countries",
+                BufferContent = false
 
             }).ConfigureAwait(false);
 
@@ -786,6 +893,13 @@ namespace MediaBrowser.Api.LiveTv
                 IsLiked = request.IsLiked,
                 IsDisliked = request.IsDisliked,
                 EnableFavoriteSorting = request.EnableFavoriteSorting,
+                IsMovie = request.IsMovie,
+                IsSeries = request.IsSeries,
+                IsNews = request.IsNews,
+                IsKids = request.IsKids,
+                IsSports = request.IsSports,
+                SortBy = request.GetOrderBy(),
+                SortOrder = request.SortOrder ?? SortOrder.Ascending,
                 AddCurrentProgram = request.AddCurrentProgram
 
             }, CancellationToken.None).ConfigureAwait(false);
@@ -868,9 +982,12 @@ namespace MediaBrowser.Api.LiveTv
             query.Limit = request.Limit;
             query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
             query.SortOrder = request.SortOrder;
+            query.IsNews = request.IsNews;
             query.IsMovie = request.IsMovie;
+            query.IsSeries = request.IsSeries;
             query.IsKids = request.IsKids;
             query.IsSports = request.IsSports;
+            query.SeriesTimerId = request.SeriesTimerId;
             query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
 
             var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
@@ -886,8 +1003,10 @@ namespace MediaBrowser.Api.LiveTv
                 IsAiring = request.IsAiring,
                 Limit = request.Limit,
                 HasAired = request.HasAired,
+                IsSeries = request.IsSeries,
                 IsMovie = request.IsMovie,
                 IsKids = request.IsKids,
+                IsNews = request.IsNews,
                 IsSports = request.IsSports,
                 EnableTotalRecordCount = request.EnableTotalRecordCount
             };
@@ -919,6 +1038,7 @@ namespace MediaBrowser.Api.LiveTv
                 IsInProgress = request.IsInProgress,
                 EnableTotalRecordCount = request.EnableTotalRecordCount,
                 IsMovie = request.IsMovie,
+                IsNews = request.IsNews,
                 IsSeries = request.IsSeries,
                 IsKids = request.IsKids,
                 IsSports = request.IsSports
@@ -975,7 +1095,8 @@ namespace MediaBrowser.Api.LiveTv
             {
                 ChannelId = request.ChannelId,
                 SeriesTimerId = request.SeriesTimerId,
-                IsActive = request.IsActive
+                IsActive = request.IsActive,
+                IsScheduled = request.IsScheduled
 
             }, CancellationToken.None).ConfigureAwait(false);
 

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

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -11,9 +11,10 @@
     <AssemblyName>MediaBrowser.Api</AssemblyName>
     <FileAlignment>512</FileAlignment>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
     <ReleaseVersion>
     </ReleaseVersion>
+    <TargetFrameworkProfile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -79,7 +80,6 @@
     <Compile Include="Dlna\DlnaService.cs" />
     <Compile Include="FilterService.cs" />
     <Compile Include="IHasDtoOptions.cs" />
-    <Compile Include="Library\ChapterService.cs" />
     <Compile Include="Playback\MediaInfoService.cs" />
     <Compile Include="Playback\TranscodingThrottler.cs" />
     <Compile Include="PlaylistService.cs" />
@@ -198,6 +198,10 @@
       <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
       <Name>MediaBrowser.Model</Name>
     </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
+      <Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
+      <Name>MediaBrowser.Server.Implementations</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />

+ 14 - 8
MediaBrowser.Api/Movies/MoviesService.cs

@@ -156,18 +156,19 @@ namespace MediaBrowser.Api.Movies
                 itemTypes.Add(typeof(LiveTvProgram).Name);
             }
 
+            var dtoOptions = GetDtoOptions(request);
+
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
                 Limit = request.Limit,
                 IncludeItemTypes = itemTypes.ToArray(),
                 IsMovie = true,
                 SimilarTo = item,
-                EnableGroupByMetadataKey = true
+                EnableGroupByMetadataKey = true,
+                DtoOptions = dtoOptions
 
             }).ToList();
 
-            var dtoOptions = GetDtoOptions(request);
-
             var result = new QueryResult<BaseItemDto>
             {
                 Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
@@ -198,7 +199,8 @@ namespace MediaBrowser.Api.Movies
                 Limit = 7,
                 ParentId = parentIdGuid,
                 Recursive = true,
-                IsPlayed = true
+                IsPlayed = true,
+                DtoOptions = dtoOptions
             };
 
             var recentlyPlayedMovies = _libraryManager.GetItemList(query).ToList();
@@ -221,7 +223,8 @@ namespace MediaBrowser.Api.Movies
                 ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(),
                 EnableGroupByMetadataKey = true,
                 ParentId = parentIdGuid,
-                Recursive = true
+                Recursive = true,
+                DtoOptions = dtoOptions
 
             }).ToList();
 
@@ -302,7 +305,8 @@ namespace MediaBrowser.Api.Movies
                     PersonTypes = new[] { PersonType.Director },
                     IncludeItemTypes = itemTypes.ToArray(),
                     IsMovie = true,
-                    EnableGroupByMetadataKey = true
+                    EnableGroupByMetadataKey = true,
+                    DtoOptions = dtoOptions
 
                 }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
                 .Take(itemLimit)
@@ -339,7 +343,8 @@ namespace MediaBrowser.Api.Movies
                     Limit = itemLimit + 2,
                     IncludeItemTypes = itemTypes.ToArray(),
                     IsMovie = true,
-                    EnableGroupByMetadataKey = true
+                    EnableGroupByMetadataKey = true,
+                    DtoOptions = dtoOptions
 
                 }).DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
                 .Take(itemLimit)
@@ -375,7 +380,8 @@ namespace MediaBrowser.Api.Movies
                     IncludeItemTypes = itemTypes.ToArray(),
                     IsMovie = true,
                     SimilarTo = item,
-                    EnableGroupByMetadataKey = true
+                    EnableGroupByMetadataKey = true,
+                    DtoOptions = dtoOptions
 
                 }).ToList();
 

+ 4 - 4
MediaBrowser.Api/PackageService.cs

@@ -46,7 +46,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The name.</value>
         [ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public PackageType? PackageType { get; set; }
+        public string PackageType { get; set; }
 
         [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET", AllowMultiple = true)]
         public string TargetSystems { get; set; }
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The name.</value>
         [ApiMember(Name = "PackageType", Description = "Package type filter (System/UserInstalled)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public PackageType PackageType { get; set; }
+        public string PackageType { get; set; }
     }
 
     /// <summary>
@@ -149,12 +149,12 @@ namespace MediaBrowser.Api
         {
             var result = new List<PackageVersionInfo>();
 
-            if (request.PackageType == PackageType.UserInstalled || request.PackageType == PackageType.All)
+            if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
             {
                 result.AddRange(_installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToList());
             }
 
-            else if (request.PackageType == PackageType.System || request.PackageType == PackageType.All)
+            else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
             {
                 var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new Progress<double>()).Result;
 

+ 63 - 23
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -367,6 +367,8 @@ namespace MediaBrowser.Api.Playback
                 {
                     param += " -crf 23";
                 }
+
+                param += " -tune zerolatency";
             }
 
             else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@@ -461,13 +463,15 @@ namespace MediaBrowser.Api.Playback
                 var level = NormalizeTranscodingLevel(state.OutputVideoCodec, state.VideoRequest.Level);
 
                 // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
+                // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
                 if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
-                    string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+                    string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
+                    string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
                 {
                     switch (level)
                     {
                         case "30":
-                            param += " -level 3";
+                            param += " -level 3.0";
                             break;
                         case "31":
                             param += " -level 3.1";
@@ -476,7 +480,7 @@ namespace MediaBrowser.Api.Playback
                             param += " -level 3.2";
                             break;
                         case "40":
-                            param += " -level 4";
+                            param += " -level 4.0";
                             break;
                         case "41":
                             param += " -level 4.1";
@@ -485,7 +489,7 @@ namespace MediaBrowser.Api.Playback
                             param += " -level 4.2";
                             break;
                         case "50":
-                            param += " -level 5";
+                            param += " -level 5.0";
                             break;
                         case "51":
                             param += " -level 5.1";
@@ -767,7 +771,20 @@ namespace MediaBrowser.Api.Playback
             if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
             {
                 outputSizeParam = GetOutputSizeParam(state, outputVideoCodec).TrimEnd('"');
-                outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
+
+                if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+                {
+                    outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase));
+                }
+                else
+                {
+                    outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
+                }
+            }
+
+            if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) && outputSizeParam.Length == 0)
+            {
+                outputSizeParam = ",format=nv12|vaapi,hwupload";
             }
 
             var videoSizeParam = string.Empty;
@@ -802,10 +819,10 @@ namespace MediaBrowser.Api.Playback
         {
             if (state.PlayableStreamFileNames.Count > 0)
             {
-                return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
+                return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
             }
 
-            return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol);
+            return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol);
         }
 
         /// <summary>
@@ -1022,7 +1039,15 @@ namespace MediaBrowser.Api.Playback
                 var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
                 if (GetVideoEncoder(state).IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)
                 {
-                    arg = "-hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
+                    var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+                    var hwOutputFormat = "vaapi";
+
+                    if (hasGraphicalSubs)
+                    {
+                        hwOutputFormat = "yuv420p";
+                    }
+
+                    arg = "-hwaccel vaapi -hwaccel_output_format " + hwOutputFormat + " -vaapi_device " + encodingOptions.VaapiDevice + " " + arg;
                 }
             }
 
@@ -1219,7 +1244,7 @@ namespace MediaBrowser.Api.Playback
 
         private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
         {
-            if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+            if (EnableThrottling(state))
             {
                 transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
                 state.TranscodingThrottler.Start();
@@ -1832,26 +1857,37 @@ namespace MediaBrowser.Api.Playback
 
             state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
 
-            var archivable = item as IArchivable;
-            state.IsInputArchive = archivable != null && archivable.IsArchive;
-
-            MediaSourceInfo mediaSource;
+            MediaSourceInfo mediaSource = null;
             if (string.IsNullOrWhiteSpace(request.LiveStreamId))
             {
-                var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
+                TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
+                    ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
+                    : null;
 
-                mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
-                   ? mediaSources.First()
-                   : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
+                if (currentJob != null)
+                {
+                    mediaSource = currentJob.MediaSource;
+                }
 
-                if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+                if (mediaSource == null)
                 {
-                    mediaSource = mediaSources.First();
+                    var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList();
+
+                    mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
+                       ? mediaSources.First()
+                       : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId));
+
+                    if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+                    {
+                        mediaSource = mediaSources.First();
+                    }
                 }
             }
             else
             {
-                mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
+                var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false);
+                mediaSource = liveStreamInfo.Item1;
+                state.DirectStreamProvider = liveStreamInfo.Item2;
             }
 
             var videoRequest = request as VideoStreamRequest;
@@ -2294,7 +2330,8 @@ namespace MediaBrowser.Api.Playback
                 state.TargetRefFrames,
                 state.TargetVideoStreamCount,
                 state.TargetAudioStreamCount,
-                state.TargetVideoCodecTag);
+                state.TargetVideoCodecTag,
+                state.IsTargetAVC);
 
             if (mediaProfile != null)
             {
@@ -2429,7 +2466,8 @@ namespace MediaBrowser.Api.Playback
                 Url = "https://mb3admin.com/admin/service/transcoding/report",
                 CancellationToken = CancellationToken.None,
                 LogRequest = false,
-                LogErrors = false
+                LogErrors = false,
+                BufferContent = false
             };
             options.RequestContent = JsonSerializer.SerializeToString(dict);
             options.RequestContentType = "application/json";
@@ -2512,7 +2550,8 @@ namespace MediaBrowser.Api.Playback
                     state.TargetRefFrames,
                     state.TargetVideoStreamCount,
                     state.TargetAudioStreamCount,
-                    state.TargetVideoCodecTag
+                    state.TargetVideoCodecTag,
+                    state.IsTargetAVC
 
                     ).FirstOrDefault() ?? string.Empty;
             }
@@ -2567,6 +2606,7 @@ namespace MediaBrowser.Api.Playback
             inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
             inputModifier = inputModifier.Trim();
 
+            //inputModifier += " -fflags +genpts+ignidx+igndts";
             if (state.VideoRequest != null && genPts)
             {
                 inputModifier += " -fflags +genpts";

+ 31 - 29
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls
 
             if (!FileSystem.FileExists(playlist))
             {
-                await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+                var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist);
+                await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
                 try
                 {
                     if (!FileSystem.FileExists(playlist))
@@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls
                             throw;
                         }
 
-                        var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
+                        var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3);
                         await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
                     }
                 }
                 finally
                 {
-                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
+                    transcodingLock.Release();
                 }
             }
 
@@ -128,10 +129,9 @@ namespace MediaBrowser.Api.Playback.Hls
             var audioBitrate = state.OutputAudioBitrate ?? 0;
             var videoBitrate = state.OutputVideoBitrate ?? 0;
 
-            var appendBaselineStream = false;
             var baselineStreamBitrate = 64000;
 
-            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);
+            var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, baselineStreamBitrate);
 
             job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType);
 
@@ -151,9 +151,10 @@ namespace MediaBrowser.Api.Playback.Hls
                 {
                     var text = reader.ReadToEnd();
 
+                    text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT");
+
                     var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
 
-                    // ffmpeg pads the reported length by a full second
                     text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
 
                     return text;
@@ -161,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Hls
             }
         }
 
-        private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
+        private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, int baselineStreamBitrate)
         {
             var builder = new StringBuilder();
 
@@ -175,14 +176,6 @@ namespace MediaBrowser.Api.Playback.Hls
             var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8");
             builder.AppendLine(playlistUrl);
 
-            // Low bitrate stream
-            if (includeBaselineStream)
-            {
-                builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + baselineStreamBitrate.ToString(UsCulture));
-                playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8");
-                builder.AppendLine(playlistUrl);
-            }
-
             return builder.ToString();
         }
 
@@ -190,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
 
-            while (true)
+            while (!cancellationToken.IsCancellationRequested)
             {
-                // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
-                using (var fileStream = GetPlaylistFileStream(playlist))
+                try
                 {
-                    using (var reader = new StreamReader(fileStream))
+                    // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
+                    using (var fileStream = GetPlaylistFileStream(playlist))
                     {
-                        var count = 0;
-
-                        while (!reader.EndOfStream)
+                        using (var reader = new StreamReader(fileStream))
                         {
-                            var line = await reader.ReadLineAsync().ConfigureAwait(false);
+                            var count = 0;
 
-                            if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
+                            while (!reader.EndOfStream)
                             {
-                                count++;
-                                if (count >= segmentCount)
+                                var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+                                if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
                                 {
-                                    Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
-                                    return;
+                                    count++;
+                                    if (count >= segmentCount)
+                                    {
+                                        Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
+                                        return;
+                                    }
                                 }
                             }
+                            await Task.Delay(100, cancellationToken).ConfigureAwait(false);
                         }
-                        await Task.Delay(100, cancellationToken).ConfigureAwait(false);
                     }
                 }
+                catch (IOException)
+                {
+                    // May get an error if the file is locked
+                }
+
+                await Task.Delay(50, cancellationToken).ConfigureAwait(false);
             }
         }
 

+ 5 - 3
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls
                 return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
             }
 
-            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+            var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath);
+            await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
             var released = false;
             try
             {
                 if (FileSystem.FileExists(segmentPath))
                 {
                     job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
-                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
+                    transcodingLock.Release();
                     released = true;
                     return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
                 }
@@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
             {
                 if (!released)
                 {
-                    ApiEntryPoint.Instance.TranscodingStartLock.Release();
+                    transcodingLock.Release();
                 }
             }
 
@@ -906,6 +907,7 @@ namespace MediaBrowser.Api.Playback.Hls
                     ).Trim();
             }
 
+            // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
             return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
                             inputModifier,
                             GetInputArgument(state),

+ 1 - 3
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs

@@ -68,8 +68,6 @@ namespace MediaBrowser.Api.Playback.Hls
     [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
     public class GetHlsVideoSegmentLegacy : VideoStreamRequest
     {
-        // TODO: Deprecate with new iOS app
-
         public string PlaylistId { get; set; }
 
         /// <summary>
@@ -113,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Hls
             var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
             file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file);
 
-            var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty);
+            var normalizedPlaylistId = request.PlaylistId;
 
             var playlistPath = Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*")
                 .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);

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

@@ -113,6 +113,8 @@ namespace MediaBrowser.Api.Playback.Hls
                 args += GetGraphicalSubtitleParam(state, codec);
             }
 
+            args += " -flags -global_header";
+
             return args;
         }
 

+ 2 - 2
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Playback
         {
             var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
 
-            var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false);
+            var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false);
 
             var profile = request.DeviceProfile;
             if (profile == null)
@@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback
 
         public void Post(CloseMediaSource request)
         {
-            var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None);
+            var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
             Task.WaitAll(task);
         }
 

+ 37 - 6
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -17,6 +17,7 @@ using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using ServiceStack;
 
 namespace MediaBrowser.Api.Playback.Progressive
 {
@@ -26,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
     public abstract class BaseProgressiveStreamingService : BaseStreamingService
     {
         protected readonly IImageProcessor ImageProcessor;
-        protected readonly IHttpClient HttpClient;
 
         protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
         {
             ImageProcessor = imageProcessor;
-            HttpClient = httpClient;
         }
 
         /// <summary>
@@ -122,6 +121,25 @@ namespace MediaBrowser.Api.Playback.Progressive
 
             var responseHeaders = new Dictionary<string, string>();
 
+            if (request.Static && state.DirectStreamProvider != null)
+            {
+                AddDlnaHeaders(state, responseHeaders, true);
+
+                using (state)
+                {
+                    var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+                    // TODO: Don't hardcode this
+                    outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
+
+                    var streamSource = new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+                    {
+                        AllowEndOfFile = false
+                    };
+                    return ResultFactory.GetAsyncStreamWriter(streamSource);
+                }
+            }
+
             // Static remote stream
             if (request.Static && state.InputProtocol == MediaProtocol.Http)
             {
@@ -129,8 +147,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
                 using (state)
                 {
-                    return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource)
-                                .ConfigureAwait(false);
+                    return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false);
                 }
             }
 
@@ -154,6 +171,19 @@ namespace MediaBrowser.Api.Playback.Progressive
 
                 using (state)
                 {
+                    if (state.MediaSource.IsInfiniteStream)
+                    {
+                        var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+                        outputHeaders["Content-Type"] = contentType;
+
+                        var streamSource = new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
+                        {
+                            AllowEndOfFile = false
+                        };
+                        return ResultFactory.GetAsyncStreamWriter(streamSource);
+                    }
+
                     TimeSpan? cacheDuration = null;
 
                     if (!string.IsNullOrEmpty(request.Tag))
@@ -345,7 +375,8 @@ namespace MediaBrowser.Api.Playback.Progressive
                 return streamResult;
             }
 
-            await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+            var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
+            await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
             try
             {
                 TranscodingJob job;
@@ -376,7 +407,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
             finally
             {
-                ApiEntryPoint.Instance.TranscodingStartLock.Release();
+                transcodingLock.Release();
             }
         }
 

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

@@ -7,6 +7,7 @@ using CommonIO;
 using MediaBrowser.Controller.Net;
 using System.Collections.Generic;
 using ServiceStack.Web;
+using MediaBrowser.Controller.Library;
 
 namespace MediaBrowser.Api.Playback.Progressive
 {
@@ -23,6 +24,10 @@ namespace MediaBrowser.Api.Playback.Progressive
         private const int BufferSize = 81920;
 
         private long _bytesWritten = 0;
+        public long StartPosition { get; set; }
+        public bool AllowEndOfFile = true;
+
+        private IDirectStreamProvider _directStreamProvider;
 
         public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
         {
@@ -34,6 +39,15 @@ namespace MediaBrowser.Api.Playback.Progressive
             _cancellationToken = cancellationToken;
         }
 
+        public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
+        {
+            _directStreamProvider = directStreamProvider;
+            _outputHeaders = outputHeaders;
+            _job = job;
+            _logger = logger;
+            _cancellationToken = cancellationToken;
+        }
+
         public IDictionary<string, string> Options
         {
             get
@@ -42,17 +56,33 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
         }
 
+        private Stream GetInputStream()
+        {
+            return _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+        }
+
         public async Task WriteToAsync(Stream outputStream)
         {
             try
             {
+                if (_directStreamProvider != null)
+                {
+                    await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false);
+                    return;
+                }
+
                 var eofCount = 0;
 
-                using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                using (var inputStream = GetInputStream())
                 {
-                    while (eofCount < 15)
+                    if (StartPosition > 0)
+                    {
+                        inputStream.Position = StartPosition;
+                    }
+
+                    while (eofCount < 15 || !AllowEndOfFile)
                     {
-                        var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
+                        var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
 
                         //var position = fs.Position;
                         //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);

+ 23 - 2
MediaBrowser.Api/Playback/StreamState.cs

@@ -37,6 +37,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <value>The log file stream.</value>
         public Stream LogFileStream { get; set; }
+        public IDirectStreamProvider DirectStreamProvider { get; set; }
 
         public string InputContainer { get; set; }
 
@@ -62,7 +63,6 @@ namespace MediaBrowser.Api.Playback
             get { return Request is VideoStreamRequest; }
         }
         public bool IsInputVideo { get; set; }
-        public bool IsInputArchive { get; set; }
 
         public VideoType VideoType { get; set; }
         public IsoType? IsoType { get; set; }
@@ -88,9 +88,17 @@ namespace MediaBrowser.Api.Playback
                         return 10;
                     }
 
+                    if (!RunTimeTicks.HasValue)
+                    {
+                        return 3;
+                    }
                     return 6;
                 }
 
+                if (!RunTimeTicks.HasValue)
+                {
+                    return 3;
+                }
                 return 3;
             }
         }
@@ -217,7 +225,7 @@ namespace MediaBrowser.Api.Playback
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -508,5 +516,18 @@ namespace MediaBrowser.Api.Playback
                 return false;
             }
         }
+
+        public bool? IsTargetAVC
+        {
+            get
+            {
+                if (Request.Static)
+                {
+                    return VideoStream == null ? null : VideoStream.IsAVC;
+                }
+
+                return true;
+            }
+        }
     }
 }

+ 0 - 4
MediaBrowser.Api/Reports/Data/ReportBuilder.cs

@@ -517,10 +517,6 @@ namespace MediaBrowser.Api.Reports
                     internalHeader = HeaderMetadata.Album;
                     break;
 
-                case HeaderMetadata.Countries:
-                    option.Column = (i, r) => this.GetListAsString(this.GetObject<IHasProductionLocations, List<string>>(i, (x) => x.ProductionLocations));
-                    break;
-
                 case HeaderMetadata.Disc:
                     option.Column = (i, r) => i.ParentIndexNumber;
                     break;

+ 0 - 25
MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Api.Reports
             result = this.GetResultStudios(result, items, topItem);
             result = this.GetResultPersons(result, items, topItem);
             result = this.GetResultProductionYears(result, items, topItem);
-            result = this.GetResulProductionLocations(result, items, topItem);
             result = this.GetResultCommunityRatings(result, items, topItem);
             result = this.GetResultParentalRatings(result, items, topItem);
 
@@ -100,30 +99,6 @@ namespace MediaBrowser.Api.Reports
             }
         }
 
-        /// <summary> Gets resul production locations. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The resul production locations. </returns>
-        private ReportStatResult GetResulProductionLocations(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Countries), topItem,
-                        items.OfType<IHasProductionLocations>()
-                        .Where(x => x.ProductionLocations != null)
-                        .SelectMany(x => x.ProductionLocations)
-                        .GroupBy(x => x)
-                        .OrderByDescending(x => x.Count())
-                        .Take(topItem)
-                        .Select(x => new ReportStatItem
-                        {
-                            Name = x.Key.ToString(),
-                            Value = x.Count().ToString()
-                        })
-            );
-
-            return result;
-        }
-
         /// <summary> Gets result community ratings. </summary>
         /// <param name="result"> The result. </param>
         /// <param name="items"> The items. </param>

+ 2 - 1
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -81,7 +81,8 @@ namespace MediaBrowser.Api
             var query = new InternalItemsQuery(user)
             {
                 IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(),
-                Recursive = true
+                Recursive = true,
+                DtoOptions = dtoOptions
             };
 
             // ExcludeArtistIds

+ 4 - 7
MediaBrowser.Api/StartupWizardService.cs

@@ -88,8 +88,6 @@ namespace MediaBrowser.Api
             var result = new StartupConfiguration
             {
                 UICulture = _config.Configuration.UICulture,
-                EnableInternetProviders = _config.Configuration.EnableInternetProviders,
-                SaveLocalMeta = _config.Configuration.SaveLocalMeta,
                 MetadataCountryCode = _config.Configuration.MetadataCountryCode,
                 PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
             };
@@ -116,15 +114,16 @@ namespace MediaBrowser.Api
             config.EnableLocalizedGuids = true;
             config.EnableStandaloneMusicKeys = true;
             config.EnableCaseSensitiveItemIds = true;
-            //config.EnableFolderView = true;
+            config.EnableFolderView = true;
             config.SchemaVersion = 109;
+            config.EnableSimpleArtistDetection = true;
+            config.SkipDeserializationForBasicTypes = true;
+            config.SkipDeserializationForPrograms = true;
         }
 
         public void Post(UpdateStartupConfiguration request)
         {
             _config.Configuration.UICulture = request.UICulture;
-            _config.Configuration.EnableInternetProviders = request.EnableInternetProviders;
-            _config.Configuration.SaveLocalMeta = request.SaveLocalMeta;
             _config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
             _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
             _config.SaveConfiguration();
@@ -215,8 +214,6 @@ namespace MediaBrowser.Api
     public class StartupConfiguration
     {
         public string UICulture { get; set; }
-        public bool EnableInternetProviders { get; set; }
-        public bool SaveLocalMeta { get; set; }
         public string MetadataCountryCode { get; set; }
         public string PreferredMetadataLanguage { get; set; }
         public string LiveTvTunerType { get; set; }

+ 1 - 1
MediaBrowser.Api/Subtitles/SubtitleService.cs

@@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Subtitles
         {
             var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
 
-            var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false);
+            var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);
 
             var builder = new StringBuilder();
 

+ 8 - 6
MediaBrowser.Api/TvShowsService.cs

@@ -302,6 +302,8 @@ namespace MediaBrowser.Api
                 (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
 
+            var dtoOptions = GetDtoOptions(request);
+
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
                 Limit = request.Limit,
@@ -309,12 +311,11 @@ namespace MediaBrowser.Api
                 {
                         typeof(Series).Name
                 },
-                SimilarTo = item
+                SimilarTo = item,
+                DtoOptions = dtoOptions
 
             }).ToList();
 
-            var dtoOptions = GetDtoOptions(request);
-
             var result = new QueryResult<BaseItemDto>
             {
                 Items = (await _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user).ConfigureAwait(false)).ToArray(),
@@ -333,6 +334,8 @@ namespace MediaBrowser.Api
 
             var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
 
+            var options = GetDtoOptions(request);
+
             var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(Episode).Name },
@@ -342,12 +345,11 @@ namespace MediaBrowser.Api
                 StartIndex = request.StartIndex,
                 Limit = request.Limit,
                 ParentId = parentIdGuid,
-                Recursive = true
+                Recursive = true,
+                DtoOptions = options
 
             }).ToList();
 
-            var options = GetDtoOptions(request);
-
             var returnItems = (await _dtoService.GetBaseItemDtos(itemsResult, options, user).ConfigureAwait(false)).ToArray();
 
             var result = new ItemsResult

+ 1 - 0
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -205,6 +205,7 @@ namespace MediaBrowser.Api.UserLibrary
         private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
         {
             dto.ChildCount = counts.ItemCount;
+            dto.ProgramCount = counts.ProgramCount;
             dto.SeriesCount = counts.SeriesCount;
             dto.EpisodeCount = counts.EpisodeCount;
             dto.MovieCount = counts.MovieCount;

+ 22 - 14
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -99,8 +99,10 @@ namespace MediaBrowser.Api.UserLibrary
         private async Task<ItemsResult> GetItems(GetItems request)
         {
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-        
-            var result = await GetQueryResult(request, user).ConfigureAwait(false);
+
+            var dtoOptions = GetDtoOptions(request);
+
+            var result = await GetQueryResult(request, dtoOptions, user).ConfigureAwait(false);
 
             if (result == null)
             {
@@ -112,8 +114,6 @@ namespace MediaBrowser.Api.UserLibrary
                 throw new InvalidOperationException("GetItemsToSerialize result.Items returned null");
             }
 
-            var dtoOptions = GetDtoOptions(request);
-
             var dtoList = await _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ConfigureAwait(false);
 
             if (dtoList == null)
@@ -131,24 +131,31 @@ namespace MediaBrowser.Api.UserLibrary
         /// <summary>
         /// Gets the items to serialize.
         /// </summary>
-        /// <param name="request">The request.</param>
-        /// <param name="user">The user.</param>
-        /// <returns>IEnumerable{BaseItem}.</returns>
-        private async Task<QueryResult<BaseItem>> GetQueryResult(GetItems request, User user)
+        private async Task<QueryResult<BaseItem>> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
         {
             var item = string.IsNullOrEmpty(request.ParentId) ?
-                user == null ? _libraryManager.RootFolder : user.RootFolder :
+                null :
                 _libraryManager.GetItemById(request.ParentId);
 
             if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
             {
-                //item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+                if (item == null || user != null)
+                {
+                    item = _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, "PlaylistsFolder", StringComparison.OrdinalIgnoreCase));
+                }
             }
             else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
             {
                 item = user == null ? _libraryManager.RootFolder : user.RootFolder;
             }
 
+            if (item == null)
+            {
+                item = string.IsNullOrEmpty(request.ParentId) ?
+                    user == null ? _libraryManager.RootFolder : user.RootFolder :
+                    _libraryManager.GetItemById(request.ParentId);
+            }
+
             // Default list type = children
 
             var folder = item as Folder;
@@ -159,14 +166,14 @@ namespace MediaBrowser.Api.UserLibrary
 
             if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
             {
-                return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+                return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
             }
 
             var userRoot = item as UserRootFolder;
 
             if (userRoot == null)
             {
-                return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+                return await folder.GetItems(GetItemsQuery(request, dtoOptions, user)).ConfigureAwait(false);
             }
 
             IEnumerable<BaseItem> items = folder.GetChildren(user, true);
@@ -180,7 +187,7 @@ namespace MediaBrowser.Api.UserLibrary
             };
         }
 
-        private InternalItemsQuery GetItemsQuery(GetItems request, User user)
+        private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, User user)
         {
             var query = new InternalItemsQuery(user)
             {
@@ -241,7 +248,8 @@ namespace MediaBrowser.Api.UserLibrary
                 AiredDuringSeason = request.AiredDuringSeason,
                 AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater,
                 EnableTotalRecordCount = request.EnableTotalRecordCount,
-                ExcludeItemIds = request.GetExcludeItemIds()
+                ExcludeItemIds = request.GetExcludeItemIds(),
+                DtoOptions = dtoOptions
             };
 
             if (!string.IsNullOrWhiteSpace(request.Ids))

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

@@ -30,6 +30,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Common.Implementations
 {
@@ -192,6 +193,8 @@ namespace MediaBrowser.Common.Implementations
             get { return Environment.OSVersion.VersionString; }
         }
 
+        public IMemoryStreamProvider MemoryStreamProvider { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
         /// </summary>
@@ -231,6 +234,8 @@ namespace MediaBrowser.Common.Implementations
 
             JsonSerializer = CreateJsonSerializer();
 
+            MemoryStreamProvider = new MemoryStreamProvider();
+
             OnLoggerLoaded(true);
             LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
 
@@ -456,6 +461,7 @@ namespace MediaBrowser.Common.Implementations
 
 			RegisterSingleInstance(JsonSerializer);
 			RegisterSingleInstance(XmlSerializer);
+            RegisterSingleInstance(MemoryStreamProvider);
 
 			RegisterSingleInstance(LogManager);
 			RegisterSingleInstance(Logger);
@@ -464,7 +470,7 @@ namespace MediaBrowser.Common.Implementations
 
 			RegisterSingleInstance(FileSystemManager);
 
-            HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager);
+            HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
 			RegisterSingleInstance(HttpClient);
 
 			NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));

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

@@ -42,6 +42,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         private readonly IApplicationPaths _appPaths;
 
         private readonly IFileSystem _fileSystem;
+        private readonly IMemoryStreamProvider _memoryStreamProvider;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="HttpClientManager" /> class.
@@ -52,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         /// <exception cref="System.ArgumentNullException">appPaths
         /// or
         /// logger</exception>
-        public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
+        public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
         {
             if (appPaths == null)
             {
@@ -65,6 +66,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             _logger = logger;
             _fileSystem = fileSystem;
+            _memoryStreamProvider = memoryStreamProvider;
             _appPaths = appPaths;
 
             // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
@@ -269,6 +271,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
                 Url = url,
                 ResourcePool = resourcePool,
                 CancellationToken = cancellationToken,
+                BufferContent = resourcePool != null
             });
         }
 
@@ -293,12 +296,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         /// </exception>
         public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
         {
-            HttpResponseInfo response;
-
             if (options.CacheMode == CacheMode.None)
             {
-                response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
-                return response;
+                return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
             }
 
             var url = options.Url;
@@ -306,7 +306,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
 
-            response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
+            var response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
             if (response != null)
             {
                 return response;
@@ -332,7 +332,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
                 {
                     using (var stream = _fileSystem.GetFileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
                     {
-                        var memoryStream = new MemoryStream();
+                        var memoryStream = _memoryStreamProvider.CreateNew();
 
                         await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
                         memoryStream.Position = 0;
@@ -366,7 +366,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             using (var responseStream = response.Content)
             {
-                var memoryStream = new MemoryStream();
+                var memoryStream = _memoryStreamProvider.CreateNew();
                 await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
                 memoryStream.Position = 0;
 
@@ -458,7 +458,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
                     using (var stream = httpResponse.GetResponseStream())
                     {
-                        var memoryStream = new MemoryStream();
+                        var memoryStream = _memoryStreamProvider.CreateNew();
 
                         await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
 
@@ -553,7 +553,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
             {
                 Url = url,
                 ResourcePool = resourcePool,
-                CancellationToken = cancellationToken
+                CancellationToken = cancellationToken,
+                BufferContent = resourcePool != null
 
             }, postData);
         }
@@ -563,7 +564,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         /// </summary>
         /// <param name="options">The options.</param>
         /// <returns>Task{System.String}.</returns>
-        /// <exception cref="System.ArgumentNullException">progress</exception>
         public async Task<string> GetTempFile(HttpRequestOptions options)
         {
             var response = await GetTempFileResponse(options).ConfigureAwait(false);

+ 31 - 0
MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using Microsoft.IO;
+
+namespace MediaBrowser.Common.Implementations.IO
+{
+    public class MemoryStreamProvider : IMemoryStreamProvider
+    {
+        readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager();
+
+        public MemoryStream CreateNew()
+        {
+            return _manager.GetStream();
+        }
+
+        public MemoryStream CreateNew(int capacity)
+        {
+            return _manager.GetStream("RecyclableMemoryStream", capacity);
+        }
+
+        public MemoryStream CreateNew(byte[] buffer)
+        {
+            return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length);
+        }
+    }
+}

+ 10 - 0
MediaBrowser.Common.Implementations/Logging/NLogger.cs

@@ -72,6 +72,11 @@ namespace MediaBrowser.Common.Implementations.Logging
         /// <param name="paramList">The param list.</param>
         public void Debug(string message, params object[] paramList)
         {
+            if (_logManager.LogSeverity == LogSeverity.Info)
+            {
+                return;
+            }
+
             _logger.Debug(message, paramList);
         }
 
@@ -137,6 +142,11 @@ namespace MediaBrowser.Common.Implementations.Logging
         /// <param name="additionalContent">Content of the additional.</param>
         public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent)
         {
+            if (severity == LogSeverity.Debug && _logManager.LogSeverity == LogSeverity.Info)
+            {
+                return;
+            }
+
             additionalContent.Insert(0, message + Environment.NewLine);
 
             const char tabChar = '\t';

+ 14 - 5
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -14,6 +14,7 @@
     <ProductVersion>10.0.0</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkProfile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -23,7 +24,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>none</DebugType>
@@ -51,11 +52,15 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
+    <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
     <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
-      <HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath>
+      <HintPath>..\packages\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
       <Private>True</Private>
     </Reference>
     <Reference Include="Patterns.Logging">
@@ -65,8 +70,8 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
     </Reference>
-    <Reference Include="SimpleInjector, Version=3.2.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
-      <HintPath>..\packages\SimpleInjector.3.2.0\lib\net45\SimpleInjector.dll</HintPath>
+    <Reference Include="SimpleInjector, Version=3.2.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+      <HintPath>..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll</HintPath>
       <Private>True</Private>
     </Reference>
     <Reference Include="System" />
@@ -75,6 +80,9 @@
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Net" />
+    <Reference Include="System.Text.Json">
+      <HintPath>..\ThirdParty\fastjsonparser\System.Text.Json.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml" />
     <Reference Include="ServiceStack.Text">
       <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
@@ -93,6 +101,7 @@
     <Compile Include="HttpClientManager\HttpClientInfo.cs" />
     <Compile Include="HttpClientManager\HttpClientManager.cs" />
     <Compile Include="IO\IsoManager.cs" />
+    <Compile Include="IO\MemoryStreamProvider.cs" />
     <Compile Include="Logging\LogHelper.cs" />
     <Compile Include="Logging\NLogger.cs" />
     <Compile Include="Logging\NlogManager.cs" />

+ 2 - 2
MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -390,13 +390,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
 
             try
             {
-                var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
-
                 if (options != null && options.MaxRuntimeMs.HasValue)
                 {
                     CurrentCancellationTokenSource.CancelAfter(options.MaxRuntimeMs.Value);
                 }
 
+                var localTask = ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress);
+
                 await localTask.ConfigureAwait(false);
 
                 status = TaskCompletionStatus.Completed;

+ 4 - 2
MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs

@@ -169,7 +169,8 @@ namespace MediaBrowser.Common.Implementations.Security
             var options = new HttpRequestOptions()
             {
                 Url = AppstoreRegUrl,
-                CancellationToken = CancellationToken.None
+                CancellationToken = CancellationToken.None,
+                BufferContent = false
             };
             options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
             options.RequestContent = parameters;
@@ -269,7 +270,8 @@ namespace MediaBrowser.Common.Implementations.Security
                         Url = MBValidateUrl,
 
                         // Seeing block length errors
-                        EnableHttpCompression = false
+                        EnableHttpCompression = false,
+                        BufferContent = false
                     };
 
                     options.SetPostData(data);

+ 4 - 2
MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs

@@ -30,7 +30,8 @@ namespace MediaBrowser.Common.Implementations.Updates
                 Url = url,
                 EnableKeepAlive = false,
                 CancellationToken = cancellationToken,
-                UserAgent = "Emby/3.0"
+                UserAgent = "Emby/3.0",
+                BufferContent = false
             };
 
             if (cacheLength.Ticks > 0)
@@ -105,7 +106,8 @@ namespace MediaBrowser.Common.Implementations.Updates
                 Url = url,
                 EnableKeepAlive = false,
                 CancellationToken = cancellationToken,
-                UserAgent = "Emby/3.0"
+                UserAgent = "Emby/3.0",
+                BufferContent = false
             };
 
             using (var stream = await _httpClient.Get(options).ConfigureAwait(false))

+ 4 - 8
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -148,14 +148,10 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// <summary>
         /// Gets all available packages.
         /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="withRegistration">if set to <c>true</c> [with registration].</param>
-        /// <param name="packageType">Type of the package.</param>
-        /// <param name="applicationVersion">The application version.</param>
         /// <returns>Task{List{PackageInfo}}.</returns>
         public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
             bool withRegistration = true,
-            PackageType? packageType = null,
+            string packageType = null,
             Version applicationVersion = null)
         {
             var data = new Dictionary<string, string>
@@ -293,7 +289,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             return packages;
         }
 
-        protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, PackageType? packageType, Version applicationVersion)
+        protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, string packageType, Version applicationVersion)
         {
             foreach (var package in packages)
             {
@@ -301,9 +297,9 @@ namespace MediaBrowser.Common.Implementations.Updates
                     .OrderByDescending(GetPackageVersion).ToList();
             }
 
-            if (packageType.HasValue)
+            if (!string.IsNullOrWhiteSpace(packageType))
             {
-                packages = packages.Where(p => p.type == packageType.Value).ToList();
+                packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList();
             }
 
             // If an app version was supplied, filter the versions for each package to only include supported versions

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

@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
+  <package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
-  <package id="NLog" version="4.3.6" targetFramework="net45" />
+  <package id="NLog" version="4.3.8" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-  <package id="SimpleInjector" version="3.2.0" targetFramework="net45" />
+  <package id="SimpleInjector" version="3.2.2" targetFramework="net45" />
 </packages>

+ 11 - 0
MediaBrowser.Common/IO/IMemoryStreamProvider.cs

@@ -0,0 +1,11 @@
+using System.IO;
+
+namespace MediaBrowser.Common.IO
+{
+    public interface IMemoryStreamProvider
+    {
+        MemoryStream CreateNew();
+        MemoryStream CreateNew(int capacity);
+        MemoryStream CreateNew(byte[] buffer);
+    }
+}

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

@@ -60,6 +60,7 @@
     <Compile Include="Extensions\BaseExtensions.cs" />
     <Compile Include="Extensions\ResourceNotFoundException.cs" />
     <Compile Include="IDependencyContainer.cs" />
+    <Compile Include="IO\IMemoryStreamProvider.cs" />
     <Compile Include="IO\ProgressStream.cs" />
     <Compile Include="IO\StreamDefaults.cs" />
     <Compile Include="Configuration\IApplicationPaths.cs" />

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

@@ -51,7 +51,7 @@ namespace MediaBrowser.Common.Updates
         /// <returns>Task{List{PackageInfo}}.</returns>
         Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
             bool withRegistration = true,
-                                                                                  PackageType? packageType = null,
+                                                                                  string packageType = null,
                                                                                   Version applicationVersion = null);
 
         /// <summary>

+ 0 - 10
MediaBrowser.Controller/Channels/IChannelManager.cs

@@ -134,15 +134,5 @@ namespace MediaBrowser.Controller.Channels
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>BaseItemDto.</returns>
         Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Downloads the channel item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="destinationPath">The destination path.</param>
-        /// <param name="progress">The progress.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task DownloadChannelItem(BaseItem item, string destinationPath, IProgress<double> progress, CancellationToken cancellationToken);
     }
 }

+ 0 - 19
MediaBrowser.Controller/Chapters/ChapterResponse.cs

@@ -1,19 +0,0 @@
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Chapters
-{
-    public class ChapterResponse
-    {
-        /// <summary>
-        /// Gets or sets the chapters.
-        /// </summary>
-        /// <value>The chapters.</value>
-        public List<RemoteChapterInfo> Chapters { get; set; }
-
-        public ChapterResponse()
-        {
-            Chapters = new List<RemoteChapterInfo>();
-        }
-    }
-}

+ 0 - 31
MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs

@@ -1,31 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Chapters
-{
-    public class ChapterSearchRequest : IHasProviderIds
-    {
-        public string Language { get; set; }
-
-        public VideoContentType ContentType { get; set; }
-
-        public string MediaPath { get; set; }
-        public string SeriesName { get; set; }
-        public string Name { get; set; }
-        public int? IndexNumber { get; set; }
-        public int? IndexNumberEnd { get; set; }
-        public int? ParentIndexNumber { get; set; }
-        public int? ProductionYear { get; set; }
-        public long? RuntimeTicks { get; set; }
-        public Dictionary<string, string> ProviderIds { get; set; }
-
-        public bool SearchAllProviders { get; set; }
-
-        public ChapterSearchRequest()
-        {
-            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-        }
-    }
-}

+ 1 - 46
MediaBrowser.Controller/Chapters/IChapterManager.cs

@@ -1,6 +1,4 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Configuration;
@@ -13,12 +11,6 @@ namespace MediaBrowser.Controller.Chapters
     /// </summary>
     public interface IChapterManager
     {
-        /// <summary>
-        /// Adds the parts.
-        /// </summary>
-        /// <param name="chapterProviders">The chapter providers.</param>
-        void AddParts(IEnumerable<IChapterProvider> chapterProviders);
-
         /// <summary>
         /// Gets the chapters.
         /// </summary>
@@ -35,43 +27,6 @@ namespace MediaBrowser.Controller.Chapters
         /// <returns>Task.</returns>
         Task SaveChapters(string itemId, List<ChapterInfo> chapters, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// Searches the specified video.
-        /// </summary>
-        /// <param name="video">The video.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
-        Task<IEnumerable<RemoteChapterResult>> Search(Video video, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Searches the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
-        Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the chapters.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{ChapterResponse}.</returns>
-        Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the providers.
-        /// </summary>
-        /// <param name="itemId">The item identifier.</param>
-        /// <returns>IEnumerable{ChapterProviderInfo}.</returns>
-        IEnumerable<ChapterProviderInfo> GetProviders(string itemId);
-
-        /// <summary>
-        /// Gets the providers.
-        /// </summary>
-        /// <returns>IEnumerable{ChapterProviderInfo}.</returns>
-        IEnumerable<ChapterProviderInfo> GetProviders();
-
         /// <summary>
         /// Gets the configuration.
         /// </summary>

+ 0 - 39
MediaBrowser.Controller/Chapters/IChapterProvider.cs

@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Chapters;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Chapters
-{
-    public interface IChapterProvider
-    {
-        /// <summary>
-        /// Gets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        string Name { get; }
-
-        /// <summary>
-        /// Gets the supported media types.
-        /// </summary>
-        /// <value>The supported media types.</value>
-        IEnumerable<VideoContentType> SupportedMediaTypes { get; }
-
-        /// <summary>
-        /// Searches the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns>
-        Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the chapters.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{ChapterResponse}.</returns>
-        Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken);
-    }
-}

+ 12 - 2
MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs

@@ -1,10 +1,20 @@
 using System;
+using System.Collections.Generic;
+using System.Net;
+using MediaBrowser.Model.Events;
 
 namespace MediaBrowser.Controller.Dlna
 {
     public interface IDeviceDiscovery
     {
-        event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
-        event EventHandler<SsdpMessageEventArgs> DeviceLeft;
+        event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
+        event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
+    }
+
+    public class UpnpDeviceInfo
+    {
+        public Uri Location { get; set; }
+        public Dictionary<string, string> Headers { get; set; }
+        public IPEndPoint LocalEndPoint { get; set; }
     }
 }

+ 0 - 1
MediaBrowser.Controller/Dlna/ISsdpHandler.cs

@@ -4,6 +4,5 @@ namespace MediaBrowser.Controller.Dlna
 {
     public interface ISsdpHandler
     {
-        event EventHandler<SsdpMessageEventArgs> MessageReceived;
     }
 }

+ 15 - 0
MediaBrowser.Controller/Entities/AggregateFolder.cs

@@ -34,11 +34,26 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool IsPhysicalRoot
+        {
+            get { return true; }
+        }
+
         public override bool CanDelete()
         {
             return false;
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// The _virtual children
         /// </summary>

+ 12 - 19
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -23,8 +23,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         IHasMusicGenres,
         IHasLookupInfo<SongInfo>,
         IHasMediaSources,
-        IThemeMedia,
-        IArchivable
+        IThemeMedia
     {
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
 
@@ -60,10 +59,19 @@ namespace MediaBrowser.Controller.Entities.Audio
             AlbumArtists = new List<string>();
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         {
-            get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+            get { return true; }
         }
 
         [IgnoreDataMember]
@@ -84,21 +92,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             }
         }
 
-        [IgnoreDataMember]
-        public bool IsArchive
-        {
-            get
-            {
-                if (string.IsNullOrWhiteSpace(Path))
-                {
-                    return false;
-                }
-                var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
-
-                return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
-            }
-        }
-
         public override bool CanDownload()
         {
             var locationType = LocationType;
@@ -262,7 +255,7 @@ namespace MediaBrowser.Controller.Entities.Audio
                 Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
                 MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(),
                 Name = i.Name,
-                Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
+                Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path,
                 RunTimeTicks = i.RunTimeTicks,
                 Container = i.Container,
                 Size = i.Size

+ 12 - 4
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -17,6 +17,9 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// </summary>
     public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
     {
+        public List<string> AlbumArtists { get; set; }
+        public List<string> Artists { get; set; }
+
         public MusicAlbum()
         {
             Artists = new List<string>();
@@ -48,6 +51,15 @@ namespace MediaBrowser.Controller.Entities.Audio
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         [IgnoreDataMember]
         public override bool SupportsCumulativeRunTimeTicks
         {
@@ -83,8 +95,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             get { return false; }
         }
 
-        public List<string> AlbumArtists { get; set; }
-
         /// <summary>
         /// Gets the tracks.
         /// </summary>
@@ -103,8 +113,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             return Tracks;
         }
 
-        public List<string> Artists { get; set; }
-
         public override List<string> GetUserDataKeys()
         {
             var list = base.GetUserDataKeys();

+ 10 - 8
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// <summary>
     /// Class MusicArtist
     /// </summary>
-    public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
+    public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo>
     {
         [IgnoreDataMember]
         public bool IsAccessedByName
@@ -24,8 +24,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             get { return ParentId == Guid.Empty; }
         }
 
-        public List<string> ProductionLocations { get; set; }
-
         [IgnoreDataMember]
         public override bool IsFolder
         {
@@ -50,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio
             get { return true; }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         public override bool CanDelete()
         {
             return !IsAccessedByName;
@@ -111,11 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
         }
 
-        public MusicArtist()
-        {
-            ProductionLocations = new List<string>();
-        }
-
         public override List<string> GetUserDataKeys()
         {
             var list = base.GetUserDataKeys();

+ 80 - 17
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.Entities
     {
         protected BaseItem()
         {
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
             Keywords = new List<string>();
             Tags = new List<string>();
             Genres = new List<string>();
@@ -44,6 +46,8 @@ namespace MediaBrowser.Controller.Entities
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             LockedFields = new List<MetadataFields>();
             ImageInfos = new List<ItemImageInfo>();
+            InheritedTags = new List<string>();
+            ProductionLocations = new List<string>();
         }
 
         public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
@@ -64,6 +68,9 @@ namespace MediaBrowser.Controller.Entities
         public static string ThemeSongFilename = "theme";
         public static string ThemeVideosFolderName = "backdrops";
 
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+
         [IgnoreDataMember]
         public string PreferredMetadataCountryCode { get; set; }
         [IgnoreDataMember]
@@ -72,6 +79,8 @@ namespace MediaBrowser.Controller.Entities
         public long? Size { get; set; }
         public string Container { get; set; }
         public string ShortOverview { get; set; }
+        [IgnoreDataMember]
+        public string Tagline { get; set; }
 
         public List<ItemImageInfo> ImageInfos { get; set; }
 
@@ -114,6 +123,31 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public bool IsInMixedFolder { get; set; }
 
+        [IgnoreDataMember]
+        protected virtual bool SupportsIsInMixedFolderDetection
+        {
+            get { return false; }
+        }
+
+        [IgnoreDataMember]
+        public virtual bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        public bool DetectIsInMixedFolder()
+        {
+            if (SupportsIsInMixedFolderDetection)
+            {
+                    
+            }
+
+            return IsInMixedFolder;
+        }
+
         [IgnoreDataMember]
         public virtual bool SupportsRemoteImageDownloading
         {
@@ -254,6 +288,19 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public string ExternalSeriesId { get; set; }
+
+        [IgnoreDataMember]
+        public string ExternalSeriesIdLegacy
+        {
+            get { return this.GetProviderId("ProviderExternalSeriesId"); }
+            set
+            {
+                this.SetProviderId("ProviderExternalSeriesId", value);
+            }
+        }
+
         /// <summary>
         /// Gets or sets the etag.
         /// </summary>
@@ -408,7 +455,7 @@ namespace MediaBrowser.Controller.Entities
 
         public virtual bool IsInternetMetadataEnabled()
         {
-            return ConfigurationManager.Configuration.EnableInternetProviders;
+            return LibraryManager.GetLibraryOptions(this).EnableInternetProviders;
         }
 
         public virtual bool CanDelete()
@@ -784,6 +831,9 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public int InheritedParentalRatingValue { get; set; }
 
+        [IgnoreDataMember]
+        public List<string> InheritedTags { get; set; }
+
         /// <summary>
         /// Gets or sets the critic rating.
         /// </summary>
@@ -841,6 +891,7 @@ namespace MediaBrowser.Controller.Entities
         public List<string> Tags { get; set; }
 
         public List<string> Keywords { get; set; }
+        public List<string> ProductionLocations { get; set; }
 
         /// <summary>
         /// Gets or sets the home page URL.
@@ -956,7 +1007,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads the theme songs.
         /// </summary>
         /// <returns>List{Audio.Audio}.</returns>
-        private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+        private static IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
         {
             var files = fileSystemChildren.Where(i => i.IsDirectory)
                 .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
@@ -992,7 +1043,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads the video backdrops.
         /// </summary>
         /// <returns>List{Video}.</returns>
-        private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+        private static IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
         {
             var files = fileSystemChildren.Where(i => i.IsDirectory)
                 .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
@@ -1078,6 +1129,12 @@ namespace MediaBrowser.Controller.Entities
             get { return true; }
         }
 
+        [IgnoreDataMember]
+        public virtual bool SupportsThemeMedia
+        {
+            get { return false; }
+        }
+
         /// <summary>
         /// Refreshes owned items such as trailers, theme videos, special features, etc.
         /// Returns true or false indicating if changes were found.
@@ -1096,14 +1153,13 @@ namespace MediaBrowser.Controller.Entities
 
             if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
-                var hasThemeMedia = this as IHasThemeMedia;
-                if (hasThemeMedia != null)
+                if (SupportsThemeMedia)
                 {
-                    if (!IsInMixedFolder)
+                    if (!DetectIsInMixedFolder())
                     {
-                        themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+                        themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
-                        themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+                        themeVideosChanged = await RefreshThemeVideos(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
                     }
                 }
 
@@ -1141,7 +1197,7 @@ namespace MediaBrowser.Controller.Entities
             return itemsChanged;
         }
 
-        private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
+        private static async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
             var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
 
@@ -1172,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// Refreshes the theme songs.
         /// </summary>
-        private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
+        private static async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
             var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
             var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
@@ -1249,7 +1305,15 @@ namespace MediaBrowser.Controller.Entities
         {
             var current = this;
 
-            return current.IsInMixedFolder == newItem.IsInMixedFolder;
+            if (!SupportsIsInMixedFolderDetection)
+            {
+                if (current.IsInMixedFolder != newItem.IsInMixedFolder)
+                {
+                    return false;
+                }
+            }
+
+            return true;
         }
 
         public void AfterMetadataRefresh()
@@ -1324,7 +1388,9 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            return ConfigurationManager.Configuration.SaveLocalMeta;
+            var libraryOptions = LibraryManager.GetLibraryOptions(this);
+
+            return libraryOptions.SaveLocalMetadata;
         }
 
         /// <summary>
@@ -2107,14 +2173,11 @@ namespace MediaBrowser.Controller.Entities
             return hasChanges;
         }
 
-        protected static string GetMappedPath(string path, LocationType locationType)
+        protected static string GetMappedPath(BaseItem item, string path, LocationType locationType)
         {
             if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
             {
-                foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
-                {
-                    path = LibraryManager.SubstitutePath(path, map.From, map.To);
-                }
+                return LibraryManager.GetPathAfterNetworkSubstitution(path, item);
             }
 
             return path;

+ 28 - 14
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -38,6 +38,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         public override bool CanDelete()
         {
             return false;
@@ -48,24 +57,14 @@ namespace MediaBrowser.Controller.Entities
         private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>();
         public LibraryOptions GetLibraryOptions()
         {
-            lock (LibraryOptions)
-            {
-                LibraryOptions options;
-                if (!LibraryOptions.TryGetValue(Path, out options))
-                {
-                    options = LoadLibraryOptions();
-                    LibraryOptions[Path] = options;
-                }
-
-                return options;
-            }
+            return GetLibraryOptions(Path);
         }
 
-        private LibraryOptions LoadLibraryOptions()
+        private static LibraryOptions LoadLibraryOptions(string path)
         {
             try
             {
-                var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(Path)) as LibraryOptions;
+                var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions;
 
                 if (result == null)
                 {
@@ -100,13 +99,28 @@ namespace MediaBrowser.Controller.Entities
             SaveLibraryOptions(Path, options);
         }
 
+        public static LibraryOptions GetLibraryOptions(string path)
+        {
+            lock (LibraryOptions)
+            {
+                LibraryOptions options;
+                if (!LibraryOptions.TryGetValue(path, out options))
+                {
+                    options = LoadLibraryOptions(path);
+                    LibraryOptions[path] = options;
+                }
+
+                return options;
+            }
+        }
+
         public static void SaveLibraryOptions(string path, LibraryOptions options)
         {
             lock (LibraryOptions)
             {
                 LibraryOptions[path] = options;
 
-                options.SchemaVersion = 2;
+                options.SchemaVersion = 3;
                 XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
             }
         }

+ 36 - 52
MediaBrowser.Controller/Entities/Folder.cs

@@ -22,13 +22,18 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Folder
     /// </summary>
-    public class Folder : BaseItem, IHasThemeMedia
+    public class Folder : BaseItem
     {
         public static IUserManager UserManager { get; set; }
         public static IUserViewManager UserViewManager { get; set; }
 
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is root.
+        /// </summary>
+        /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
+        public bool IsRoot { get; set; }
+
+        public virtual List<LinkedChild> LinkedChildren { get; set; }
 
         [IgnoreDataMember]
         public DateTime? DateLastMediaAdded { get; set; }
@@ -36,9 +41,12 @@ namespace MediaBrowser.Controller.Entities
         public Folder()
         {
             LinkedChildren = new List<LinkedChild>();
+        }
 
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
         }
 
         [IgnoreDataMember]
@@ -47,6 +55,21 @@ namespace MediaBrowser.Controller.Entities
             get { return false; }
         }
 
+        [IgnoreDataMember]
+        public virtual bool IsPhysicalRoot
+        {
+            get { return false; }
+        }
+
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         /// <summary>
         /// Gets a value indicating whether this instance is folder.
         /// </summary>
@@ -117,19 +140,6 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is physical root.
-        /// </summary>
-        /// <value><c>true</c> if this instance is physical root; otherwise, <c>false</c>.</value>
-        public bool IsPhysicalRoot { get; set; }
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is root.
-        /// </summary>
-        /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
-        public bool IsRoot { get; set; }
-
-        public virtual List<LinkedChild> LinkedChildren { get; set; }
-
         [IgnoreDataMember]
         protected virtual bool SupportsShortcutChildren
         {
@@ -178,8 +188,6 @@ namespace MediaBrowser.Controller.Entities
             item.SetParent(null);
         }
 
-        #region Indexing
-
         /// <summary>
         /// Returns the valid set of index by options for this folder type.
         /// Override or extend to modify.
@@ -207,8 +215,6 @@ namespace MediaBrowser.Controller.Entities
             get { return GetIndexByOptions(); }
         }
 
-        #endregion
-
         /// <summary>
         /// Gets the actual children.
         /// </summary>
@@ -801,18 +807,6 @@ namespace MediaBrowser.Controller.Entities
                 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)
             {
@@ -1149,29 +1143,19 @@ namespace MediaBrowser.Controller.Entities
             return LinkedChildren
                 .Select(i =>
                 {
-                    var requiresPostFilter = true;
-
-                    if (!string.IsNullOrWhiteSpace(i.Path))
-                    {
-                        requiresPostFilter = false;
-
-                        if (!locations.Any(l => FileSystem.ContainsSubPath(l, i.Path)))
-                        {
-                            return null;
-                        }
-                    }
-
                     var child = GetLinkedChild(i);
 
-                    if (requiresPostFilter && child != null)
+                    if (child != null)
                     {
-                        if (string.IsNullOrWhiteSpace(child.Path))
+                        var childLocationType = child.LocationType;
+                        if (childLocationType == LocationType.Remote || childLocationType == LocationType.Virtual)
                         {
-                            Logger.Debug("Found LinkedChild with null path: {0}", child.Name);
-                            return child;
+                            if (!child.IsVisibleStandalone(user))
+                            {
+                                return null;
+                            }
                         }
-
-                        if (!locations.Any(l => FileSystem.ContainsSubPath(l, child.Path)))
+                        else if (childLocationType == LocationType.FileSystem && !locations.Any(l => FileSystem.ContainsSubPath(l, child.Path)))
                         {
                             return null;
                         }

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

@@ -8,19 +8,14 @@ using System.Runtime.Serialization;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
+    public class Game : BaseItem, IHasTrailers, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
     {
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
-
         public Game()
         {
             MultiPartGameFiles = new List<string>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
             RemoteTrailerIds = new List<Guid>();
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
@@ -39,6 +34,12 @@ namespace MediaBrowser.Controller.Entities
             get { return true; }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
+        }
+
         /// <summary>
         /// Gets or sets the remote trailers.
         /// </summary>
@@ -98,7 +99,7 @@ namespace MediaBrowser.Controller.Entities
 
         public override IEnumerable<string> GetDeletePaths()
         {
-            if (!IsInMixedFolder)
+            if (!DetectIsInMixedFolder())
             {
                 return new[] { System.IO.Path.GetDirectoryName(Path) };
             }

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

@@ -26,6 +26,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// Gets or sets the game system.
         /// </summary>

+ 0 - 8
MediaBrowser.Controller/Entities/IArchivable.cs

@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
-    public interface IArchivable
-    {
-        bool IsArchive { get; }
-    }
-}

+ 1 - 5
MediaBrowser.Controller/Entities/IHasImages.cs

@@ -150,11 +150,7 @@ namespace MediaBrowser.Controller.Entities
         /// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value>
         bool SupportsLocalMetadata { get; }
 
-        /// <summary>
-        /// Gets a value indicating whether this instance is in mixed folder.
-        /// </summary>
-        /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
-        bool IsInMixedFolder { get; }
+        bool DetectIsInMixedFolder();
 
         /// <summary>
         /// Gets a value indicating whether this instance is locked.

+ 3 - 0
MediaBrowser.Controller/Entities/IHasMetadata.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -62,5 +63,7 @@ namespace MediaBrowser.Controller.Entities
 
         int? GetInheritedParentalRatingValue();
         int InheritedParentalRatingValue { get; set; }
+        List<string> GetInheritedTags();
+        List<string> InheritedTags { get; set; }
     }
 }

+ 0 - 34
MediaBrowser.Controller/Entities/IHasProductionLocations.cs

@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
-    /// <summary>
-    /// Interface IHasProductionLocations
-    /// </summary>
-    public interface IHasProductionLocations
-    {
-        /// <summary>
-        /// Gets or sets the production locations.
-        /// </summary>
-        /// <value>The production locations.</value>
-        List<string> ProductionLocations { get; set; }
-    }
-
-    public static class ProductionLocationExtensions
-    {
-        public static void AddProductionLocation(this IHasProductionLocations item, string name)
-        {
-            if (string.IsNullOrWhiteSpace(name))
-            {
-                throw new ArgumentNullException("name");
-            }
-
-            if (!item.ProductionLocations.Contains(name, StringComparer.OrdinalIgnoreCase))
-            {
-                item.ProductionLocations.Add(name);
-            }
-        }
-    }
-}

+ 0 - 39
MediaBrowser.Controller/Entities/IHasTaglines.cs

@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Controller.Entities
-{
-    /// <summary>
-    /// Interface IHasTaglines
-    /// </summary>
-    public interface IHasTaglines
-    {
-        /// <summary>
-        /// Gets or sets the taglines.
-        /// </summary>
-        /// <value>The taglines.</value>
-        List<string> Taglines { get; set; }
-    }
-
-    public static class TaglineExtensions
-    {
-        /// <summary>
-        /// Adds the tagline.
-        /// </summary>
-        /// <param name="tagline">The tagline.</param>
-        /// <exception cref="System.ArgumentNullException">tagline</exception>
-        public static void AddTagline(this IHasTaglines item, string tagline)
-        {
-            if (string.IsNullOrWhiteSpace(tagline))
-            {
-                throw new ArgumentNullException("tagline");
-            }
-
-            if (!item.Taglines.Contains(tagline, StringComparer.OrdinalIgnoreCase))
-            {
-                item.Taglines.Add(tagline);
-            }
-        }
-    }
-}

+ 0 - 23
MediaBrowser.Controller/Entities/IHasThemeMedia.cs

@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
-    /// <summary>
-    /// Interface IHasThemeMedia
-    /// </summary>
-    public interface IHasThemeMedia
-    {
-        /// <summary>
-        /// Gets or sets the theme song ids.
-        /// </summary>
-        /// <value>The theme song ids.</value>
-        List<Guid> ThemeSongIds { get; set; }
-
-        /// <summary>
-        /// Gets or sets the theme video ids.
-        /// </summary>
-        /// <value>The theme video ids.</value>
-        List<Guid> ThemeVideoIds { get; set; }
-    }
-}

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

@@ -20,5 +20,7 @@ namespace MediaBrowser.Controller.Entities
         Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user);
 
         bool EnableRememberingTrackSelections { get; }
+
+        bool SupportsPlayedStatus { get; }
     }
 }

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

@@ -2,6 +2,9 @@
 using System;
 using System.Collections.Generic;
 using MediaBrowser.Model.Configuration;
+using System.Linq;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -101,6 +104,8 @@ namespace MediaBrowser.Controller.Entities
         public bool? IsMovie { get; set; }
         public bool? IsSports { get; set; }
         public bool? IsKids { get; set; }
+        public bool? IsNews { get; set; }
+        public bool? IsSeries { get; set; }
 
         public int? MinPlayers { get; set; }
         public int? MaxPlayers { get; set; }
@@ -137,6 +142,7 @@ namespace MediaBrowser.Controller.Entities
         public DayOfWeek[] AirDays { get; set; }
         public SeriesStatus[] SeriesStatuses { get; set; }
         public string AlbumArtistStartsWithOrGreater { get; set; }
+        public string ExternalSeriesId { get; set; }
 
         public string[] AlbumNames { get; set; }
         public string[] ArtistNames { get; set; }
@@ -149,11 +155,53 @@ namespace MediaBrowser.Controller.Entities
         public Dictionary<string, string> ExcludeProviderIds { get; set; }
         public bool EnableGroupByMetadataKey { get; set; }
 
+        public List<Tuple<string, SortOrder>> OrderBy { get; set; }
+
+        public DateTime? MinDateCreated { get; set; }
+        public DateTime? MinDateLastSaved { get; set; }
+
+        public DtoOptions DtoOptions { get; set; }
+
+        public bool HasField(ItemFields name)
+        {
+            var fields = DtoOptions.Fields;
+
+            switch (name)
+            {
+                case ItemFields.ThemeSongIds:
+                case ItemFields.ThemeVideoIds:
+                case ItemFields.ProductionLocations:
+                case ItemFields.Keywords:
+                case ItemFields.Taglines:
+                case ItemFields.ShortOverview:
+                case ItemFields.CustomRating:
+                case ItemFields.DateCreated:
+                case ItemFields.SortName:
+                case ItemFields.Overview:
+                case ItemFields.OfficialRatingDescription:
+                case ItemFields.HomePageUrl:
+                case ItemFields.VoteCount:
+                case ItemFields.DisplayMediaType:
+                case ItemFields.ServiceName:
+                case ItemFields.Genres:
+                case ItemFields.Studios:
+                case ItemFields.Settings:
+                case ItemFields.OriginalTitle:
+                case ItemFields.Tags:
+                case ItemFields.DateLastMediaAdded:
+                case ItemFields.CriticRatingSummary:
+                    return fields.Count == 0 || fields.Contains(name);
+                default:
+                    return true;
+            }
+        }
+
         public InternalItemsQuery()
         {
             GroupByPresentationUniqueKey = true;
             EnableTotalRecordCount = true;
 
+            DtoOptions = new DtoOptions();
             AlbumNames = new string[] { };
             ArtistNames = new string[] { };
             ExcludeArtistIds = new string[] { };
@@ -191,6 +239,7 @@ namespace MediaBrowser.Controller.Entities
             TrailerTypes = new TrailerType[] { };
             AirDays = new DayOfWeek[] { };
             SeriesStatuses = new SeriesStatus[] { };
+            OrderBy = new List<Tuple<string, SortOrder>>();
         }
 
         public InternalItemsQuery(User user)

+ 13 - 11
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -15,24 +15,17 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class Movie
     /// </summary>
-    public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
+    public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
     {
         public List<Guid> SpecialFeatureIds { get; set; }
 
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
-        public List<string> ProductionLocations { get; set; }
-
         public Movie()
         {
             SpecialFeatureIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
             RemoteTrailerIds = new List<Guid>();
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
             Taglines = new List<string>();
-            ProductionLocations = new List<string>();
         }
 
         public string AwardSummary { get; set; }
@@ -75,13 +68,22 @@ namespace MediaBrowser.Controller.Entities.Movies
             set { TmdbCollectionName = value; }
         }
 
+        [IgnoreDataMember]
+        protected override bool SupportsIsInMixedFolderDetection
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
             var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
             // Must have a parent to have special features
             // In other words, it must be part of the Parent/Child tree
-            if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder)
+            if (LocationType == LocationType.FileSystem && GetParent() != null && !DetectIsInMixedFolder())
             {
                 var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
@@ -119,7 +121,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         {
             var info = GetItemLookupInfo<MovieInfo>();
 
-            if (!IsInMixedFolder)
+            if (!DetectIsInMixedFolder())
             {
                 info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
             }
@@ -145,7 +147,7 @@ namespace MediaBrowser.Controller.Entities.Movies
                 else
                 {
                     // Try to get the year from the folder name
-                    if (!IsInMixedFolder)
+                    if (!DetectIsInMixedFolder())
                     {
                         info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
 

+ 11 - 4
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -6,7 +6,7 @@ using System.Runtime.Serialization;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo<MusicVideoInfo>
+    public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasBudget, IHasLookupInfo<MusicVideoInfo>
     {
         /// <summary>
         /// Gets or sets the budget.
@@ -19,12 +19,10 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
-        public List<string> ProductionLocations { get; set; }
         public List<string> Artists { get; set; }
 
         public MusicVideo()
         {
-            ProductionLocations = new List<string>();
             Artists = new List<string>();
         }
 
@@ -37,6 +35,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        protected override bool SupportsIsInMixedFolderDetection
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         public override UnratedItem GetBlockUnratedType()
         {
             return UnratedItem.Music;
@@ -65,7 +72,7 @@ namespace MediaBrowser.Controller.Entities
                 else
                 {
                     // Try to get the year from the folder name
-                    if (!IsInMixedFolder)
+                    if (!DetectIsInMixedFolder())
                     {
                         info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
 

+ 3 - 11
MediaBrowser.Controller/Entities/Photo.cs

@@ -1,19 +1,11 @@
 using MediaBrowser.Model.Drawing;
-using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class Photo : BaseItem, IHasTaglines
+    public class Photo : BaseItem
     {
-        public List<string> Taglines { get; set; }
-
-        public Photo()
-        {
-            Taglines = new List<string>();
-        }
-
         [IgnoreDataMember]
         public override bool SupportsLocalMetadata
         {
@@ -37,13 +29,13 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return Album;
+                return AlbumEntity;
             }
         }
 
 
         [IgnoreDataMember]
-        public PhotoAlbum Album
+        public PhotoAlbum AlbumEntity
         {
             get
             {

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

@@ -16,6 +16,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         protected override bool GetBlockUnratedValue(UserPolicy config)
         {
             return config.BlockUnratedItems.Contains(UnratedItem.Other);

+ 0 - 2
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -154,8 +154,6 @@ namespace MediaBrowser.Controller.Entities.TV
 
             Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
 
-            var id = Guid.NewGuid().ToString("N");
-
             var items = GetEpisodes(user).Where(filter);
 
             var result = PostFilterAndSort(items, query, false, false);

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

@@ -17,17 +17,14 @@ namespace MediaBrowser.Controller.Entities.TV
     /// <summary>
     /// Class Series
     /// </summary>
-    public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures, IMetadataContainer, IHasOriginalTitle
+    public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer, IHasOriginalTitle
     {
-        public List<Guid> SpecialFeatureIds { get; set; }
-
         public int? AnimeSeriesIndex { get; set; }
 
         public Series()
         {
             AirDays = new List<DayOfWeek>();
 
-            SpecialFeatureIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
             RemoteTrailerIds = new List<Guid>();

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

@@ -10,16 +10,12 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Trailer
     /// </summary>
-    public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
+    public class Trailer : Video, IHasCriticRating, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
     {
-        public List<string> ProductionLocations { get; set; }
-
         public Trailer()
         {
             RemoteTrailers = new List<MediaUrl>();
-            Taglines = new List<string>();
             Keywords = new List<string>();
-            ProductionLocations = new List<string>();
             TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
         }
 
@@ -35,12 +31,6 @@ namespace MediaBrowser.Controller.Entities
             get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
         }
 
-        /// <summary>
-        /// Gets or sets the taglines.
-        /// </summary>
-        /// <value>The taglines.</value>
-        public List<string> Taglines { get; set; }
-
         /// <summary>
         /// Gets or sets the budget.
         /// </summary>
@@ -64,7 +54,7 @@ namespace MediaBrowser.Controller.Entities
 
             info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer);
 
-            if (!IsInMixedFolder && LocationType == LocationType.FileSystem)
+            if (!DetectIsInMixedFolder() && LocationType == LocationType.FileSystem)
             {
                 info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
             }
@@ -90,7 +80,7 @@ namespace MediaBrowser.Controller.Entities
                 else
                 {
                     // Try to get the year from the folder name
-                    if (!IsInMixedFolder)
+                    if (!DetectIsInMixedFolder())
                     {
                         info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
 

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

@@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         private void ClearCache()
         {
             lock (_childIdsLock)

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

@@ -13,7 +13,6 @@ namespace MediaBrowser.Controller.Entities
     public class UserView : Folder
     {
         public string ViewType { get; set; }
-        public Guid ParentId { get; set; }
         public Guid DisplayParentId { get; set; }
 
         public Guid? UserId { get; set; }
@@ -45,6 +44,15 @@ namespace MediaBrowser.Controller.Entities
             return list;
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return false;
+            }
+        }
+
         public override int GetChildCount(User user)
         {
             return GetChildren(user, true).Count();

+ 3 - 15
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
                             Limit = query.Limit,
                             IsAiring = true
 
-                        }, CancellationToken.None).ConfigureAwait(false);
+                        }, new Dto.DtoOptions(), CancellationToken.None).ConfigureAwait(false);
 
                         return GetResult(result);
                     }
@@ -1497,13 +1497,7 @@ namespace MediaBrowser.Controller.Entities
             {
                 var filterValue = query.HasThemeSong.Value;
 
-                var themeCount = 0;
-                var iHasThemeMedia = item as IHasThemeMedia;
-
-                if (iHasThemeMedia != null)
-                {
-                    themeCount = iHasThemeMedia.ThemeSongIds.Count;
-                }
+                var themeCount = item.ThemeSongIds.Count;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
 
                 if (!ok)
@@ -1516,13 +1510,7 @@ namespace MediaBrowser.Controller.Entities
             {
                 var filterValue = query.HasThemeVideo.Value;
 
-                var themeCount = 0;
-                var iHasThemeMedia = item as IHasThemeMedia;
-
-                if (iHasThemeMedia != null)
-                {
-                    themeCount = iHasThemeMedia.ThemeVideoIds.Count;
-                }
+                var themeCount = item.ThemeVideoIds.Count;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
 
                 if (!ok)

+ 28 - 20
MediaBrowser.Controller/Entities/Video.cs

@@ -25,8 +25,7 @@ namespace MediaBrowser.Controller.Entities
         ISupportsPlaceHolders,
         IHasMediaSources,
         IHasShortOverview,
-        IThemeMedia,
-        IArchivable
+        IThemeMedia
     {
         [IgnoreDataMember]
         public string PrimaryVersionId { get; set; }
@@ -45,6 +44,24 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        [IgnoreDataMember]
+        protected override bool SupportsIsInMixedFolderDetection
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         public override string CreatePresentationUniqueKey()
         {
             if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
@@ -64,6 +81,12 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
+        }
+
         public int? TotalBitrate { get; set; }
         public ExtraType? ExtraType { get; set; }
 
@@ -165,7 +188,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         {
-            get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+            get { return true; }
         }
 
         [IgnoreDataMember]
@@ -197,21 +220,6 @@ namespace MediaBrowser.Controller.Entities
             get { return LocalAlternateVersions.Count > 0; }
         }
 
-        [IgnoreDataMember]
-        public bool IsArchive
-        {
-            get
-            {
-                if (string.IsNullOrWhiteSpace(Path))
-                {
-                    return false;
-                }
-                var ext = System.IO.Path.GetExtension(Path) ?? string.Empty;
-
-                return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase);
-            }
-        }
-
         public IEnumerable<Guid> GetAdditionalPartIds()
         {
             return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
@@ -480,7 +488,7 @@ namespace MediaBrowser.Controller.Entities
 
         public override IEnumerable<string> GetDeletePaths()
         {
-            if (!IsInMixedFolder)
+            if (!DetectIsInMixedFolder())
             {
                 return new[] { ContainingFolderPath };
             }
@@ -600,7 +608,7 @@ namespace MediaBrowser.Controller.Entities
                 Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
                 MediaStreams = mediaStreams,
                 Name = GetMediaSourceName(i, mediaStreams),
-                Path = enablePathSubstitution ? GetMappedPath(i.Path, locationType) : i.Path,
+                Path = enablePathSubstitution ? GetMappedPath(i, i.Path, locationType) : i.Path,
                 RunTimeTicks = i.RunTimeTicks,
                 Video3DFormat = i.Video3DFormat,
                 VideoType = i.VideoType,

+ 5 - 2
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -506,6 +506,8 @@ namespace MediaBrowser.Controller.Library
         /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
         QueryResult<BaseItem> QueryItems(InternalItemsQuery query);
 
+        string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null);
+
         /// <summary>
         /// Substitutes the path.
         /// </summary>
@@ -554,9 +556,10 @@ namespace MediaBrowser.Controller.Library
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
 
-        void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary);
+        void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary);
         void RemoveVirtualFolder(string name, bool refreshLibrary);
-        void AddMediaPath(string virtualFolderName, string path);
+        void AddMediaPath(string virtualFolderName, MediaPathInfo path);
+        void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
         void RemoveMediaPath(string virtualFolderName, string path);
 
         QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);

+ 10 - 6
MediaBrowser.Controller/Library/IMediaSourceManager.cs

@@ -7,6 +7,7 @@ using System;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using System.IO;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -60,11 +61,8 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// Gets the static media source.
         /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="mediaSourceId">The media source identifier.</param>
-        /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
         /// <returns>MediaSourceInfo.</returns>
-        Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution);
+        Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken);
 
         /// <summary>
         /// Opens the media source.
@@ -82,6 +80,8 @@ namespace MediaBrowser.Controller.Library
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
         Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken);
+
+        Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
         
         /// <summary>
         /// Pings the media source.
@@ -95,8 +95,12 @@ namespace MediaBrowser.Controller.Library
         /// Closes the media source.
         /// </summary>
         /// <param name="id">The live stream identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseLiveStream(string id, CancellationToken cancellationToken);
+        Task CloseLiveStream(string id);
+    }
+
+    public interface IDirectStreamProvider
+    {
+        Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
     }
 }

+ 3 - 3
MediaBrowser.Controller/Library/IMediaSourceProvider.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Dto;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using System;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -22,14 +23,13 @@ namespace MediaBrowser.Controller.Library
         /// <param name="openToken">The open token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
-        Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken);
+        Task<Tuple<MediaSourceInfo,IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken);
 
         /// <summary>
         /// Closes the media source.
         /// </summary>
         /// <param name="liveStreamId">The live stream identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken);
+        Task CloseMediaSource(string liveStreamId);
     }
 }

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

@@ -9,6 +9,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Events;
+using MediaBrowser.Controller.Library;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{SeriesTimerInfoDto}.</returns>
         Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken);
-        
+
         /// <summary>
         /// Deletes the recording.
         /// </summary>
@@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="recording">The recording.</param>
         /// <returns>Task.</returns>
         Task DeleteRecording(BaseItem recording);
-        
+
         /// <summary>
         /// Cancels the timer.
         /// </summary>
@@ -83,7 +84,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="user">The user.</param>
         /// <returns>Task{RecordingInfoDto}.</returns>
         Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
-        
+
         /// <summary>
         /// Gets the timer.
         /// </summary>
@@ -125,14 +126,14 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{SeriesTimerInfoDto}}.</returns>
         Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken);
-        
+
         /// <summary>
         /// Gets the channel.
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <returns>Channel.</returns>
         LiveTvChannel GetInternalChannel(string id);
-        
+
         /// <summary>
         /// Gets the recording.
         /// </summary>
@@ -156,8 +157,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="mediaSourceId">The media source identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{StreamResponseInfo}.</returns>
-        Task<MediaSourceInfo> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken);
-        
+        Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken);
+
         /// <summary>
         /// Gets the program.
         /// </summary>
@@ -220,9 +221,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// Closes the live stream.
         /// </summary>
         /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseLiveStream(string id, CancellationToken cancellationToken);
+        Task CloseLiveStream(string id);
 
         /// <summary>
         /// Gets the guide information.
@@ -242,10 +242,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// Gets the recommended programs internal.
         /// </summary>
-        /// <param name="query">The query.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;QueryResult&lt;LiveTvProgram&gt;&gt;.</returns>
-        Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken);
+        Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the live tv information.
@@ -303,18 +301,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// Gets the recording media sources.
         /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
-        Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken);
+        Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the channel media sources.
         /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
-        Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken);
+        Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
 
         /// <summary>
         /// Adds the information to recording dto.
@@ -331,8 +323,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="fields">The fields.</param>
         /// <param name="user">The user.</param>
         /// <returns>Task.</returns>
-        Task AddInfoToProgramDto(List<Tuple<BaseItem,BaseItemDto>> programs, List<ItemFields> fields, User user = null);
-      
+        Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> programs, List<ItemFields> fields, User user = null);
+
         /// <summary>
         /// Saves the tuner host.
         /// </summary>
@@ -395,7 +387,7 @@ namespace MediaBrowser.Controller.LiveTv
         Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
         Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
 
-        List<IListingsProvider> ListingProviders { get;}
+        List<IListingsProvider> ListingProviders { get; }
 
         event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
         event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;

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

@@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
         bool CanDelete(User user);
 
         string SeriesTimerId { get; set; }
+        string TimerId { get; set; }
         RecordingStatus Status { get; set; }
         DateTime? EndDate { get; set; }
         DateTime DateLastSaved { get; set; }

+ 11 - 0
MediaBrowser.Controller/LiveTv/ILiveTvService.cs

@@ -4,6 +4,7 @@ using System;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Library;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -245,4 +246,14 @@ namespace MediaBrowser.Controller.LiveTv
         /// <returns>Task.</returns>
         Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken);
     }
+
+    public interface ISupportsDirectStreamProvider
+    {
+        Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken);
+    }
+
+    public interface ISupportsUpdatingDefaults
+    {
+        Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken);
+    }
 }

+ 2 - 5
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -22,9 +22,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// Gets the channels.
         /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
-        Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken);
+        Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
         /// <summary>
         /// Gets the tuner infos.
         /// </summary>
@@ -38,7 +37,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="streamId">The stream identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
-        Task<Tuple<MediaSourceInfo,SemaphoreSlim>> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
+        Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
         /// <summary>
         /// Gets the channel stream media sources.
         /// </summary>
@@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
         Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
-
-        string ApplyDuration(string streamPath, TimeSpan duration);
     }
     public interface IConfigurableTunerHost
     {

+ 45 - 0
MediaBrowser.Controller/LiveTv/LiveStream.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Controller.LiveTv
+{
+    public class LiveStream
+    {
+        public MediaSourceInfo OriginalMediaSource { get; set; }
+        public MediaSourceInfo OpenedMediaSource { get; set; }
+        public int ConsumerCount {
+            get { return SharedStreamIds.Count; }
+        }
+        public ITunerHost TunerHost { get; set; }
+        public string OriginalStreamId { get; set; }
+        public bool EnableStreamSharing { get; set; }
+        public string UniqueId = Guid.NewGuid().ToString("N");
+
+        public List<string> SharedStreamIds = new List<string>(); 
+
+        public LiveStream(MediaSourceInfo mediaSource)
+        {
+            OriginalMediaSource = mediaSource;
+            OpenedMediaSource = mediaSource;
+            EnableStreamSharing = true;
+        }
+
+        public Task Open(CancellationToken cancellationToken)
+        {
+            return OpenInternal(cancellationToken);
+        }
+
+        protected virtual Task OpenInternal(CancellationToken cancellationToken)
+        {
+            return Task.FromResult(true);
+        }
+
+        public virtual Task Close()
+        {
+            return Task.FromResult(true);
+        }
+    }
+}

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

@@ -20,6 +20,7 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
+        public string TimerId { get; set; }
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
         public RecordingStatus Status { get; set; }
@@ -112,7 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
 
         public override bool CanDelete()
         {
-            return true;
+            return Status == RecordingStatus.Completed;
         }
 
         public override bool IsAuthorizedToDelete(User user)

+ 55 - 3
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -5,11 +5,12 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.MediaInfo;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Runtime.Serialization;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public class LiveTvChannel : BaseItem, IHasMediaSources
+    public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
     {
         public override List<string> GetUserDataKeys()
         {
@@ -81,10 +82,10 @@ namespace MediaBrowser.Controller.LiveTv
 
             if (!string.IsNullOrEmpty(Number))
             {
-                double.TryParse(Number, out number);
+                double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number);
             }
 
-            return number.ToString("000-") + (Name ?? string.Empty);
+            return number.ToString("00000-") + (Name ?? string.Empty);
         }
 
         [IgnoreDataMember]
@@ -137,5 +138,56 @@ namespace MediaBrowser.Controller.LiveTv
         {
             return false;
         }
+
+        [IgnoreDataMember]
+        public bool IsMovie { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is sports.
+        /// </summary>
+        /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsSports { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is series.
+        /// </summary>
+        /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsSeries { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is live.
+        /// </summary>
+        /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsLive { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is news.
+        /// </summary>
+        /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsNews { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is kids.
+        /// </summary>
+        /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsKids { get; set; }
+
+        [IgnoreDataMember]
+        public bool IsPremiere { get; set; }
+
+        [IgnoreDataMember]
+        public bool IsRepeat { get; set; }
+
+        /// <summary>
+        /// Gets or sets the episode title.
+        /// </summary>
+        /// <value>The episode title.</value>
+        [IgnoreDataMember]
+        public string EpisodeTitle { get; set; }
     }
 }

+ 20 - 1
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
+        public string TimerId { get; set; }
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
         public RecordingStatus Status { get; set; }
@@ -53,6 +54,24 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
+        [IgnoreDataMember]
+        protected override bool SupportsIsInMixedFolderDetection
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return Status == RecordingStatus.Completed && base.SupportsPlayedStatus;
+            }
+        }
+
         [IgnoreDataMember]
         public override LocationType LocationType
         {
@@ -111,7 +130,7 @@ namespace MediaBrowser.Controller.LiveTv
 
         public override bool CanDelete()
         {
-            return true;
+            return Status == RecordingStatus.Completed;
         }
 
         public override bool IsAuthorizedToDelete(User user)

+ 2 - 0
MediaBrowser.Controller/LiveTv/ProgramInfo.cs

@@ -107,6 +107,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value>The image URL.</value>
         public string ImageUrl { get; set; }
 
+        public string LogoImageUrl { get; set; }
+
         /// <summary>
         /// Gets or sets a value indicating whether this instance has image.
         /// </summary>

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

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -26,6 +27,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         public string Name { get; set; }
 
+        public string ServiceName { get; set; }
+
         /// <summary>
         /// Description of the recording.
         /// </summary>
@@ -53,6 +56,11 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value>
         public bool RecordAnyChannel { get; set; }
 
+        public int KeepUpTo { get; set; }
+        public KeepUntil KeepUntil { get; set; }
+
+        public bool SkipEpisodesInLibrary { get; set; }
+
         /// <summary>
         /// Gets or sets a value indicating whether [record new only].
         /// </summary>
@@ -104,6 +112,8 @@ namespace MediaBrowser.Controller.LiveTv
         public SeriesTimerInfo()
         {
             Days = new List<DayOfWeek>();
+            SkipEpisodesInLibrary = true;
+            KeepUntil = KeepUntil.UntilDeleted;
         }
     }
 }

+ 35 - 3
MediaBrowser.Controller/LiveTv/TimerInfo.cs

@@ -1,10 +1,17 @@
 using MediaBrowser.Model.LiveTv;
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.LiveTv
 {
     public class TimerInfo
     {
+        public TimerInfo()
+        {
+            Genres = new List<string>();
+            KeepUntil = KeepUntil.UntilDeleted;
+        }
+
         /// <summary>
         /// Id of the recording.
         /// </summary>
@@ -15,7 +22,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// <value>The series timer identifier.</value>
         public string SeriesTimerId { get; set; }
-        
+
         /// <summary>
         /// ChannelId of the recording.
         /// </summary>
@@ -26,7 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// <value>The program identifier.</value>
         public string ProgramId { get; set; }
-        
+
         /// <summary>
         /// Name of the recording.
         /// </summary>
@@ -76,11 +83,36 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
         public bool IsPostPaddingRequired { get; set; }
-        
+
         /// <summary>
         /// Gets or sets the priority.
         /// </summary>
         /// <value>The priority.</value>
         public int Priority { get; set; }
+
+
+        // Program properties
+        public int? SeasonNumber { get; set; }
+        /// <summary>
+        /// Gets or sets the episode number.
+        /// </summary>
+        /// <value>The episode number.</value>
+        public int? EpisodeNumber { get; set; }
+        public bool IsMovie { get; set; }
+        public bool IsKids { get; set; }
+        public bool IsSports { get; set; }
+        public bool IsNews { get; set; }
+        public int? ProductionYear { get; set; }
+        public string EpisodeTitle { get; set; }
+        public DateTime? OriginalAirDate { get; set; }
+        public bool IsProgramSeries { get; set; }
+        public bool IsRepeat { get; set; }
+        public string HomePageUrl { get; set; }
+        public float? CommunityRating { get; set; }
+        public string ShortOverview { get; set; }
+        public string OfficialRating { get; set; }
+        public List<string> Genres { get; set; }
+        public string RecordingPath { get; set; }
+        public KeepUntil KeepUntil { get; set; }
     }
 }

+ 1 - 11
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -91,10 +91,7 @@
     <Compile Include="Channels\InternalChannelItemQuery.cs" />
     <Compile Include="Channels\IRequiresMediaInfoCallback.cs" />
     <Compile Include="Channels\ISearchableChannel.cs" />
-    <Compile Include="Chapters\ChapterSearchRequest.cs" />
     <Compile Include="Chapters\IChapterManager.cs" />
-    <Compile Include="Chapters\IChapterProvider.cs" />
-    <Compile Include="Chapters\ChapterResponse.cs" />
     <Compile Include="Collections\CollectionCreationOptions.cs" />
     <Compile Include="Collections\CollectionEvents.cs" />
     <Compile Include="Collections\ICollectionManager.cs" />
@@ -135,7 +132,6 @@
     <Compile Include="Entities\Game.cs" />
     <Compile Include="Entities\GameGenre.cs" />
     <Compile Include="Entities\GameSystem.cs" />
-    <Compile Include="Entities\IArchivable.cs" />
     <Compile Include="Entities\IByReferenceItem.cs" />
     <Compile Include="Entities\IHasAspectRatio.cs" />
     <Compile Include="Entities\IHasBudget.cs" />
@@ -147,15 +143,12 @@
     <Compile Include="Entities\IHasMediaSources.cs" />
     <Compile Include="Entities\IHasMetascore.cs" />
     <Compile Include="Entities\IHasOriginalTitle.cs" />
-    <Compile Include="Entities\IHasProductionLocations.cs" />
     <Compile Include="Entities\IHasProgramAttributes.cs" />
     <Compile Include="Entities\IHasScreenshots.cs" />
     <Compile Include="Entities\IHasSeries.cs" />
     <Compile Include="Entities\IHasShortOverview.cs" />
     <Compile Include="Entities\IHasSpecialFeatures.cs" />
     <Compile Include="Entities\IHasStartDate.cs" />
-    <Compile Include="Entities\IHasTaglines.cs" />
-    <Compile Include="Entities\IHasThemeMedia.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IHasUserData.cs" />
     <Compile Include="Entities\IHiddenFromDisplay.cs" />
@@ -201,6 +194,7 @@
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="LiveTv\IListingsProvider.cs" />
     <Compile Include="LiveTv\ITunerHost.cs" />
+    <Compile Include="LiveTv\LiveStream.cs" />
     <Compile Include="LiveTv\RecordingGroup.cs" />
     <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
     <Compile Include="LiveTv\ILiveTvRecording.cs" />
@@ -266,7 +260,6 @@
     <Compile Include="Playlists\IPlaylistManager.cs" />
     <Compile Include="Playlists\Playlist.cs" />
     <Compile Include="Plugins\ILocalizablePlugin.cs" />
-    <Compile Include="Power\IPowerManagement.cs" />
     <Compile Include="Providers\AlbumInfo.cs" />
     <Compile Include="Providers\ArtistInfo.cs" />
     <Compile Include="Providers\BookInfo.cs" />
@@ -290,9 +283,7 @@
     <Compile Include="Providers\IHasItemChangeMonitor.cs" />
     <Compile Include="Providers\IHasLookupInfo.cs" />
     <Compile Include="Providers\IHasOrder.cs" />
-    <Compile Include="Providers\IImageFileSaver.cs" />
     <Compile Include="Providers\IImageProvider.cs" />
-    <Compile Include="Providers\IImageSaver.cs" />
     <Compile Include="Providers\ILocalImageFileProvider.cs" />
     <Compile Include="Providers\ILocalMetadataProvider.cs" />
     <Compile Include="Providers\ImageRefreshMode.cs" />
@@ -319,7 +310,6 @@
     <Compile Include="Providers\SongInfo.cs" />
     <Compile Include="Providers\TrailerInfo.cs" />
     <Compile Include="Providers\VideoContentType.cs" />
-    <Compile Include="RelatedMedia\IRelatedMediaProvider.cs" />
     <Compile Include="Security\AuthenticationInfo.cs" />
     <Compile Include="Security\AuthenticationInfoQuery.cs" />
     <Compile Include="Security\IAuthenticationRepository.cs" />

+ 3 - 3
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="offset">The offset.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
+        Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
 
-        Task<string> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
+        Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, int? imageStreamIndex, CancellationToken cancellationToken);
 
         /// <summary>
         /// Extracts the video images on interval.
@@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="inputFiles">The input files.</param>
         /// <param name="protocol">The protocol.</param>
         /// <returns>System.String.</returns>
-        string GetProbeSizeArgument(string[] inputFiles, MediaProtocol protocol);
+        string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol);
 
         /// <summary>
         /// Gets the input argument.

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

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

+ 2 - 2
MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs

@@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Net
 
             var cancellationTokenSource = new CancellationTokenSource();
 
-            Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
+            Logger.Debug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
 
             var timer = SendOnTimer ?
                 new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
@@ -267,7 +267,7 @@ namespace MediaBrowser.Controller.Net
         /// <param name="connection">The connection.</param>
         private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
         {
-            Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
+            Logger.Debug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
 
             var timer = connection.Item3;
 

+ 0 - 8
MediaBrowser.Controller/Net/IHttpResultFactory.cs

@@ -11,14 +11,6 @@ namespace MediaBrowser.Controller.Net
     /// </summary>
     public interface IHttpResultFactory
     {
-        /// <summary>
-        /// Throws the error.
-        /// </summary>
-        /// <param name="statusCode">The status code.</param>
-        /// <param name="errorMessage">The error message.</param>
-        /// <param name="responseHeaders">The response headers.</param>
-        void ThrowError(int statusCode, string errorMessage, IDictionary<string, string> responseHeaders = null);
-        
         /// <summary>
         /// Gets the result.
         /// </summary>

+ 16 - 7
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -31,6 +31,15 @@ namespace MediaBrowser.Controller.Playlists
             }
         }
 
+        [IgnoreDataMember]
+        public override bool SupportsPlayedStatus
+        {
+            get
+            {
+                return string.Equals(MediaType, "Video", StringComparison.OrdinalIgnoreCase);
+            }
+        }
+
         [IgnoreDataMember]
         public override bool AlwaysScanInternalMetadataPath
         {
@@ -198,15 +207,15 @@ namespace MediaBrowser.Controller.Playlists
 
         public override bool IsVisible(User user)
         {
-            if (base.IsVisible(user))
-            {
-                var userId = user.Id.ToString("N");
+            var userId = user.Id.ToString("N");
 
-                return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
-                    string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
-            }
+            return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) ||
+                string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase);
+        }
 
-            return false;
+        public override bool IsVisibleStandalone(User user)
+        {
+            return IsVisible(user);
         }
     }
 }

+ 0 - 13
MediaBrowser.Controller/Power/IPowerManagement.cs

@@ -1,13 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Power
-{
-    public interface IPowerManagement
-    {
-        /// <summary>
-        /// Schedules the wake.
-        /// </summary>
-        /// <param name="utcTime">The UTC time.</param>
-        void ScheduleWake(DateTime utcTime);
-    }
-}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä