diff options
24 files changed, 365 insertions, 132 deletions
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 4465be97a..3364c3c6b 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/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); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 8c4bcf0a3..75321f872 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/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)) { diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 6eba19545..0930c0002 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/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; } } } diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 58cfa086e..ff79bb48f 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/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(); } diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index cdfd00ce9..38eae2577 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/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, diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 37c2bf4d2..e4a2cd007 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/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); } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs index 57fddb2b1..a006a1723 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs +++ b/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; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index b364414d1..3b1cdb542 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/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 { diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 3eb091a19..38c0f71cc 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/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); diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 8266f4d3a..78ac92f25 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/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)) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7d74c51ba..14d3e7284 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/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"); + } } } diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index a78161140..98641636a 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/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. diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 1cc37de57..b27138b4a 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/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; } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 9bfa684be..3977a9ab0 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/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 @@ -236,6 +236,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>(); // HLS will preserve timestamps so we can just grab the full subtitle stream @@ -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> diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 760829ebf..dfeed7450 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -131,6 +131,12 @@ namespace MediaBrowser.Model.Entities 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> /// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value> diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 2852f5719..0f092b554 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/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) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 756ff6917..dc6f4a525 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/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; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index e832142a9..66eeb61f7 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/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)) diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index e63a27551..757f08562 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/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; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 5de4cf499..d549cad46 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/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(); } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index f52d8a9fe..0cff99c5d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/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", diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 43fb10df0..73400f834 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/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 { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 7eb015ae7..271b2bb39 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/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[] { } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index aad88d022..df7b3f061 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/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); |
