Răsfoiți Sursa

sync updates

Luke Pulverenti 10 ani în urmă
părinte
comite
a025f4eefa
24 a modificat fișierele cu 365 adăugiri și 132 ștergeri
  1. 11 0
      MediaBrowser.Api/BaseApiService.cs
  2. 1 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  3. 38 24
      MediaBrowser.Api/Playback/MediaInfoService.cs
  4. 1 1
      MediaBrowser.Api/Playback/TranscodingThrottler.cs
  5. 8 0
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  6. 5 3
      MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
  7. 73 4
      MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
  8. 2 2
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  9. 2 2
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  10. 1 1
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  11. 71 38
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  12. 1 0
      MediaBrowser.Model/Configuration/UserConfiguration.cs
  13. 58 1
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  14. 44 30
      MediaBrowser.Model/Dlna/StreamInfo.cs
  15. 6 0
      MediaBrowser.Model/Entities/MediaStream.cs
  16. 5 4
      MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
  17. 4 4
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  18. 23 8
      MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
  19. 1 1
      MediaBrowser.Server.Implementations/Library/UserViewManager.cs
  20. 4 2
      MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  21. 1 0
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  22. 2 3
      MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
  23. 2 2
      MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
  24. 1 1
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs

+ 11 - 0
MediaBrowser.Api/BaseApiService.cs

@@ -73,6 +73,17 @@ namespace MediaBrowser.Api
             return ResultFactory.GetOptimizedResultUsingCache(Request, cacheKey, lastDateModified, cacheDuration, factoryFn);
         }
 
+        /// <summary>
+        /// Infers the server address from the url
+        /// </summary>
+        /// <returns></returns>
+        protected string GetServerAddress()
+        {
+            var index = Request.AbsoluteUri.IndexOf(Request.PathInfo, StringComparison.OrdinalIgnoreCase);
+
+            return Request.AbsoluteUri.Substring(0, index);
+        }
+
         protected void AssertCanUpdateUser(IUserManager userManager, string userId)
         {
             var auth = AuthorizationContext.GetAuthorizationInfo(Request);

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -699,7 +699,7 @@ namespace MediaBrowser.Api.Playback
 
                 if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
                 {
-                    var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
+                    var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result;
 
                     if (!string.IsNullOrEmpty(charenc))
                     {

+ 38 - 24
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -59,6 +59,9 @@ namespace MediaBrowser.Api.Playback
 
         [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
         public string MediaSourceId { get; set; }
+
+        [ApiMember(Name = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+        public string LiveStreamId { get; set; }
     }
 
     [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")]
@@ -142,7 +145,7 @@ namespace MediaBrowser.Api.Playback
 
         public async Task<object> Post(GetPostedPlaybackInfo request)
         {
-            var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false);
+            var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false);
             var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
 
             var profile = request.DeviceProfile;
@@ -164,29 +167,37 @@ namespace MediaBrowser.Api.Playback
             return ToOptimizedResult(info);
         }
 
-        private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string mediaSourceId = null)
+        private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string mediaSourceId = null, string liveStreamId = null)
         {
             var result = new PlaybackInfoResponse();
 
-            IEnumerable<MediaSourceInfo> mediaSources;
-
-            try
-            {
-                mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
-            }
-            catch (PlaybackException ex)
+            if (string.IsNullOrWhiteSpace(liveStreamId))
             {
-                mediaSources = new List<MediaSourceInfo>();
-                result.ErrorCode = ex.ErrorCode;
-            }
+                IEnumerable<MediaSourceInfo> mediaSources;
+                try
+                {
+                    mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
+                }
+                catch (PlaybackException ex)
+                {
+                    mediaSources = new List<MediaSourceInfo>();
+                    result.ErrorCode = ex.ErrorCode;
+                }
 
-            result.MediaSources = mediaSources.ToList();
+                result.MediaSources = mediaSources.ToList();
 
-            if (!string.IsNullOrWhiteSpace(mediaSourceId))
+                if (!string.IsNullOrWhiteSpace(mediaSourceId))
+                {
+                    result.MediaSources = result.MediaSources
+                        .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
+                        .ToList();
+                }
+            }
+            else
             {
-                result.MediaSources = result.MediaSources
-                    .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
-                    .ToList();
+                var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
+
+                result.MediaSources = new List<MediaSourceInfo> { mediaSource };
             }
 
             if (result.MediaSources.Count == 0)
@@ -236,6 +247,8 @@ namespace MediaBrowser.Api.Playback
         {
             var streamBuilder = new StreamBuilder();
 
+            var baseUrl = GetServerAddress();
+
             var options = new VideoOptions
             {
                 MediaSources = new List<MediaSourceInfo> { mediaSource },
@@ -275,7 +288,7 @@ namespace MediaBrowser.Api.Playback
 
                 if (streamInfo != null)
                 {
-                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token);
                 }
             }
 
@@ -293,7 +306,7 @@ namespace MediaBrowser.Api.Playback
 
                 if (streamInfo != null)
                 {
-                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token);
                 }
             }
 
@@ -307,21 +320,22 @@ namespace MediaBrowser.Api.Playback
                 if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode)
                 {
                     streamInfo.StartPositionTicks = startTimeTicks;
-                    mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
+                    mediaSource.TranscodingUrl = streamInfo.ToUrl(baseUrl, auth.Token);
                     mediaSource.TranscodingContainer = streamInfo.Container;
                     mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
                 }
 
                 if (streamInfo != null)
                 {
-                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+                    SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, baseUrl, auth.Token);
                 }
             }
         }
 
-        private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken)
+        private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string baseUrl, string accessToken)
         {
-            var profiles = info.GetSubtitleProfiles(false, "-", accessToken);
+            var profiles = info.GetSubtitleProfiles(false, baseUrl, accessToken);
+            mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex;
 
             foreach (var profile in profiles)
             {
@@ -333,7 +347,7 @@ namespace MediaBrowser.Api.Playback
 
                         if (profile.DeliveryMethod == SubtitleDeliveryMethod.External)
                         {
-                            stream.DeliveryUrl = profile.Url.TrimStart('-');
+                            stream.DeliveryUrl = profile.Url;
                         }
                     }
                 }

+ 1 - 1
MediaBrowser.Api/Playback/TranscodingThrottler.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback
 
             var options = GetOptions();
 
-            if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds))
+            if (/*options.EnableThrottling &&*/ IsThrottleAllowed(_job, options.ThrottleThresholdSeconds))
             {
                 PauseTranscoding();
             }

+ 8 - 0
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -304,6 +304,14 @@ namespace MediaBrowser.Api.UserLibrary
         {
             var user = _userManager.GetUserById(request.UserId);
 
+            if (!request.IsPlayed.HasValue)
+            {
+                if (user.Configuration.HidePlayedInLatest)
+                {
+                    request.IsPlayed = false;
+                }
+            }
+
             var list = _userViewManager.GetLatestItems(new LatestItemsQuery
             {
                 GroupItems = request.GroupItems,

+ 5 - 3
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs

@@ -1,4 +1,5 @@
-using System.IO;
+using MediaBrowser.Model.MediaInfo;
+using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -47,8 +48,9 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// Gets the subtitle language encoding parameter.
         /// </summary>
         /// <param name="path">The path.</param>
+        /// <param name="protocol">The protocol.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
-        string GetSubtitleFileCharacterSet(string path);
-
+        Task<string> GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken);
     }
 }

+ 73 - 4
MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs

@@ -41,8 +41,6 @@ namespace MediaBrowser.Controller.MediaEncoding
             streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
                 .ToList();
 
-            var full = streams.Where(s => !s.IsForced);
-
             MediaStream stream = null;
 
             if (mode == SubtitlePlaybackMode.None)
@@ -55,13 +53,13 @@ namespace MediaBrowser.Controller.MediaEncoding
                 // if the audio language is not understood by the user, load their preferred subs, if there are any
                 if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
                 {
-                    stream = full.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
+                    stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
                 }
             }
             else if (mode == SubtitlePlaybackMode.Always)
             {
                 // always load the most suitable full subtitles
-                stream = full.FirstOrDefault();
+                stream = streams.FirstOrDefault(s => !s.IsForced);
             }
 
             // load forced subs if we have found no suitable full subtitles
@@ -97,6 +95,77 @@ namespace MediaBrowser.Controller.MediaEncoding
                  .ThenBy(i => i.Index);
         }
 
+        public static void SetSubtitleStreamScores(List<MediaStream> streams,
+            List<string> preferredLanguages,
+            SubtitlePlaybackMode mode,
+            string audioTrackLanguage)
+        {
+            if (mode == SubtitlePlaybackMode.None)
+            {
+                return;
+            }
+
+            streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
+                .ToList();
+
+            var filteredStreams = new List<MediaStream>();
+
+            if (mode == SubtitlePlaybackMode.Default)
+            {
+                // if the audio language is not understood by the user, load their preferred subs, if there are any
+                if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
+                {
+                    filteredStreams = streams.Where(s => !s.IsForced && ContainsOrdinal(preferredLanguages, s.Language))
+                        .ToList();
+                }
+            }
+            else if (mode == SubtitlePlaybackMode.Always)
+            {
+                // always load the most suitable full subtitles
+                filteredStreams = streams.Where(s => !s.IsForced)
+                    .ToList();
+            }
+
+            // load forced subs if we have found no suitable full subtitles
+            if (filteredStreams.Count == 0)
+            {
+                filteredStreams = streams
+                    .Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
+                    .ToList();
+            }
+
+            foreach (var stream in filteredStreams)
+            {
+                stream.Score = GetSubtitleScore(stream, preferredLanguages);
+            }
+        }
+
+        private static int GetSubtitleScore(MediaStream stream, List<string> languagePreferences)
+        {
+            var values = new List<int>();
+
+            var index = languagePreferences.FindIndex(l => string.Equals(stream.Language, l, StringComparison.OrdinalIgnoreCase));
+
+            values.Add(index == -1 ? 0 : 100 - index);
+
+            values.Add(stream.IsDefault ? 1 : 0);
+            values.Add(stream.SupportsExternalStream ? 1 : 0);
+            values.Add(stream.IsTextSubtitleStream ? 1 : 0);
+            values.Add(stream.IsExternal ? 1 : 0);
+
+            values.Reverse();
+            var scale = 1;
+            var score = 0;
+
+            foreach (var value in values)
+            {
+                score += scale * (value + 1);
+                scale *= 10;
+            }
+
+            return score;
+        }
+
         private static int GetBooleanOrderBy(bool value)
         {
             return value ? 0 : 1;

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

@@ -124,7 +124,7 @@ namespace MediaBrowser.Dlna.Didl
         {
             if (streamInfo == null)
             {
-                var sources = _user == null ? video.GetMediaSources(true).ToList() : _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList();
+                var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(video, true).ToList() : _mediaSourceManager.GetStaticMediaSources(video, true, _user).ToList();
 
                 streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
                 {
@@ -351,7 +351,7 @@ namespace MediaBrowser.Dlna.Didl
 
             if (streamInfo == null)
             {
-                var sources = _user == null ? audio.GetMediaSources(true).ToList() : _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
+                var sources = _user == null ? _mediaSourceManager.GetStaticMediaSources(audio, true).ToList() : _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
 
                 streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
                {

+ 2 - 2
MediaBrowser.Dlna/PlayTo/PlayToController.cs

@@ -467,10 +467,10 @@ namespace MediaBrowser.Dlna.PlayTo
 
             var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
                 _dlnaManager.GetDefaultProfile();
-
+            
             var hasMediaSources = item as IHasMediaSources;
             var mediaSources = hasMediaSources != null
-                ? (user == null ? hasMediaSources.GetMediaSources(true) : _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList()
+                ? (user == null ? _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true) : _mediaSourceManager.GetStaticMediaSources(hasMediaSources, true, user)).ToList()
                 : new List<MediaSourceInfo>();
 
             var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);

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

@@ -951,7 +951,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
                 {
-                    var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
+                    var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.MediaSource.Protocol, CancellationToken.None).Result;
 
                     if (!string.IsNullOrEmpty(charenc))
                     {

+ 71 - 38
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
@@ -29,8 +30,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         private readonly IFileSystem _fileSystem;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IJsonSerializer _json;
+        private readonly IHttpClient _httpClient;
+        private readonly IMediaSourceManager _mediaSourceManager;
 
-        public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json)
+        public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager)
         {
             _libraryManager = libraryManager;
             _logger = logger;
@@ -38,6 +41,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             _fileSystem = fileSystem;
             _mediaEncoder = mediaEncoder;
             _json = json;
+            _httpClient = httpClient;
+            _mediaSourceManager = mediaSourceManager;
         }
 
         private string SubtitleCachePath
@@ -127,9 +132,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             int subtitleStreamIndex,
             CancellationToken cancellationToken)
         {
-            var item = (Video)_libraryManager.GetItemById(new Guid(itemId));
+            var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, false, cancellationToken).ConfigureAwait(false);
 
-            var mediaSource = item.GetMediaSources(false)
+            var mediaSource = mediaSources
                 .First(i => string.Equals(i.Id, mediaSourceId));
 
             var subtitleStream = mediaSource.MediaStreams
@@ -149,20 +154,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
 
-            var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item3).ConfigureAwait(false);
+            var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item2, fileInfo.Item4, cancellationToken).ConfigureAwait(false);
 
-            return new Tuple<Stream, string>(stream, fileInfo.Item2);
+            return new Tuple<Stream, string>(stream, fileInfo.Item3);
         }
 
-        private async Task<Stream> GetSubtitleStream(string path, bool requiresCharset)
+        private async Task<Stream> GetSubtitleStream(string path, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken)
         {
             if (requiresCharset)
             {
-                var charset = GetSubtitleFileCharacterSet(path);
+                var charset = await GetSubtitleFileCharacterSet(path, protocol, cancellationToken).ConfigureAwait(false);
 
                 if (!string.IsNullOrEmpty(charset))
                 {
-                    using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                    using (var fs = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
                     {
                         using (var reader = new StreamReader(fs, GetEncoding(charset)))
                         {
@@ -196,7 +201,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             }
         }
 
-        private async Task<Tuple<string, string, bool>> GetReadableFile(string mediaPath,
+        private async Task<Tuple<string, MediaProtocol, string, bool>> GetReadableFile(string mediaPath,
             string[] inputFiles,
             MediaProtocol protocol,
             MediaStream subtitleStream,
@@ -228,12 +233,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 }
 
                 // Extract    
-                var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, "." + outputFormat);
+                var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat);
 
                 await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
                         .ConfigureAwait(false);
 
-                return new Tuple<string, string, bool>(outputPath, outputFormat, false);
+                return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, outputFormat, false);
             }
 
             var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec)
@@ -242,14 +247,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             if (GetReader(currentFormat, false) == null)
             {
                 // Convert    
-                var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, ".srt");
+                var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt");
 
-                await ConvertTextSubtitleToSrt(subtitleStream.Path, outputPath, cancellationToken).ConfigureAwait(false);
+                await ConvertTextSubtitleToSrt(subtitleStream.Path, protocol, outputPath, cancellationToken).ConfigureAwait(false);
 
-                return new Tuple<string, string, bool>(outputPath, "srt", true);
+                return new Tuple<string, MediaProtocol, string, bool>(outputPath, MediaProtocol.File, "srt", true);
             }
 
-            return new Tuple<string, string, bool>(subtitleStream.Path, currentFormat, true);
+            return new Tuple<string, MediaProtocol, string, bool>(subtitleStream.Path, protocol, currentFormat, true);
         }
 
         private async Task<SubtitleTrackInfo> GetTrackInfo(Stream stream,
@@ -336,10 +341,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         /// Converts the text subtitle to SRT.
         /// </summary>
         /// <param name="inputPath">The input path.</param>
+        /// <param name="inputProtocol">The input protocol.</param>
         /// <param name="outputPath">The output path.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        public async Task ConvertTextSubtitleToSrt(string inputPath, string outputPath, CancellationToken cancellationToken)
+        private async Task ConvertTextSubtitleToSrt(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
         {
             var semaphore = GetLock(outputPath);
 
@@ -349,7 +355,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             {
                 if (!File.Exists(outputPath))
                 {
-                    await ConvertTextSubtitleToSrtInternal(inputPath, outputPath).ConfigureAwait(false);
+                    await ConvertTextSubtitleToSrtInternal(inputPath, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false);
                 }
             }
             finally
@@ -362,13 +368,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
         /// Converts the text subtitle to SRT internal.
         /// </summary>
         /// <param name="inputPath">The input path.</param>
+        /// <param name="inputProtocol">The input protocol.</param>
         /// <param name="outputPath">The output path.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        /// <exception cref="System.ArgumentNullException">inputPath
+        /// <exception cref="System.ArgumentNullException">
+        /// inputPath
         /// or
-        /// outputPath</exception>
+        /// outputPath
+        /// </exception>
         /// <exception cref="System.ApplicationException"></exception>
-        private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string outputPath)
+        private async Task ConvertTextSubtitleToSrtInternal(string inputPath, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(inputPath))
             {
@@ -382,7 +392,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
 
-            var encodingParam = GetSubtitleFileCharacterSet(inputPath);
+            var encodingParam = await GetSubtitleFileCharacterSet(inputPath, inputProtocol, cancellationToken).ConfigureAwait(false);
 
             if (!string.IsNullOrEmpty(encodingParam))
             {
@@ -688,32 +698,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             }
         }
 
-        private string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension)
+        private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension)
         {
-            var ticksParam = string.Empty;
+            if (protocol == MediaProtocol.File)
+            {
+                var ticksParam = string.Empty;
+
+                var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
 
-            var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
+                var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension;
 
-            var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam).GetMD5() + outputSubtitleExtension;
+                var prefix = filename.Substring(0, 1);
+
+                return Path.Combine(SubtitleCachePath, prefix, filename);
+            }
+            else
+            {
+                var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5() + outputSubtitleExtension;
 
-            var prefix = filename.Substring(0, 1);
+                var prefix = filename.Substring(0, 1);
 
-            return Path.Combine(SubtitleCachePath, prefix, filename);
+                return Path.Combine(SubtitleCachePath, prefix, filename);
+            }
         }
 
-        /// <summary>
-        /// Gets the subtitle language encoding param.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>System.String.</returns>
-        public string GetSubtitleFileCharacterSet(string path)
+        public async Task<string> GetSubtitleFileCharacterSet(string path, MediaProtocol protocol, CancellationToken cancellationToken)
         {
-            if (GetFileEncoding(path).Equals(Encoding.UTF8))
+            if (protocol == MediaProtocol.File)
             {
-                return string.Empty;
+                if (GetFileEncoding(path).Equals(Encoding.UTF8))
+                {
+                    return string.Empty;
+                }
             }
 
-            var charset = DetectCharset(path);
+            var charset = await DetectCharset(path, protocol, cancellationToken).ConfigureAwait(false);
 
             if (!string.IsNullOrWhiteSpace(charset))
             {
@@ -769,11 +788,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             }
         }
 
-        private string DetectCharset(string path)
+        private async Task<string> DetectCharset(string path, MediaProtocol protocol, CancellationToken cancellationToken)
         {
             try
             {
-                using (var file = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+                using (var file = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
                 {
                     var detector = new CharsetDetector();
                     detector.Feed(file);
@@ -819,5 +838,19 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             // It's ok - anything aside from utf is ok since that's what we're looking for
             return Encoding.Default;
         }
+
+        private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
+        {
+            if (protocol == MediaProtocol.Http)
+            {
+                return await _httpClient.Get(path, cancellationToken).ConfigureAwait(false);
+            }
+            if (protocol == MediaProtocol.File)
+            {
+                return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+            }
+
+            throw new ArgumentOutOfRangeException("protocol");
+        }
     }
 }

+ 1 - 0
MediaBrowser.Model/Configuration/UserConfiguration.cs

@@ -54,6 +54,7 @@ namespace MediaBrowser.Model.Configuration
         public string[] LatestItemsExcludes { get; set; }
 
         public bool HasMigratedToPolicy { get; set; }
+        public bool HidePlayedInLatest { get; set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="UserConfiguration" /> class.

+ 58 - 1
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -282,6 +282,49 @@ namespace MediaBrowser.Model.Dlna
             return playMethods;
         }
 
+        private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles)
+        {
+            int highestScore = -1;
+
+            foreach (MediaStream stream in item.MediaStreams)
+            {
+                if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue)
+                {
+                    if (stream.Score.Value > highestScore)
+                    {
+                        highestScore = stream.Score.Value;
+                    }
+                }    
+            }
+
+            List<MediaStream> topStreams = new List<MediaStream>();
+            foreach (MediaStream stream in item.MediaStreams)
+            {
+                if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore)
+                {
+                    topStreams.Add(stream);
+                }
+            }
+
+            // If multiple streams have an equal score, try to pick the most efficient one
+            if (topStreams.Count > 1)
+            {
+                foreach (MediaStream stream in topStreams)
+                {
+                    foreach (SubtitleProfile profile in subtitleProfiles)
+                    {
+                        if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec))
+                        {
+                            return stream.Index;
+                        }
+                    }
+                }
+            }
+
+            // If no optimization panned out, just use the original default
+            return item.DefaultSubtitleStreamIndex;
+        }
+
         private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
         {
             StreamInfo playlistItem = new StreamInfo
@@ -294,7 +337,7 @@ namespace MediaBrowser.Model.Dlna
                 DeviceProfile = options.Profile
             };
 
-            playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? item.DefaultSubtitleStreamIndex;
+            playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles);
             MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null;
 
             MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex);
@@ -618,6 +661,8 @@ namespace MediaBrowser.Model.Dlna
             // Look for an external profile that matches the stream type (text/graphical)
             foreach (SubtitleProfile profile in subtitleProfiles)
             {
+                bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
+
                 if (!profile.SupportsLanguage(subtitleStream.Language))
                 {
                     continue;
@@ -625,6 +670,11 @@ namespace MediaBrowser.Model.Dlna
 
                 if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
                 {
+                    if (!requiresConversion)
+                    {
+                        return profile;
+                    }
+
                     if (subtitleStream.SupportsExternalStream)
                     {
                         return profile;
@@ -640,6 +690,8 @@ namespace MediaBrowser.Model.Dlna
 
             foreach (SubtitleProfile profile in subtitleProfiles)
             {
+                bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
+
                 if (!profile.SupportsLanguage(subtitleStream.Language))
                 {
                     continue;
@@ -647,6 +699,11 @@ namespace MediaBrowser.Model.Dlna
 
                 if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
                 {
+                    if (!requiresConversion)
+                    {
+                        return profile;
+                    }
+
                     return profile;
                 }
             }

+ 44 - 30
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -217,9 +217,9 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
         {
-            List<SubtitleStreamInfo> list = GetSubtitleProfiles(includeSelectedTrackOnly, baseUrl, accessToken);
+            List<SubtitleStreamInfo> list = GetSubtitleProfiles(includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
             List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>();
 
             // First add the selected track
@@ -235,6 +235,11 @@ namespace MediaBrowser.Model.Dlna
         }
 
         public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        {
+            return GetSubtitleProfiles(includeSelectedTrackOnly, false, baseUrl, accessToken);
+        }
+
+        public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
         {
             List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
 
@@ -250,9 +255,7 @@ namespace MediaBrowser.Model.Dlna
                 {
                     if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
                     {
-                        SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks);
-
-                        list.Add(info);
+                        AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
                     }
                 }
             }
@@ -263,9 +266,7 @@ namespace MediaBrowser.Model.Dlna
                 {
                     if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
                     {
-                        SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks);
-
-                        list.Add(info);
+                        AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
                     }
                 }
             }
@@ -273,44 +274,57 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks)
+        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
         {
-            SubtitleStreamInfo info = GetSubtitleStreamInfo(stream);
-
-            if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
+            if (enableAllProfiles)
             {
-                if (MediaSource.Protocol == MediaProtocol.Http)
-                {
-                    info.Url = stream.Path;
-                }
-                else if (!string.IsNullOrEmpty(baseUrl))
+                foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles)
                 {
-                    info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
-                        baseUrl,
-                        ItemId,
-                        MediaSourceId,
-                        StringHelper.ToStringCultureInvariant(stream.Index),
-                        StringHelper.ToStringCultureInvariant(startPositionTicks),
-                        SubtitleFormat);
+                    SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile });
+
+                    list.Add(info);
                 }
             }
+            else
+            {
+                SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles);
 
-            return info;
+                list.Add(info);
+            }
         }
 
-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream)
+        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles)
         {
-            SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile.SubtitleProfiles, Context);
-
-            return new SubtitleStreamInfo
+            SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context);
+            SubtitleStreamInfo info = new SubtitleStreamInfo
             {
                 IsForced = stream.IsForced,
                 Language = stream.Language,
                 Name = stream.Language ?? "Unknown",
-                Format = SubtitleFormat,
+                Format = subtitleProfile.Format,
                 Index = stream.Index,
                 DeliveryMethod = subtitleProfile.Method
             };
+
+            if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
+            {
+                if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format))
+                {
+                    info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
+                        baseUrl,
+                        ItemId,
+                        MediaSourceId,
+                        StringHelper.ToStringCultureInvariant(stream.Index),
+                        StringHelper.ToStringCultureInvariant(startPositionTicks),
+                        subtitleProfile.Format);
+                }
+                else
+                {
+                    info.Url = stream.Path;
+                }
+            }
+
+            return info;
         }
 
         /// <summary>

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

@@ -130,6 +130,12 @@ namespace MediaBrowser.Model.Entities
         /// <value>The index.</value>
         public int Index { get; set; }
 
+        /// <summary>
+        /// Gets or sets the score.
+        /// </summary>
+        /// <value>The score.</value>
+        public int? Score { get; set; }
+        
         /// <summary>
         /// Gets or sets a value indicating whether this instance is external.
         /// </summary>

+ 5 - 4
MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs

@@ -6,15 +6,14 @@ using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Providers;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Model.Providers;
 
 namespace MediaBrowser.Providers.MediaInfo
 {
@@ -23,14 +22,16 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly ILibraryManager _libraryManager;
         private readonly IServerConfigurationManager _config;
         private readonly ISubtitleManager _subtitleManager;
+        private readonly IMediaSourceManager _mediaSourceManager;
         private readonly ILogger _logger;
 
-        public SubtitleScheduledTask(ILibraryManager libraryManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger)
+        public SubtitleScheduledTask(ILibraryManager libraryManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager)
         {
             _libraryManager = libraryManager;
             _config = config;
             _subtitleManager = subtitleManager;
             _logger = logger;
+            _mediaSourceManager = mediaSourceManager;
         }
 
         public string Name
@@ -107,7 +108,7 @@ namespace MediaBrowser.Providers.MediaInfo
                 (options.DownloadMovieSubtitles &&
                 video is Movie))
             {
-                var mediaStreams = video.GetMediaSources(false).First().MediaStreams;
+                var mediaStreams = _mediaSourceManager.GetStaticMediaSources(video, false).First().MediaStreams;
 
                 var downloadedLanguages = await new SubtitleDownloader(_logger,
                     _subtitleManager)

+ 4 - 4
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -261,7 +261,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                     if (user == null)
                     {
-                        dto.MediaSources = hasMediaSources.GetMediaSources(true).ToList();
+                        dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList();
                     }
                     else
                     {
@@ -269,7 +269,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     }
                 }
             }
-
+            
             if (fields.Contains(ItemFields.Studios))
             {
                 AttachStudios(dto, item);
@@ -1280,7 +1280,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     }
                     else
                     {
-                        mediaStreams = iHasMediaSources.GetMediaSources(true).First().MediaStreams;
+                        mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams;
                     }
 
                     dto.MediaStreams = mediaStreams;
@@ -1453,7 +1453,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             var tvChannel = item as LiveTvChannel;
             if (tvChannel != null)
             {
-                dto.MediaSources = tvChannel.GetMediaSources(true).ToList();
+                dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(tvChannel, true).ToList();
             }
 
             var channelItem = item as IChannelItem;

+ 23 - 8
MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs

@@ -135,6 +135,7 @@ namespace MediaBrowser.Server.Implementations.Library
             IEnumerable<MediaSourceInfo> mediaSources;
 
             var hasMediaSources = (IHasMediaSources)item;
+            User user = null;
 
             if (string.IsNullOrWhiteSpace(userId))
             {
@@ -142,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.Library
             }
             else
             {
-                var user = _userManager.GetUserById(userId);
+                user = _userManager.GetUserById(userId);
                 mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
             }
 
@@ -154,6 +155,10 @@ namespace MediaBrowser.Server.Implementations.Library
 
             foreach (var source in dynamicMediaSources)
             {
+                if (user != null)
+                {
+                    SetUserProperties(source, user);
+                }
                 if (source.Protocol == MediaProtocol.File)
                 {
                     source.SupportsDirectStream = File.Exists(source.Path);
@@ -225,6 +230,11 @@ namespace MediaBrowser.Server.Implementations.Library
             return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken);
         }
 
+        public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution)
+        {
+            return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
+        }
+
         public IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution)
         {
             if (item == null)
@@ -288,6 +298,9 @@ namespace MediaBrowser.Server.Implementations.Library
                 preferredSubs,
                 user.Configuration.SubtitleMode,
                 audioLangage);
+
+            MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs,
+                user.Configuration.SubtitleMode, audioLangage);
         }
 
         private IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
@@ -311,11 +324,6 @@ namespace MediaBrowser.Server.Implementations.Library
             .ToList();
         }
 
-        public MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution)
-        {
-            return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
-        }
-
         private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams = new ConcurrentDictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
         private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
 
@@ -428,9 +436,16 @@ namespace MediaBrowser.Server.Implementations.Library
 
             try
             {
-                var tuple = GetProvider(id);
+                LiveStreamInfo current;
+                if (_openStreams.TryGetValue(id, out current))
+                {
+                    if (current.MediaSource.RequiresClosing)
+                    {
+                        var tuple = GetProvider(id);
 
-                await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+                        await tuple.Item1.CloseMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+                    }
+                }
 
                 LiveStreamInfo removed;
                 if (_openStreams.TryRemove(id, out removed))

+ 1 - 1
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -215,7 +215,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 if (request.IsPlayed.HasValue)
                 {
                     var val = request.IsPlayed.Value;
-                    if (i.IsPlayed(currentUser) != val)
+                    if (i is Video && i.IsPlayed(currentUser) != val)
                     {
                         return false;
                     }

+ 4 - 2
MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -17,11 +17,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         private readonly ILiveTvManager _liveTvManager;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly ILogger _logger;
+        private readonly IMediaSourceManager _mediaSourceManager;
 
-        public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager)
+        public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager, IMediaSourceManager mediaSourceManager)
         {
             _liveTvManager = liveTvManager;
             _jsonSerializer = jsonSerializer;
+            _mediaSourceManager = mediaSourceManager;
             _logger = logManager.GetLogger(GetType().Name);
         }
 
@@ -63,7 +65,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 var hasMediaSources = (IHasMediaSources)item;
 
-                sources = hasMediaSources.GetMediaSources(false)
+                sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
                    .ToList();
             }
 

+ 1 - 0
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -52,6 +52,7 @@
     "HeaderAddUser": "Add User",
     "LabelAddConnectSupporterHelp": "To add a user who isn't listed, you'll need to first link their account to Emby Connect from their user profile page.",
     "LabelPinCode": "Pin code:",
+    "OptionHideWatchedContentFromLatestMedia": "Hide watched content from latest media",
     "ButtonOk": "Ok",
     "ButtonCancel": "Cancel",
     "ButtonExit": "Exit",

+ 2 - 3
MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs

@@ -198,8 +198,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2";
             codecProfiles.Add(new CodecProfile
             {
-                Type = CodecType.Audio,
-                Codec = "mpeg4",
+                Type = CodecType.VideoAudio,
                 Conditions = new[]
                     {
                         new ProfileCondition
@@ -207,7 +206,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                             Condition = ProfileConditionType.LessThanEqual,
                             Property = ProfileConditionValue.AudioChannels,
                             Value = maxAudioChannels,
-                            IsRequired = false
+                            IsRequired = true
                         },
                         new ProfileCondition
                         {

+ 2 - 2
MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs

@@ -495,7 +495,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             // No sense creating external subs if we're already burning one into the video
             var externalSubs = streamInfo.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ?
                 new List<SubtitleStreamInfo>() :
-                streamInfo.GetExternalSubtitles(false, null, null);
+                streamInfo.GetExternalSubtitles(false, true, null, null);
 
             // Mark as requiring conversion if transcoding the video, or if any subtitles need to be extracted
             var requiresVideoTranscoding = streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting;
@@ -823,7 +823,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
             var hasMediaSources = item as IHasMediaSources;
 
-            var mediaSources = hasMediaSources.GetMediaSources(false).ToList();
+            var mediaSources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false).ToList();
 
             var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
                 ? new string[] { }

+ 1 - 1
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -530,7 +530,7 @@ namespace MediaBrowser.Server.Startup.Common
             RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
             RegisterSingleInstance<IAuthService>(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager));
 
-            SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer);
+            SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager);
             RegisterSingleInstance(SubtitleEncoder);
 
             await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false);