From ec4000404d59ee0e8342805cf7ce0c4ca04023b4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Jan 2014 08:52:01 -0500 Subject: [PATCH] rework text subtitles --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 41 +++-- .../Playback/BaseStreamingService.cs | 164 ++++++++++++------ .../Playback/Hls/BaseHlsService.cs | 6 +- .../Playback/Hls/VideoHlsService.cs | 16 +- .../Playback/Progressive/AudioService.cs | 2 +- .../BaseProgressiveStreamingService.cs | 36 ++-- .../Playback/Progressive/VideoService.cs | 21 +-- .../MediaInfo/IMediaEncoder.cs | 6 +- MediaBrowser.Common/Net/MimeTypes.cs | 2 +- .../MediaInfo/FFMpegManager.cs | 5 +- MediaBrowser.Model/LiveTv/ChannelQuery.cs | 12 ++ .../Movies/ManualMovieDbImageProvider.cs | 20 ++- .../ManualMovieDbPersonImageProvider.cs | 2 +- .../Movies/MovieDbPersonProvider.cs | 4 +- .../Movies/MovieDbProvider.cs | 27 ++- .../Movies/MovieUpdatesPrescanTask.cs | 2 +- .../LiveTv/LiveTvManager.cs | 29 +++- .../MediaEncoder/MediaEncoder.cs | 40 ++--- .../Session/SessionWebSocketListener.cs | 6 + MediaBrowser.WebDashboard/ApiClient.js | 25 ++- MediaBrowser.WebDashboard/packages.config | 2 +- 21 files changed, 293 insertions(+), 175 deletions(-) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 98cc8dddaf..b530a1e8bc 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -1,11 +1,11 @@ -using System.Globalization; -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; using ServiceStack; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -27,6 +27,20 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string UserId { get; set; } + + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } } [Route("/LiveTv/Channels/{Id}", "GET")] @@ -116,26 +130,26 @@ namespace MediaBrowser.Api.LiveTv public string SeriesTimerId { get; set; } } - [Route("/LiveTv/Programs", "GET")] + [Route("/LiveTv/Programs", "GET,POST")] [Api(Description = "Gets available live tv epgs..")] public class GetPrograms : IReturn> { - [ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string ChannelIds { get; set; } - [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string UserId { get; set; } - [ApiMember(Name = "MinStartDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "MinStartDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string MinStartDate { get; set; } - [ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string MaxStartDate { get; set; } - [ApiMember(Name = "MinEndDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "MinEndDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string MinEndDate { get; set; } - [ApiMember(Name = "MaxEndDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "MaxEndDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string MaxEndDate { get; set; } } @@ -260,7 +274,9 @@ namespace MediaBrowser.Api.LiveTv var result = _liveTvManager.GetChannels(new ChannelQuery { ChannelType = request.Type, - UserId = request.UserId + UserId = request.UserId, + StartIndex = request.StartIndex, + Limit = request.Limit }, CancellationToken.None).Result; @@ -309,6 +325,11 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } + public object Post(GetPrograms request) + { + return Get(request); + } + public object Get(GetRecordings request) { var result = _liveTvManager.GetRecordings(new RecordingQuery diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index dc34fca77c..c400da5bf3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -253,25 +253,44 @@ namespace MediaBrowser.Api.Playback return returnFirstIfNoIndex ? streams.FirstOrDefault() : null; } + protected EncodingQuality GetQualitySetting() + { + var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality; + + if (quality == EncodingQuality.Auto) + { + var cpuCount = Environment.ProcessorCount; + + if (cpuCount >= 4) + { + return EncodingQuality.HighQuality; + } + + return EncodingQuality.HighSpeed; + } + + return quality; + } + /// /// Gets the number of threads. /// /// System.Int32. /// Unrecognized MediaEncodingQuality value. - protected int GetNumberOfThreads() + protected int GetNumberOfThreads(bool isWebm) { - var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality; + // Webm: http://www.webmproject.org/docs/encoder-parameters/ + // The decoder will usually automatically use an appropriate number of threads according to how many cores are available but it can only use multiple threads + // for the coefficient data if the encoder selected --token-parts > 0 at encode time. - switch (quality) + switch (GetQualitySetting()) { - case EncodingQuality.Auto: - return 0; case EncodingQuality.HighSpeed: return 2; case EncodingQuality.HighQuality: - return 2; + return isWebm ? Math.Min(3, Environment.ProcessorCount - 1) : 2; case EncodingQuality.MaxQuality: - return 0; + return isWebm ? Math.Max(2, Environment.ProcessorCount - 1) : 0; default: throw new Exception("Unrecognized MediaEncodingQuality value."); } @@ -285,30 +304,74 @@ namespace MediaBrowser.Api.Playback /// System.String. protected string GetVideoQualityParam(StreamState state, string videoCodec) { - var args = string.Empty; - // webm if (videoCodec.Equals("libvpx", StringComparison.OrdinalIgnoreCase)) { - args = "-speed 16 -quality good -profile:v 0 -slices 8"; + // http://www.webmproject.org/docs/encoder-parameters/ + return "-speed 16 -quality good -profile:v 0 -slices 8"; } // asf/wmv - else if (videoCodec.Equals("wmv2", StringComparison.OrdinalIgnoreCase)) + if (videoCodec.Equals("wmv2", StringComparison.OrdinalIgnoreCase)) { - args = "-g 100 -qmax 15"; + return "-g 100 -qmax 15"; } - else if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase)) + if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase)) { - args = "-preset superfast"; - } - else if (videoCodec.Equals("mpeg4", StringComparison.OrdinalIgnoreCase)) - { - args = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; + return "-preset superfast"; } - return args.Trim(); + if (videoCodec.Equals("mpeg4", StringComparison.OrdinalIgnoreCase)) + { + return "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; + } + + return string.Empty; + } + + protected string GetAudioFilterParam(StreamState state, bool isHls) + { + var volParam = string.Empty; + var audioSampleRate = string.Empty; + + var channels = GetNumAudioChannelsParam(state.Request, state.AudioStream); + + // Boost volume to 200% when downsampling from 6ch to 2ch + if (channels.HasValue && channels.Value <= 2 && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) + { + volParam = ",volume=2.000000"; + } + + if (state.Request.AudioSampleRate.HasValue) + { + audioSampleRate = state.Request.AudioSampleRate.Value + ":"; + } + + var adelay = isHls ? "adelay=1," : string.Empty; + + var pts = string.Empty; + + if (state.SubtitleStream != null) + { + if (state.SubtitleStream.Codec.IndexOf("srt", StringComparison.OrdinalIgnoreCase) != -1 || + state.SubtitleStream.Codec.IndexOf("subrip", StringComparison.OrdinalIgnoreCase) != -1 || + string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || + string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)) + { + var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds; + + pts = string.Format(",asetpts=PTS-{0}/TB", + Math.Round(seconds).ToString(UsCulture)); + } + } + + return string.Format("-af \"{0}aresample={1}async=1{2}{3}\"", + + adelay, + audioSampleRate, + volParam, + pts); } /// @@ -323,6 +386,7 @@ namespace MediaBrowser.Api.Playback // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/ var assSubtitleParam = string.Empty; + var copyTsParam = string.Empty; var request = state.VideoRequest; @@ -333,7 +397,8 @@ namespace MediaBrowser.Api.Playback string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)) { - assSubtitleParam = GetTextSubtitleParam(state, request.StartTimeTicks, performTextSubtitleConversion); + assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion); + copyTsParam = " -copyts"; } } @@ -343,7 +408,7 @@ namespace MediaBrowser.Api.Playback var widthParam = request.Width.Value.ToString(UsCulture); var heightParam = request.Height.Value.ToString(UsCulture); - return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam); + return string.Format("{3} -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam, copyTsParam); } var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase); @@ -354,8 +419,8 @@ namespace MediaBrowser.Api.Playback var widthParam = request.Width.Value.ToString(UsCulture); return isH264Output ? - string.Format(" -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", widthParam, assSubtitleParam) : - string.Format(" -vf \"scale={0}:-1{1}\"", widthParam, assSubtitleParam); + string.Format("{2} -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", widthParam, assSubtitleParam, copyTsParam) : + string.Format("{2} -vf \"scale={0}:-1{1}\"", widthParam, assSubtitleParam, copyTsParam); } // If a fixed height was requested @@ -364,8 +429,8 @@ namespace MediaBrowser.Api.Playback var heightParam = request.Height.Value.ToString(UsCulture); return isH264Output ? - string.Format(" -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", heightParam, assSubtitleParam) : - string.Format(" -vf \"scale=-1:{0}{1}\"", heightParam, assSubtitleParam); + string.Format("{2} -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", heightParam, assSubtitleParam, copyTsParam) : + string.Format("{2} -vf \"scale=-1:{0}{1}\"", heightParam, assSubtitleParam, copyTsParam); } // If a max width was requested @@ -374,8 +439,8 @@ namespace MediaBrowser.Api.Playback var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); return isH264Output ? - string.Format(" -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", maxWidthParam, assSubtitleParam) : - string.Format(" -vf \"scale=min(iw\\,{0}):-1{1}\"", maxWidthParam, assSubtitleParam); + string.Format("{2} -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", maxWidthParam, assSubtitleParam, copyTsParam) : + string.Format("{2} -vf \"scale=min(iw\\,{0}):-1{1}\"", maxWidthParam, assSubtitleParam, copyTsParam); } // If a max height was requested @@ -384,8 +449,8 @@ namespace MediaBrowser.Api.Playback var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); return isH264Output ? - string.Format(" -vf \"scale=trunc(oh*a*2)/2:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam) : - string.Format(" -vf \"scale=-1:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam); + string.Format("{2} -vf \"scale=trunc(oh*a*2)/2:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam, copyTsParam) : + string.Format("{2} -vf \"scale=-1:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam, copyTsParam); } if (state.VideoStream == null) @@ -408,45 +473,45 @@ namespace MediaBrowser.Api.Playback var widthParam = outputSize.Width.ToString(UsCulture); var heightParam = outputSize.Height.ToString(UsCulture); - return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam); + return string.Format("{3} -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam, copyTsParam); } // Otherwise use -vf scale since ffmpeg will ensure internally that the aspect ratio is preserved - return string.Format(" -vf \"scale={0}:-1{1}\"", Convert.ToInt32(outputSize.Width), assSubtitleParam); + return string.Format("{2} -vf \"scale={0}:-1{1}\"", Convert.ToInt32(outputSize.Width), assSubtitleParam, copyTsParam); } /// /// Gets the text subtitle param. /// /// The state. - /// The start time ticks. /// if set to true [perform conversion]. /// System.String. - protected string GetTextSubtitleParam(StreamState state, long? startTimeTicks, bool performConversion) + protected string GetTextSubtitleParam(StreamState state, bool performConversion) { - var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, startTimeTicks, performConversion) : - GetExtractedAssPath(state, startTimeTicks, performConversion); + var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, performConversion) : + GetExtractedAssPath(state, performConversion); if (string.IsNullOrEmpty(path)) { return string.Empty; } - return string.Format(",ass='{0}'", path.Replace('\\', '/').Replace(":/", "\\:/")); + var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds; + + return string.Format(",ass='{0}',setpts=PTS -{1}/TB", + path.Replace('\\', '/').Replace(":/", "\\:/"), + Math.Round(seconds).ToString(UsCulture)); } /// /// Gets the extracted ass path. /// /// The state. - /// The start time ticks. /// if set to true [perform conversion]. /// System.String. - private string GetExtractedAssPath(StreamState state, long? startTimeTicks, bool performConversion) + private string GetExtractedAssPath(StreamState state, bool performConversion) { - var offset = TimeSpan.FromTicks(startTimeTicks ?? 0); - - var path = FFMpegManager.Instance.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream, offset, ".ass"); + var path = FFMpegManager.Instance.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream, ".ass"); if (performConversion) { @@ -460,7 +525,7 @@ namespace MediaBrowser.Api.Playback Directory.CreateDirectory(parentPath); - var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, offset, path, CancellationToken.None); + var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, path, CancellationToken.None); Task.WaitAll(task); } @@ -478,14 +543,11 @@ namespace MediaBrowser.Api.Playback /// /// The media path. /// The subtitle stream. - /// The start time ticks. /// if set to true [perform conversion]. /// System.String. - private string GetConvertedAssPath(string mediaPath, MediaStream subtitleStream, long? startTimeTicks, bool performConversion) + private string GetConvertedAssPath(string mediaPath, MediaStream subtitleStream, bool performConversion) { - var offset = TimeSpan.FromTicks(startTimeTicks ?? 0); - - var path = FFMpegManager.Instance.GetSubtitleCachePath(mediaPath, subtitleStream, offset, ".ass"); + var path = FFMpegManager.Instance.GetSubtitleCachePath(mediaPath, subtitleStream, ".ass"); if (performConversion) { @@ -495,7 +557,7 @@ namespace MediaBrowser.Api.Playback Directory.CreateDirectory(parentPath); - var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, offset, CancellationToken.None); + var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, CancellationToken.None); Task.WaitAll(task); } @@ -534,9 +596,9 @@ namespace MediaBrowser.Api.Playback videoSizeParam = string.Format(",scale={0}:{1}", state.VideoStream.Width.Value.ToString(UsCulture), state.VideoStream.Height.Value.ToString(UsCulture)); } - return string.Format(" -filter_complex \"[0:{0}]format=yuva444p{3},lut=u=128:v=128:y=gammaval(.3)[sub] ; [0:{1}] [sub] overlay{2}\"", - state.SubtitleStream.Index, - state.VideoStream.Index, + return string.Format(" -filter_complex \"[0:{0}]format=yuva444p{3},lut=u=128:v=128:y=gammaval(.3)[sub] ; [0:{1}] [sub] overlay{2}\"", + state.SubtitleStream.Index, + state.VideoStream.Index, outputSizeParam, videoSizeParam); } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 9abedde4b4..9c42c77292 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.Api.Playback.Hls if (isPlaylistNewlyCreated) { var minimumSegmentCount = 3; - var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality; + var quality = GetQualitySetting(); if (quality == EncodingQuality.HighSpeed || quality == EncodingQuality.HighQuality) { @@ -267,9 +267,9 @@ namespace MediaBrowser.Api.Playback.Hls var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds); - var threads = GetNumberOfThreads(); + var threads = GetNumberOfThreads(false); - var args = string.Format("{0}{1} {2} {3} -i {4}{5} -threads {6} {7} {8} -sc_threshold 0 {9} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{10}\"", + var args = string.Format("{0}{1} {2} {3} -i {4}{5} -map_metadata -1 -threads {6} {7} {8} -sc_threshold 0 {9} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{10}\"", itsOffset, probeSize, GetUserAgentParam(state.MediaPath), diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 583082500c..8fbb42f24c 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -80,21 +80,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " -ab " + bitrate.Value.ToString(UsCulture); } - var volParam = string.Empty; - var audioSampleRate = string.Empty; - - // Boost volume to 200% when downsampling from 6ch to 2ch - if (channels.HasValue && channels.Value <= 2 && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) - { - volParam = ",volume=2.000000"; - } - - if (state.Request.AudioSampleRate.HasValue) - { - audioSampleRate = state.Request.AudioSampleRate.Value + ":"; - } - - args += string.Format(" -af \"adelay=1,aresample={0}async=1{1}\"", audioSampleRate, volParam); + args += " " + GetAudioFilterParam(state, true); return args; } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index baf7f48fe1..050c06627d 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive const string vn = " -vn"; - var threads = GetNumberOfThreads(); + var threads = GetNumberOfThreads(false); return string.Format("{0} -i {1}{2} -threads {3}{4} {5} -id3v2_version 3 -write_id3v1 1 \"{6}\"", GetFastSeekCommandLineParameter(request), diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index e367801d25..440632825e 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -106,6 +107,7 @@ namespace MediaBrowser.Api.Playback.Progressive var transferMode = GetHeader("transferMode.dlna.org"); responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; + responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; var contentFeatures = string.Empty; var extension = GetOutputFileExtension(state); @@ -118,22 +120,22 @@ namespace MediaBrowser.Api.Playback.Progressive const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; - //if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=MP3"; - //} - //else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=AAC_ISO"; - //} - //else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=WMABASE"; - //} - //else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=AVI"; - //} + if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MP3"; + } + else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AAC_ISO"; + } + else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=WMABASE"; + } + else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AVI"; + } //else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) //{ // contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC"; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index cdc8e2ffb0..c3ec02a59f 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -104,9 +104,9 @@ namespace MediaBrowser.Api.Playback.Progressive format = " -f mp4 -movflags frag_keyframe+empty_moov"; } - var threads = string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase) ? 2 : GetNumberOfThreads(); + var threads = GetNumberOfThreads(string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)); - return string.Format("{0} {1} {2} -i {3}{4}{5} {6} {7} -threads {8} {9}{10} \"{11}\"", + return string.Format("{0} {1} {2} -i {3}{4}{5} {6} {7} -map_metadata -1 -threads {8} {9}{10} \"{11}\"", probeSize, GetUserAgentParam(state.MediaPath), GetFastSeekCommandLineParameter(state.Request), @@ -170,6 +170,7 @@ namespace MediaBrowser.Api.Playback.Progressive if (bitrate.HasValue) { qualityParam += string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); + //qualityParam += string.Format(" -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); } if (!string.IsNullOrEmpty(qualityParam)) @@ -238,21 +239,7 @@ namespace MediaBrowser.Api.Playback.Progressive args += " -ab " + bitrate.Value.ToString(UsCulture); } - var volParam = string.Empty; - var AudioSampleRate = string.Empty; - - // Boost volume to 200% when downsampling from 6ch to 2ch - if (channels.HasValue && channels.Value <= 2 && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5) - { - volParam = ",volume=2.000000"; - } - - if (state.Request.AudioSampleRate.HasValue) - { - AudioSampleRate = state.Request.AudioSampleRate.Value + ":"; - } - - args += string.Format(" -af \"aresample={0}async=1{1}\"", AudioSampleRate, volParam); + args += " " + GetAudioFilterParam(state, true); return args; } diff --git a/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs b/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs index 31fa78fdb8..82643779b3 100644 --- a/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs +++ b/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs @@ -40,11 +40,10 @@ namespace MediaBrowser.Common.MediaInfo /// The input files. /// The type. /// Index of the subtitle stream. - /// The offset. /// The output path. /// The cancellation token. /// Task. - Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, TimeSpan offset, string outputPath, CancellationToken cancellationToken); + Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, string outputPath, CancellationToken cancellationToken); /// /// Converts the text subtitle to ass. @@ -52,10 +51,9 @@ namespace MediaBrowser.Common.MediaInfo /// The input path. /// The output path. /// The language. - /// The offset. /// The cancellation token. /// Task. - Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, TimeSpan offset, CancellationToken cancellationToken); + Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken); /// /// Gets the media info. diff --git a/MediaBrowser.Common/Net/MimeTypes.cs b/MediaBrowser.Common/Net/MimeTypes.cs index 47536a341e..85b9b1f38f 100644 --- a/MediaBrowser.Common/Net/MimeTypes.cs +++ b/MediaBrowser.Common/Net/MimeTypes.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Net } if (ext.Equals(".avi", StringComparison.OrdinalIgnoreCase)) { - return "video/avi"; + return "video/x-msvideo"; } if (ext.Equals(".m4v", StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index ced53299d8..6442229499 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -235,12 +235,11 @@ namespace MediaBrowser.Controller.MediaInfo /// /// The media path. /// The subtitle stream. - /// The offset. /// The output extension. /// System.String. - public string GetSubtitleCachePath(string mediaPath, MediaStream subtitleStream, TimeSpan? offset, string outputExtension) + public string GetSubtitleCachePath(string mediaPath, MediaStream subtitleStream, string outputExtension) { - var ticksParam = offset.HasValue ? "_" + offset.Value.Ticks : ""; + var ticksParam = string.Empty; if (subtitleStream.IsExternal) { diff --git a/MediaBrowser.Model/LiveTv/ChannelQuery.cs b/MediaBrowser.Model/LiveTv/ChannelQuery.cs index 578f3039bb..eb3b20ce3b 100644 --- a/MediaBrowser.Model/LiveTv/ChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/ChannelQuery.cs @@ -17,5 +17,17 @@ namespace MediaBrowser.Model.LiveTv /// /// The user identifier. public string UserId { get; set; } + + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + public int? Limit { get; set; } } } diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index b9cabded7d..21e50f4001 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Providers.Movies { var list = new List(); - var results = FetchImages(item, _jsonSerializer); + var results = await FetchImages((BaseItem)item, _jsonSerializer, cancellationToken).ConfigureAwait(false); if (results == null) { @@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Movies var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - list.AddRange(GetPosters(results, item).Select(i => new RemoteImageInfo + list.AddRange(GetPosters(results).Select(i => new RemoteImageInfo { Url = tmdbImageUrl + i.file_path, CommunityRating = i.vote_average, @@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Movies RatingType = RatingType.Score })); - list.AddRange(GetBackdrops(results, item).Select(i => new RemoteImageInfo + list.AddRange(GetBackdrops(results).Select(i => new RemoteImageInfo { Url = tmdbImageUrl + i.file_path, CommunityRating = i.vote_average, @@ -119,9 +119,8 @@ namespace MediaBrowser.Providers.Movies /// Gets the posters. /// /// The images. - /// The item. /// IEnumerable{MovieDbProvider.Poster}. - private IEnumerable GetPosters(MovieDbProvider.Images images, IHasImages item) + private IEnumerable GetPosters(MovieDbProvider.Images images) { return images.posters ?? new List(); } @@ -130,9 +129,8 @@ namespace MediaBrowser.Providers.Movies /// Gets the backdrops. /// /// The images. - /// The item. /// IEnumerable{MovieDbProvider.Backdrop}. - private IEnumerable GetBackdrops(MovieDbProvider.Images images, IHasImages item) + private IEnumerable GetBackdrops(MovieDbProvider.Images images) { var eligibleBackdrops = images.backdrops == null ? new List() : images.backdrops @@ -147,10 +145,14 @@ namespace MediaBrowser.Providers.Movies /// /// The item. /// The json serializer. + /// The cancellation token. /// Task{MovieImages}. - private MovieDbProvider.Images FetchImages(IHasImages item, IJsonSerializer jsonSerializer) + private async Task FetchImages(BaseItem item, IJsonSerializer jsonSerializer, + CancellationToken cancellationToken) { - var path = MovieDbProvider.Current.GetDataFilePath((BaseItem)item); + await MovieDbProvider.Current.EnsureMovieInfo(item, cancellationToken).ConfigureAwait(false); + + var path = MovieDbProvider.Current.GetDataFilePath(item); if (!string.IsNullOrEmpty(path)) { diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs index 453284751c..4d3a5baac1 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(id)) { - await MovieDbPersonProvider.Current.DownloadPersonInfoIfNeeded(id, cancellationToken).ConfigureAwait(false); + await MovieDbPersonProvider.Current.EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false); var dataFilePath = MovieDbPersonProvider.GetPersonDataFilePath(_config.ApplicationPaths, id); diff --git a/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs b/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs index c16c504125..70ad6611a1 100644 --- a/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs @@ -235,7 +235,7 @@ namespace MediaBrowser.Providers.Movies /// Task. private async Task FetchInfo(Person person, string id, bool isForcedRefresh, CancellationToken cancellationToken) { - await DownloadPersonInfoIfNeeded(id, cancellationToken).ConfigureAwait(false); + await EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false); if (isForcedRefresh || !HasAltMeta(person)) { @@ -249,7 +249,7 @@ namespace MediaBrowser.Providers.Movies } } - internal async Task DownloadPersonInfoIfNeeded(string id, CancellationToken cancellationToken) + internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken) { var personDataPath = GetPersonDataPath(ConfigurationManager.ApplicationPaths, id); diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index fed1b444e8..8dd64fd14c 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -558,6 +558,31 @@ namespace MediaBrowser.Providers.Movies JsonSerializer.SerializeToFile(mainResult, dataFilePath); } + internal Task EnsureMovieInfo(BaseItem item, CancellationToken cancellationToken) + { + var path = GetDataFilePath(item); + + var fileInfo = _fileSystem.GetFileSystemInfo(path); + + if (fileInfo.Exists) + { + // If it's recent or automatic updates are enabled, don't re-download + if (ConfigurationManager.Configuration.EnableTmdbUpdates || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7) + { + return Task.FromResult(true); + } + } + + var id = item.GetProviderId(MetadataProviders.Tmdb); + + if (string.IsNullOrEmpty(id)) + { + return Task.FromResult(true); + } + + return DownloadMovieInfo(id, item is BoxSet, item.GetPreferredMetadataLanguage(), cancellationToken); + } + /// /// Gets the data file path. /// @@ -575,7 +600,7 @@ namespace MediaBrowser.Providers.Movies return GetDataFilePath(item is BoxSet, id, item.GetPreferredMetadataLanguage()); } - internal string GetDataFilePath(bool isBoxset, string tmdbId, string preferredLanguage) + private string GetDataFilePath(bool isBoxset, string tmdbId, string preferredLanguage) { var path = GetMovieDataPath(ConfigurationManager.ApplicationPaths, isBoxset, tmdbId); diff --git a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs index 291d2ff4dd..d9a367e2cc 100644 --- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.Movies /// Task. public async Task Run(IProgress progress, CancellationToken cancellationToken) { - if (!_config.Configuration.EnableInternetProviders) + if (!_config.Configuration.EnableInternetProviders && !_config.Configuration.EnableTmdbUpdates) { progress.Report(100); return; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 7dc210cccf..b6622a3100 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv }); } - var returnChannels = channels.OrderBy(i => + channels = channels.OrderBy(i => { double number = 0; @@ -109,14 +109,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv return number; - }).ThenBy(i => i.Name) - .Select(i => _tvDtoService.GetChannelInfoDto(i, GetCurrentProgram(i.ChannelInfo.Id), user)) - .ToArray(); + }).ThenBy(i => i.Name); + + var allChannels = channels.ToList(); + IEnumerable allEnumerable = allChannels; + + if (query.StartIndex.HasValue) + { + allEnumerable = allEnumerable.Skip(query.StartIndex.Value); + } + + if (query.Limit.HasValue) + { + allEnumerable = allEnumerable.Take(query.Limit.Value); + } + + var returnChannels = allEnumerable + .Select(i => _tvDtoService.GetChannelInfoDto(i, GetCurrentProgram(i.ChannelInfo.Id), user)) + .ToArray(); var result = new QueryResult { Items = returnChannels, - TotalRecordCount = returnChannels.Length + TotalRecordCount = allChannels.Count }; return Task.FromResult(result); @@ -575,9 +590,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv .Where(i => _tvDtoService.GetInternalSeriesTimerId(currentServiceName, i.SeriesTimerId) == guid); } - IEnumerable entities = await GetEntities(recordings, service.Name, cancellationToken).ConfigureAwait(false); + recordings = recordings.OrderByDescending(i => i.StartDate); - entities = entities.OrderByDescending(i => i.RecordingInfo.StartDate); + IEnumerable entities = await GetEntities(recordings, service.Name, cancellationToken).ConfigureAwait(false); if (user != null) { diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 990b695ae4..bcc857a805 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); } - + /// /// Gets the media info. /// @@ -378,11 +378,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The input path. /// The output path. /// The language. - /// The offset. /// The cancellation token. /// Task. - public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, TimeSpan offset, - CancellationToken cancellationToken) + public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -392,7 +390,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { if (!File.Exists(outputPath)) { - await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language, offset).ConfigureAwait(false); + await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language).ConfigureAwait(false); } } finally @@ -409,13 +407,12 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The input path. /// The output path. /// The language. - /// The offset. /// Task. /// inputPath /// or /// outputPath /// - private async Task ConvertTextSubtitleToAssInternal(string inputPath, string outputPath, string language, TimeSpan offset) + private async Task ConvertTextSubtitleToAssInternal(string inputPath, string outputPath, string language) { if (string.IsNullOrEmpty(inputPath)) { @@ -427,8 +424,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder throw new ArgumentNullException("outputPath"); } - - var slowSeekParam = offset.TotalSeconds > 0 ? " -ss " + offset.TotalSeconds.ToString(UsCulture) : string.Empty; var encodingParam = string.IsNullOrEmpty(language) ? string.Empty : GetSubtitleLanguageEncodingParam(language) + " "; @@ -444,7 +439,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder UseShellExecute = false, FileName = FFMpegPath, Arguments = - string.Format("{0} -i \"{1}\" {2} -c:s ass \"{3}\"", encodingParam, inputPath, slowSeekParam, outputPath), + string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false @@ -557,7 +552,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder return string.Empty; } - + /// /// Gets the subtitle language encoding param. /// @@ -598,7 +593,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder case "vie": return "-sub_charenc windows-1258"; case "kor": - return "-sub_charenc cp949"; + return "-sub_charenc cp949"; default: return "-sub_charenc windows-1252"; } @@ -610,12 +605,11 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The input files. /// The type. /// Index of the subtitle stream. - /// The offset. /// The output path. /// The cancellation token. /// Task. /// Must use inputPath list overload - public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, TimeSpan offset, string outputPath, CancellationToken cancellationToken) + public async Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -625,7 +619,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { if (!File.Exists(outputPath)) { - await ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, offset, outputPath, cancellationToken).ConfigureAwait(false); + await ExtractTextSubtitleInternal(GetInputArgument(inputFiles, type), subtitleStreamIndex, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -639,7 +633,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// /// The input path. /// Index of the subtitle stream. - /// The offset. /// The output path. /// The cancellation token. /// Task. @@ -649,7 +642,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// or /// cancellationToken /// - private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, TimeSpan offset, string outputPath, CancellationToken cancellationToken) + private async Task ExtractTextSubtitleInternal(string inputPath, int subtitleStreamIndex, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) { @@ -661,9 +654,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder throw new ArgumentNullException("outputPath"); } - - var slowSeekParam = GetSlowSeekCommandLineParameter(offset); - var fastSeekParam = GetFastSeekCommandLineParameter(offset); var process = new Process { @@ -676,7 +666,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder RedirectStandardError = true, FileName = FFMpegPath, - Arguments = string.Format(" {0} -i {1} {2} -map 0:{3} -an -vn -c:s ass \"{4}\"", fastSeekParam, inputPath, slowSeekParam, subtitleStreamIndex, outputPath), + Arguments = string.Format("-i {0} -map 0:{1} -an -vn -c:s ass \"{2}\"", inputPath, subtitleStreamIndex, outputPath), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false } @@ -872,8 +862,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder switch (threedFormat.Value) { case Video3DFormat.HalfSideBySide: - vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; - // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. + vf = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; + // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. break; case Video3DFormat.FullSideBySide: vf = "crop=iw/2:ih:0:0,setdar=dar=a,,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; @@ -882,7 +872,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder case Video3DFormat.HalfTopAndBottom: vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600 - break; + break; case Video3DFormat.FullTopAndBottom: vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,scale=600:600/dar"; // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made the scale width to 600 @@ -892,7 +882,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf) : - string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf); + string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, outputPath, vf); var probeSize = GetProbeSizeArgument(type); diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index d693f90e24..e5fbc7d9fa 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs @@ -90,6 +90,12 @@ namespace MediaBrowser.Server.Implementations.Session var vals = message.Data.Split('|'); + if (vals.Length < 3) + { + _logger.Error("Client sent invalid identity message."); + return; + } + var client = vals[0]; var deviceId = vals[1]; var version = vals[2]; diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index f2b2d6b7b1..cbe815c03a 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -436,13 +436,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi self.getLiveTvPrograms = function (options) { - var url = self.getUrl("LiveTv/Programs", options || {}); + options = options || {}; + + if (options.channelIds) { - return self.ajax({ - type: "GET", - url: url, - dataType: "json" - }); + return self.ajax({ + type: "POST", + url: self.getUrl("LiveTv/Programs"), + data: JSON.stringify(options), + contentType: "application/json", + dataType: "json" + }); + + } else { + + return self.ajax({ + type: "GET", + url: self.getUrl("LiveTv/Programs", options), + dataType: "json" + }); + } }; self.getLiveTvRecordings = function (options) { diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 3bb7e71050..c47d67815e 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file