diff options
21 files changed, 291 insertions, 173 deletions
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 98cc8ddda..b530a1e8b 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; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + [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; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + [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<QueryResult<ProgramInfoDto>> { - [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 dc34fca77..c400da5bf 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; + } + /// <summary> /// Gets the number of threads. /// </summary> /// <returns>System.Int32.</returns> /// <exception cref="System.Exception">Unrecognized MediaEncodingQuality value.</exception> - 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 /// <returns>System.String.</returns> 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)) + { + return "-g 100 -qmax 15"; + } + + if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase)) + { + return "-preset superfast"; + } + + if (videoCodec.Equals("mpeg4", StringComparison.OrdinalIgnoreCase)) { - args = "-g 100 -qmax 15"; + return "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; } - else if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase)) + 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) { - args = "-preset superfast"; + volParam = ",volume=2.000000"; } - else if (videoCodec.Equals("mpeg4", StringComparison.OrdinalIgnoreCase)) + + if (state.Request.AudioSampleRate.HasValue) { - args = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; + audioSampleRate = state.Request.AudioSampleRate.Value + ":"; } - return args.Trim(); + 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); } /// <summary> @@ -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); } /// <summary> /// Gets the text subtitle param. /// </summary> /// <param name="state">The state.</param> - /// <param name="startTimeTicks">The start time ticks.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> /// <returns>System.String.</returns> - 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)); } /// <summary> /// Gets the extracted ass path. /// </summary> /// <param name="state">The state.</param> - /// <param name="startTimeTicks">The start time ticks.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> /// <returns>System.String.</returns> - 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 /// </summary> /// <param name="mediaPath">The media path.</param> /// <param name="subtitleStream">The subtitle stream.</param> - /// <param name="startTimeTicks">The start time ticks.</param> /// <param name="performConversion">if set to <c>true</c> [perform conversion].</param> /// <returns>System.String.</returns> - 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 9abedde4b..9c42c7729 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 583082500..8fbb42f24 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 baf7f48fe..050c06627 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 e367801d2..440632825 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 cdc8e2ffb..c3ec02a59 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 31fa78fdb..82643779b 100644 --- a/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs +++ b/MediaBrowser.Common/MediaInfo/IMediaEncoder.cs @@ -40,11 +40,10 @@ namespace MediaBrowser.Common.MediaInfo /// <param name="inputFiles">The input files.</param> /// <param name="type">The type.</param> /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> - /// <param name="offset">The offset.</param> /// <param name="outputPath">The output path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - 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); /// <summary> /// Converts the text subtitle to ass. @@ -52,10 +51,9 @@ namespace MediaBrowser.Common.MediaInfo /// <param name="inputPath">The input path.</param> /// <param name="outputPath">The output path.</param> /// <param name="language">The language.</param> - /// <param name="offset">The offset.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, TimeSpan offset, CancellationToken cancellationToken); + Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken); /// <summary> /// Gets the media info. diff --git a/MediaBrowser.Common/Net/MimeTypes.cs b/MediaBrowser.Common/Net/MimeTypes.cs index 47536a341..85b9b1f38 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 ced53299d..644222949 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -235,12 +235,11 @@ namespace MediaBrowser.Controller.MediaInfo /// </summary> /// <param name="mediaPath">The media path.</param> /// <param name="subtitleStream">The subtitle stream.</param> - /// <param name="offset">The offset.</param> /// <param name="outputExtension">The output extension.</param> /// <returns>System.String.</returns> - 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 578f3039b..eb3b20ce3 100644 --- a/MediaBrowser.Model/LiveTv/ChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/ChannelQuery.cs @@ -17,5 +17,17 @@ namespace MediaBrowser.Model.LiveTv /// </summary> /// <value>The user identifier.</value> public string UserId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } } } diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index b9cabded7..21e50f400 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<RemoteImageInfo>(); - 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. /// </summary> /// <param name="images">The images.</param> - /// <param name="item">The item.</param> /// <returns>IEnumerable{MovieDbProvider.Poster}.</returns> - private IEnumerable<MovieDbProvider.Poster> GetPosters(MovieDbProvider.Images images, IHasImages item) + private IEnumerable<MovieDbProvider.Poster> GetPosters(MovieDbProvider.Images images) { return images.posters ?? new List<MovieDbProvider.Poster>(); } @@ -130,9 +129,8 @@ namespace MediaBrowser.Providers.Movies /// Gets the backdrops. /// </summary> /// <param name="images">The images.</param> - /// <param name="item">The item.</param> /// <returns>IEnumerable{MovieDbProvider.Backdrop}.</returns> - private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images, IHasImages item) + private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images) { var eligibleBackdrops = images.backdrops == null ? new List<MovieDbProvider.Backdrop>() : images.backdrops @@ -147,10 +145,14 @@ namespace MediaBrowser.Providers.Movies /// </summary> /// <param name="item">The item.</param> /// <param name="jsonSerializer">The json serializer.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{MovieImages}.</returns> - private MovieDbProvider.Images FetchImages(IHasImages item, IJsonSerializer jsonSerializer) + private async Task<MovieDbProvider.Images> 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 453284751..4d3a5baac 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 c16c50412..70ad6611a 100644 --- a/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs @@ -235,7 +235,7 @@ namespace MediaBrowser.Providers.Movies /// <returns>Task.</returns> 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 fed1b444e..8dd64fd14 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); + } + /// <summary> /// Gets the data file path. /// </summary> @@ -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 291d2ff4d..d9a367e2c 100644 --- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.Movies /// <returns>Task.</returns> public async Task Run(IProgress<double> 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 7dc210ccc..b6622a310 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<LiveTvChannel> 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<ChannelInfoDto> { 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<ILiveTvRecording> entities = await GetEntities(recordings, service.Name, cancellationToken).ConfigureAwait(false); + recordings = recordings.OrderByDescending(i => i.StartDate); - entities = entities.OrderByDescending(i => i.RecordingInfo.StartDate); + IEnumerable<ILiveTvRecording> 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 990b695ae..bcc857a80 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)); } - + /// <summary> /// Gets the media info. /// </summary> @@ -378,11 +378,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// <param name="inputPath">The input path.</param> /// <param name="outputPath">The output path.</param> /// <param name="language">The language.</param> - /// <param name="offset">The offset.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - 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 /// <param name="inputPath">The input path.</param> /// <param name="outputPath">The output path.</param> /// <param name="language">The language.</param> - /// <param name="offset">The offset.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">inputPath /// or /// outputPath</exception> /// <exception cref="System.ApplicationException"></exception> - 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; } - + /// <summary> /// Gets the subtitle language encoding param. /// </summary> @@ -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 /// <param name="inputFiles">The input files.</param> /// <param name="type">The type.</param> /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> - /// <param name="offset">The offset.</param> /// <param name="outputPath">The output path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentException">Must use inputPath list overload</exception> - 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 /// </summary> /// <param name="inputPath">The input path.</param> /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> - /// <param name="offset">The offset.</param> /// <param name="outputPath">The output path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> @@ -649,7 +642,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// or /// cancellationToken</exception> /// <exception cref="System.ApplicationException"></exception> - 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 d693f90e2..e5fbc7d9f 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 f2b2d6b7b..cbe815c03 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 3bb7e7105..c47d67815 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="MediaBrowser.ApiClient.Javascript" version="3.0.219" targetFramework="net45" /> + <package id="MediaBrowser.ApiClient.Javascript" version="3.0.223" targetFramework="net45" /> </packages>
\ No newline at end of file |
