mirror of https://github.com/jellyfin/jellyfin.git
rework text subtitles
This commit is contained in:
parent
c23bd68220
commit
ec4000404d
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue