mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
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.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
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")]
|
[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; }
|
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")]
|
[Route("/LiveTv/Channels/{Id}", "GET")]
|
||||||
@ -116,26 +130,26 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
public string SeriesTimerId { get; set; }
|
public string SeriesTimerId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/LiveTv/Programs", "GET")]
|
[Route("/LiveTv/Programs", "GET,POST")]
|
||||||
[Api(Description = "Gets available live tv epgs..")]
|
[Api(Description = "Gets available live tv epgs..")]
|
||||||
public class GetPrograms : IReturn<QueryResult<ProgramInfoDto>>
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
public string MaxEndDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +274,9 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
var result = _liveTvManager.GetChannels(new ChannelQuery
|
var result = _liveTvManager.GetChannels(new ChannelQuery
|
||||||
{
|
{
|
||||||
ChannelType = request.Type,
|
ChannelType = request.Type,
|
||||||
UserId = request.UserId
|
UserId = request.UserId,
|
||||||
|
StartIndex = request.StartIndex,
|
||||||
|
Limit = request.Limit
|
||||||
|
|
||||||
}, CancellationToken.None).Result;
|
}, CancellationToken.None).Result;
|
||||||
|
|
||||||
@ -309,6 +325,11 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object Post(GetPrograms request)
|
||||||
|
{
|
||||||
|
return Get(request);
|
||||||
|
}
|
||||||
|
|
||||||
public object Get(GetRecordings request)
|
public object Get(GetRecordings request)
|
||||||
{
|
{
|
||||||
var result = _liveTvManager.GetRecordings(new RecordingQuery
|
var result = _liveTvManager.GetRecordings(new RecordingQuery
|
||||||
|
@ -253,25 +253,44 @@ namespace MediaBrowser.Api.Playback
|
|||||||
return returnFirstIfNoIndex ? streams.FirstOrDefault() : null;
|
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>
|
/// <summary>
|
||||||
/// Gets the number of threads.
|
/// Gets the number of threads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>System.Int32.</returns>
|
/// <returns>System.Int32.</returns>
|
||||||
/// <exception cref="System.Exception">Unrecognized MediaEncodingQuality value.</exception>
|
/// <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:
|
case EncodingQuality.HighSpeed:
|
||||||
return 2;
|
return 2;
|
||||||
case EncodingQuality.HighQuality:
|
case EncodingQuality.HighQuality:
|
||||||
return 2;
|
return isWebm ? Math.Min(3, Environment.ProcessorCount - 1) : 2;
|
||||||
case EncodingQuality.MaxQuality:
|
case EncodingQuality.MaxQuality:
|
||||||
return 0;
|
return isWebm ? Math.Max(2, Environment.ProcessorCount - 1) : 0;
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unrecognized MediaEncodingQuality value.");
|
throw new Exception("Unrecognized MediaEncodingQuality value.");
|
||||||
}
|
}
|
||||||
@ -285,30 +304,74 @@ namespace MediaBrowser.Api.Playback
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected string GetVideoQualityParam(StreamState state, string videoCodec)
|
protected string GetVideoQualityParam(StreamState state, string videoCodec)
|
||||||
{
|
{
|
||||||
var args = string.Empty;
|
|
||||||
|
|
||||||
// webm
|
// webm
|
||||||
if (videoCodec.Equals("libvpx", StringComparison.OrdinalIgnoreCase))
|
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
|
// 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";
|
return "-preset superfast";
|
||||||
}
|
|
||||||
else if (videoCodec.Equals("mpeg4", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
args = "-mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
/// <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/
|
// http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
|
||||||
|
|
||||||
var assSubtitleParam = string.Empty;
|
var assSubtitleParam = string.Empty;
|
||||||
|
var copyTsParam = string.Empty;
|
||||||
|
|
||||||
var request = state.VideoRequest;
|
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, "ass", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(state.SubtitleStream.Codec, "ssa", 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 widthParam = request.Width.Value.ToString(UsCulture);
|
||||||
var heightParam = request.Height.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);
|
var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
|
||||||
@ -354,8 +419,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var widthParam = request.Width.Value.ToString(UsCulture);
|
var widthParam = request.Width.Value.ToString(UsCulture);
|
||||||
|
|
||||||
return isH264Output ?
|
return isH264Output ?
|
||||||
string.Format(" -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", widthParam, assSubtitleParam) :
|
string.Format("{2} -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", widthParam, assSubtitleParam, copyTsParam) :
|
||||||
string.Format(" -vf \"scale={0}:-1{1}\"", widthParam, assSubtitleParam);
|
string.Format("{2} -vf \"scale={0}:-1{1}\"", widthParam, assSubtitleParam, copyTsParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a fixed height was requested
|
// If a fixed height was requested
|
||||||
@ -364,8 +429,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var heightParam = request.Height.Value.ToString(UsCulture);
|
var heightParam = request.Height.Value.ToString(UsCulture);
|
||||||
|
|
||||||
return isH264Output ?
|
return isH264Output ?
|
||||||
string.Format(" -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", heightParam, assSubtitleParam) :
|
string.Format("{2} -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", heightParam, assSubtitleParam, copyTsParam) :
|
||||||
string.Format(" -vf \"scale=-1:{0}{1}\"", heightParam, assSubtitleParam);
|
string.Format("{2} -vf \"scale=-1:{0}{1}\"", heightParam, assSubtitleParam, copyTsParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a max width was requested
|
// If a max width was requested
|
||||||
@ -374,8 +439,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
|
||||||
|
|
||||||
return isH264Output ?
|
return isH264Output ?
|
||||||
string.Format(" -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", maxWidthParam, assSubtitleParam) :
|
string.Format("{2} -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", maxWidthParam, assSubtitleParam, copyTsParam) :
|
||||||
string.Format(" -vf \"scale=min(iw\\,{0}):-1{1}\"", maxWidthParam, assSubtitleParam);
|
string.Format("{2} -vf \"scale=min(iw\\,{0}):-1{1}\"", maxWidthParam, assSubtitleParam, copyTsParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a max height was requested
|
// If a max height was requested
|
||||||
@ -384,8 +449,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
||||||
|
|
||||||
return isH264Output ?
|
return isH264Output ?
|
||||||
string.Format(" -vf \"scale=trunc(oh*a*2)/2: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(" -vf \"scale=-1:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam);
|
string.Format("{2} -vf \"scale=-1:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam, copyTsParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.VideoStream == null)
|
if (state.VideoStream == null)
|
||||||
@ -408,45 +473,45 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var widthParam = outputSize.Width.ToString(UsCulture);
|
var widthParam = outputSize.Width.ToString(UsCulture);
|
||||||
var heightParam = outputSize.Height.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
|
// 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>
|
/// <summary>
|
||||||
/// Gets the text subtitle param.
|
/// Gets the text subtitle param.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <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>
|
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <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) :
|
var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, performConversion) :
|
||||||
GetExtractedAssPath(state, startTimeTicks, performConversion);
|
GetExtractedAssPath(state, performConversion);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
return string.Empty;
|
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>
|
/// <summary>
|
||||||
/// Gets the extracted ass path.
|
/// Gets the extracted ass path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <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>
|
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <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, ".ass");
|
||||||
|
|
||||||
var path = FFMpegManager.Instance.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream, offset, ".ass");
|
|
||||||
|
|
||||||
if (performConversion)
|
if (performConversion)
|
||||||
{
|
{
|
||||||
@ -460,7 +525,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
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);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
@ -478,14 +543,11 @@ namespace MediaBrowser.Api.Playback
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mediaPath">The media path.</param>
|
/// <param name="mediaPath">The media path.</param>
|
||||||
/// <param name="subtitleStream">The subtitle stream.</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>
|
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <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, ".ass");
|
||||||
|
|
||||||
var path = FFMpegManager.Instance.GetSubtitleCachePath(mediaPath, subtitleStream, offset, ".ass");
|
|
||||||
|
|
||||||
if (performConversion)
|
if (performConversion)
|
||||||
{
|
{
|
||||||
@ -495,7 +557,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
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);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
if (isPlaylistNewlyCreated)
|
if (isPlaylistNewlyCreated)
|
||||||
{
|
{
|
||||||
var minimumSegmentCount = 3;
|
var minimumSegmentCount = 3;
|
||||||
var quality = ServerConfigurationManager.Configuration.MediaEncodingQuality;
|
var quality = GetQualitySetting();
|
||||||
|
|
||||||
if (quality == EncodingQuality.HighSpeed || quality == EncodingQuality.HighQuality)
|
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 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,
|
itsOffset,
|
||||||
probeSize,
|
probeSize,
|
||||||
GetUserAgentParam(state.MediaPath),
|
GetUserAgentParam(state.MediaPath),
|
||||||
|
@ -80,21 +80,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
args += " -ab " + bitrate.Value.ToString(UsCulture);
|
args += " -ab " + bitrate.Value.ToString(UsCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
var volParam = string.Empty;
|
args += " " + GetAudioFilterParam(state, true);
|
||||||
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);
|
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
|
|
||||||
const string vn = " -vn";
|
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}\"",
|
return string.Format("{0} -i {1}{2} -threads {3}{4} {5} -id3v2_version 3 -write_id3v1 1 \"{6}\"",
|
||||||
GetFastSeekCommandLineParameter(request),
|
GetFastSeekCommandLineParameter(request),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using System;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
using MediaBrowser.Common.MediaInfo;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -106,6 +107,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
|
|
||||||
var transferMode = GetHeader("transferMode.dlna.org");
|
var transferMode = GetHeader("transferMode.dlna.org");
|
||||||
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
|
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
|
||||||
|
responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
|
||||||
|
|
||||||
var contentFeatures = string.Empty;
|
var contentFeatures = string.Empty;
|
||||||
var extension = GetOutputFileExtension(state);
|
var extension = GetOutputFileExtension(state);
|
||||||
@ -118,22 +120,22 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
|
|
||||||
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
|
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
|
||||||
|
|
||||||
//if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
{
|
||||||
// contentFeatures = "DLNA.ORG_PN=MP3";
|
contentFeatures = "DLNA.ORG_PN=MP3";
|
||||||
//}
|
}
|
||||||
//else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
{
|
||||||
// contentFeatures = "DLNA.ORG_PN=AAC_ISO";
|
contentFeatures = "DLNA.ORG_PN=AAC_ISO";
|
||||||
//}
|
}
|
||||||
//else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
{
|
||||||
// contentFeatures = "DLNA.ORG_PN=WMABASE";
|
contentFeatures = "DLNA.ORG_PN=WMABASE";
|
||||||
//}
|
}
|
||||||
//else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
{
|
||||||
// contentFeatures = "DLNA.ORG_PN=AVI";
|
contentFeatures = "DLNA.ORG_PN=AVI";
|
||||||
//}
|
}
|
||||||
//else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase))
|
//else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
//{
|
||||||
// contentFeatures = "DLNA.ORG_PN=MPEG4_P2_SP_AAC";
|
// 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";
|
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,
|
probeSize,
|
||||||
GetUserAgentParam(state.MediaPath),
|
GetUserAgentParam(state.MediaPath),
|
||||||
GetFastSeekCommandLineParameter(state.Request),
|
GetFastSeekCommandLineParameter(state.Request),
|
||||||
@ -170,6 +170,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue)
|
||||||
{
|
{
|
||||||
qualityParam += string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
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))
|
if (!string.IsNullOrEmpty(qualityParam))
|
||||||
@ -238,21 +239,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
args += " -ab " + bitrate.Value.ToString(UsCulture);
|
args += " -ab " + bitrate.Value.ToString(UsCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
var volParam = string.Empty;
|
args += " " + GetAudioFilterParam(state, true);
|
||||||
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);
|
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,10 @@ namespace MediaBrowser.Common.MediaInfo
|
|||||||
/// <param name="inputFiles">The input files.</param>
|
/// <param name="inputFiles">The input files.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</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="outputPath">The output path.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Converts the text subtitle to ass.
|
/// Converts the text subtitle to ass.
|
||||||
@ -52,10 +51,9 @@ namespace MediaBrowser.Common.MediaInfo
|
|||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="language">The language.</param>
|
/// <param name="language">The language.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Gets the media info.
|
/// Gets the media info.
|
||||||
|
@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
}
|
}
|
||||||
if (ext.Equals(".avi", StringComparison.OrdinalIgnoreCase))
|
if (ext.Equals(".avi", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "video/avi";
|
return "video/x-msvideo";
|
||||||
}
|
}
|
||||||
if (ext.Equals(".m4v", StringComparison.OrdinalIgnoreCase))
|
if (ext.Equals(".m4v", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -235,12 +235,11 @@ namespace MediaBrowser.Controller.MediaInfo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mediaPath">The media path.</param>
|
/// <param name="mediaPath">The media path.</param>
|
||||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
/// <param name="subtitleStream">The subtitle stream.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
|
||||||
/// <param name="outputExtension">The output extension.</param>
|
/// <param name="outputExtension">The output extension.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <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)
|
if (subtitleStream.IsExternal)
|
||||||
{
|
{
|
||||||
|
@ -17,5 +17,17 @@ namespace MediaBrowser.Model.LiveTv
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The user identifier.</value>
|
/// <value>The user identifier.</value>
|
||||||
public string UserId { get; set; }
|
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 list = new List<RemoteImageInfo>();
|
||||||
|
|
||||||
var results = FetchImages(item, _jsonSerializer);
|
var results = await FetchImages((BaseItem)item, _jsonSerializer, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (results == null)
|
if (results == null)
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
|
|
||||||
var tmdbImageUrl = tmdbSettings.images.base_url + "original";
|
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,
|
Url = tmdbImageUrl + i.file_path,
|
||||||
CommunityRating = i.vote_average,
|
CommunityRating = i.vote_average,
|
||||||
@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
RatingType = RatingType.Score
|
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,
|
Url = tmdbImageUrl + i.file_path,
|
||||||
CommunityRating = i.vote_average,
|
CommunityRating = i.vote_average,
|
||||||
@ -119,9 +119,8 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
/// Gets the posters.
|
/// Gets the posters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="images">The images.</param>
|
/// <param name="images">The images.</param>
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <returns>IEnumerable{MovieDbProvider.Poster}.</returns>
|
/// <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>();
|
return images.posters ?? new List<MovieDbProvider.Poster>();
|
||||||
}
|
}
|
||||||
@ -130,9 +129,8 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
/// Gets the backdrops.
|
/// Gets the backdrops.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="images">The images.</param>
|
/// <param name="images">The images.</param>
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <returns>IEnumerable{MovieDbProvider.Backdrop}.</returns>
|
/// <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>() :
|
var eligibleBackdrops = images.backdrops == null ? new List<MovieDbProvider.Backdrop>() :
|
||||||
images.backdrops
|
images.backdrops
|
||||||
@ -147,10 +145,14 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="jsonSerializer">The json serializer.</param>
|
/// <param name="jsonSerializer">The json serializer.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{MovieImages}.</returns>
|
/// <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))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(id))
|
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);
|
var dataFilePath = MovieDbPersonProvider.GetPersonDataFilePath(_config.ApplicationPaths, id);
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
private async Task FetchInfo(Person person, string id, bool isForcedRefresh, CancellationToken cancellationToken)
|
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))
|
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);
|
var personDataPath = GetPersonDataPath(ConfigurationManager.ApplicationPaths, id);
|
||||||
|
|
||||||
|
@ -558,6 +558,31 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
JsonSerializer.SerializeToFile(mainResult, dataFilePath);
|
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>
|
/// <summary>
|
||||||
/// Gets the data file path.
|
/// Gets the data file path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -575,7 +600,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
return GetDataFilePath(item is BoxSet, id, item.GetPreferredMetadataLanguage());
|
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);
|
var path = GetMovieDataPath(ConfigurationManager.ApplicationPaths, isBoxset, tmdbId);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_config.Configuration.EnableInternetProviders)
|
if (!_config.Configuration.EnableInternetProviders && !_config.Configuration.EnableTmdbUpdates)
|
||||||
{
|
{
|
||||||
progress.Report(100);
|
progress.Report(100);
|
||||||
return;
|
return;
|
||||||
|
@ -98,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnChannels = channels.OrderBy(i =>
|
channels = channels.OrderBy(i =>
|
||||||
{
|
{
|
||||||
double number = 0;
|
double number = 0;
|
||||||
|
|
||||||
@ -109,14 +109,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
return number;
|
return number;
|
||||||
|
|
||||||
}).ThenBy(i => i.Name)
|
}).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))
|
.Select(i => _tvDtoService.GetChannelInfoDto(i, GetCurrentProgram(i.ChannelInfo.Id), user))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var result = new QueryResult<ChannelInfoDto>
|
var result = new QueryResult<ChannelInfoDto>
|
||||||
{
|
{
|
||||||
Items = returnChannels,
|
Items = returnChannels,
|
||||||
TotalRecordCount = returnChannels.Length
|
TotalRecordCount = allChannels.Count
|
||||||
};
|
};
|
||||||
|
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
@ -575,9 +590,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
.Where(i => _tvDtoService.GetInternalSeriesTimerId(currentServiceName, i.SeriesTimerId) == guid);
|
.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)
|
if (user != null)
|
||||||
{
|
{
|
||||||
|
@ -378,11 +378,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="language">The language.</param>
|
/// <param name="language">The language.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, TimeSpan offset,
|
public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken)
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
var semaphore = GetLock(outputPath);
|
var semaphore = GetLock(outputPath);
|
||||||
|
|
||||||
@ -392,7 +390,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
{
|
{
|
||||||
if (!File.Exists(outputPath))
|
if (!File.Exists(outputPath))
|
||||||
{
|
{
|
||||||
await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language, offset).ConfigureAwait(false);
|
await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -409,13 +407,12 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="language">The language.</param>
|
/// <param name="language">The language.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException">inputPath
|
/// <exception cref="System.ArgumentNullException">inputPath
|
||||||
/// or
|
/// or
|
||||||
/// outputPath</exception>
|
/// outputPath</exception>
|
||||||
/// <exception cref="System.ApplicationException"></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))
|
if (string.IsNullOrEmpty(inputPath))
|
||||||
{
|
{
|
||||||
@ -428,8 +425,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var slowSeekParam = offset.TotalSeconds > 0 ? " -ss " + offset.TotalSeconds.ToString(UsCulture) : string.Empty;
|
|
||||||
|
|
||||||
var encodingParam = string.IsNullOrEmpty(language) ? string.Empty :
|
var encodingParam = string.IsNullOrEmpty(language) ? string.Empty :
|
||||||
GetSubtitleLanguageEncodingParam(language) + " ";
|
GetSubtitleLanguageEncodingParam(language) + " ";
|
||||||
|
|
||||||
@ -444,7 +439,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
FileName = FFMpegPath,
|
FileName = FFMpegPath,
|
||||||
Arguments =
|
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,
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
ErrorDialog = false
|
ErrorDialog = false
|
||||||
@ -610,12 +605,11 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// <param name="inputFiles">The input files.</param>
|
/// <param name="inputFiles">The input files.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</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="outputPath">The output path.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
|
/// <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);
|
var semaphore = GetLock(outputPath);
|
||||||
|
|
||||||
@ -625,7 +619,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
{
|
{
|
||||||
if (!File.Exists(outputPath))
|
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
|
finally
|
||||||
@ -639,7 +633,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</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="outputPath">The output path.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
@ -649,7 +642,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// or
|
/// or
|
||||||
/// cancellationToken</exception>
|
/// cancellationToken</exception>
|
||||||
/// <exception cref="System.ApplicationException"></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))
|
if (string.IsNullOrEmpty(inputPath))
|
||||||
{
|
{
|
||||||
@ -662,9 +655,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var slowSeekParam = GetSlowSeekCommandLineParameter(offset);
|
|
||||||
var fastSeekParam = GetFastSeekCommandLineParameter(offset);
|
|
||||||
|
|
||||||
var process = new Process
|
var process = new Process
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo
|
StartInfo = new ProcessStartInfo
|
||||||
@ -676,7 +666,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
RedirectStandardError = true,
|
RedirectStandardError = true,
|
||||||
|
|
||||||
FileName = FFMpegPath,
|
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,
|
WindowStyle = ProcessWindowStyle.Hidden,
|
||||||
ErrorDialog = false
|
ErrorDialog = false
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,12 @@ namespace MediaBrowser.Server.Implementations.Session
|
|||||||
|
|
||||||
var vals = message.Data.Split('|');
|
var vals = message.Data.Split('|');
|
||||||
|
|
||||||
|
if (vals.Length < 3)
|
||||||
|
{
|
||||||
|
_logger.Error("Client sent invalid identity message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var client = vals[0];
|
var client = vals[0];
|
||||||
var deviceId = vals[1];
|
var deviceId = vals[1];
|
||||||
var version = vals[2];
|
var version = vals[2];
|
||||||
|
@ -436,13 +436,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
|||||||
|
|
||||||
self.getLiveTvPrograms = function (options) {
|
self.getLiveTvPrograms = function (options) {
|
||||||
|
|
||||||
var url = self.getUrl("LiveTv/Programs", options || {});
|
options = options || {};
|
||||||
|
|
||||||
|
if (options.channelIds) {
|
||||||
|
|
||||||
|
return self.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: self.getUrl("LiveTv/Programs"),
|
||||||
|
data: JSON.stringify(options),
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: "json"
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
return self.ajax({
|
return self.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: url,
|
url: self.getUrl("LiveTv/Programs", options),
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.getLiveTvRecordings = function (options) {
|
self.getLiveTvRecordings = function (options) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.219" targetFramework="net45" />
|
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.223" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
Loading…
x
Reference in New Issue
Block a user