switch to subtitle filter
This commit is contained in:
parent
5d08aa39a9
commit
437062b29e
|
@ -1,5 +1,4 @@
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Library;
|
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
|
@ -101,9 +101,9 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
|
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions);
|
protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the transcoding job.
|
/// Gets the type of the transcoding job.
|
||||||
|
@ -478,12 +478,10 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="outputVideoCodec">The output video codec.</param>
|
/// <param name="outputVideoCodec">The output video codec.</param>
|
||||||
/// <param name="performTextSubtitleConversion">if set to <c>true</c> [perform text subtitle conversion].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected string GetOutputSizeParam(StreamState state,
|
protected string GetOutputSizeParam(StreamState state,
|
||||||
string outputVideoCodec,
|
string outputVideoCodec,
|
||||||
bool performTextSubtitleConversion,
|
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 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/
|
||||||
|
@ -496,7 +494,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
if (state.SubtitleStream != null && !state.SubtitleStream.IsGraphicalSubtitleStream)
|
if (state.SubtitleStream != null && !state.SubtitleStream.IsGraphicalSubtitleStream)
|
||||||
{
|
{
|
||||||
assSubtitleParam = GetTextSubtitleParam(state, performTextSubtitleConversion, cancellationToken);
|
assSubtitleParam = GetTextSubtitleParam(state, cancellationToken);
|
||||||
copyTsParam = " -copyts";
|
copyTsParam = " -copyts";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,103 +572,41 @@ namespace MediaBrowser.Api.Playback
|
||||||
/// 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="performConversion">if set to <c>true</c> [perform conversion].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected string GetTextSubtitleParam(StreamState state,
|
protected string GetTextSubtitleParam(StreamState state,
|
||||||
bool performConversion,
|
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var path = state.SubtitleStream.IsExternal ?
|
|
||||||
GetConvertedAssPath(state.SubtitleStream, performConversion, cancellationToken) :
|
|
||||||
GetExtractedAssPath(state, performConversion, cancellationToken);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds;
|
var seconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds;
|
||||||
|
|
||||||
return string.Format(",ass='{0}',setpts=PTS -{1}/TB",
|
if (state.SubtitleStream.IsExternal)
|
||||||
path.Replace('\\', '/').Replace(":/", "\\:/"),
|
{
|
||||||
|
var subtitlePath = state.SubtitleStream.Path;
|
||||||
|
|
||||||
|
var charsetParam = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
|
||||||
|
{
|
||||||
|
var charenc = MediaEncoder.GetSubtitleLanguageEncodingParam(subtitlePath, state.SubtitleStream.Language);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(charenc))
|
||||||
|
{
|
||||||
|
charsetParam = ":charenc=" + charenc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Perhaps also use original_size=1920x800
|
||||||
|
|
||||||
|
return string.Format(",subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
|
||||||
|
subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
|
||||||
|
charsetParam,
|
||||||
Math.Round(seconds).ToString(UsCulture));
|
Math.Round(seconds).ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
return string.Format(",subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
|
||||||
/// Gets the extracted ass path.
|
state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
|
||||||
/// </summary>
|
state.SubtitleStream.Index.ToString(UsCulture),
|
||||||
/// <param name="state">The state.</param>
|
Math.Round(seconds).ToString(UsCulture));
|
||||||
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
private string GetExtractedAssPath(StreamState state,
|
|
||||||
bool performConversion,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var path = EncodingManager.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream.Index, ".ass");
|
|
||||||
|
|
||||||
if (performConversion)
|
|
||||||
{
|
|
||||||
InputType type;
|
|
||||||
|
|
||||||
var inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.IsRemote, state.VideoType, state.IsoType, null, state.PlayableStreamFileNames, out type);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var parentPath = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
|
||||||
|
|
||||||
// Don't re-encode ass/ssa to ass because ffmpeg ass encoder fails if there's more than one ass rectangle. Affect Anime mostly.
|
|
||||||
// See https://lists.ffmpeg.org/pipermail/ffmpeg-cvslog/2013-April/063616.html
|
|
||||||
var isAssSubtitle = string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, isAssSubtitle, path, cancellationToken);
|
|
||||||
|
|
||||||
Task.WaitAll(task);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the converted ass path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
|
||||||
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
private string GetConvertedAssPath(MediaStream subtitleStream,
|
|
||||||
bool performConversion,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var path = EncodingManager.GetSubtitleCachePath(subtitleStream.Path, ".ass");
|
|
||||||
|
|
||||||
if (performConversion)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var parentPath = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
|
||||||
|
|
||||||
var task = MediaEncoder.ConvertTextSubtitleToAss(subtitleStream.Path, path, subtitleStream.Language, cancellationToken);
|
|
||||||
|
|
||||||
Task.WaitAll(task);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -688,7 +624,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
// Add resolution params, if specified
|
// Add resolution params, if specified
|
||||||
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
|
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
|
||||||
{
|
{
|
||||||
outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, false, CancellationToken.None).TrimEnd('"');
|
outputSizeParam = GetOutputSizeParam(state, outputVideoCodec, CancellationToken.None).TrimEnd('"');
|
||||||
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
|
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
/// Gets the video arguments.
|
/// Gets the video arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
|
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected abstract string GetVideoArguments(StreamState state, bool performSubtitleConversion);
|
protected abstract string GetVideoArguments(StreamState state);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the segment file extension.
|
/// Gets the segment file extension.
|
||||||
|
@ -272,9 +271,9 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
|
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
|
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||||
{
|
{
|
||||||
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
|
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
|
||||||
|
|
||||||
|
@ -289,7 +288,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
var inputModifier = GetInputModifier(state);
|
var inputModifier = GetInputModifier(state);
|
||||||
|
|
||||||
// If performSubtitleConversions is true we're actually starting ffmpeg
|
// If performSubtitleConversions is true we're actually starting ffmpeg
|
||||||
var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0";
|
var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
|
||||||
|
|
||||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
|
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
|
||||||
itsOffset,
|
itsOffset,
|
||||||
|
@ -297,7 +296,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
GetInputArgument(state),
|
GetInputArgument(state),
|
||||||
threads,
|
threads,
|
||||||
GetMapArgs(state),
|
GetMapArgs(state),
|
||||||
GetVideoArguments(state, performSubtitleConversions),
|
GetVideoArguments(state),
|
||||||
GetAudioArguments(state),
|
GetAudioArguments(state),
|
||||||
state.SegmentLength.ToString(UsCulture),
|
state.SegmentLength.ToString(UsCulture),
|
||||||
startNumberParam,
|
startNumberParam,
|
||||||
|
|
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion)
|
protected override string GetVideoArguments(StreamState state)
|
||||||
{
|
{
|
||||||
var codec = state.OutputVideoCodec;
|
var codec = state.OutputVideoCodec;
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
|
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
|
||||||
{
|
{
|
||||||
args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
|
args += GetOutputSizeParam(state, codec, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,10 +155,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
/// Gets the video arguments.
|
/// Gets the video arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
|
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected override string GetVideoArguments(StreamState state,
|
protected override string GetVideoArguments(StreamState state)
|
||||||
bool performSubtitleConversion)
|
|
||||||
{
|
{
|
||||||
var codec = state.OutputVideoCodec;
|
var codec = state.OutputVideoCodec;
|
||||||
|
|
||||||
|
@ -181,7 +179,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
|
if (state.VideoRequest.Width.HasValue || state.VideoRequest.Height.HasValue || state.VideoRequest.MaxHeight.HasValue || state.VideoRequest.MaxWidth.HasValue)
|
||||||
{
|
{
|
||||||
args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
|
args += GetOutputSizeParam(state, codec, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
|
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
/// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
||||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
|
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||||
{
|
{
|
||||||
var audioTranscodeParams = new List<string>();
|
var audioTranscodeParams = new List<string>();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Threading;
|
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Channels;
|
using MediaBrowser.Controller.Channels;
|
||||||
|
@ -14,6 +13,7 @@ using MediaBrowser.Model.IO;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
|
@ -90,9 +90,9 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="performSubtitleConversions">if set to <c>true</c> [perform subtitle conversions].</param>
|
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
|
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||||
{
|
{
|
||||||
// Get the output codec name
|
// Get the output codec name
|
||||||
var videoCodec = state.OutputVideoCodec;
|
var videoCodec = state.OutputVideoCodec;
|
||||||
|
@ -114,7 +114,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
GetInputArgument(state),
|
GetInputArgument(state),
|
||||||
keyFrame,
|
keyFrame,
|
||||||
GetMapArgs(state),
|
GetMapArgs(state),
|
||||||
GetVideoArguments(state, videoCodec, performSubtitleConversions),
|
GetVideoArguments(state, videoCodec),
|
||||||
threads,
|
threads,
|
||||||
GetAudioArguments(state),
|
GetAudioArguments(state),
|
||||||
format,
|
format,
|
||||||
|
@ -127,9 +127,8 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="codec">The video codec.</param>
|
/// <param name="codec">The video codec.</param>
|
||||||
/// <param name="performSubtitleConversion">if set to <c>true</c> [perform subtitle conversion].</param>
|
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetVideoArguments(StreamState state, string codec, bool performSubtitleConversion)
|
private string GetVideoArguments(StreamState state, string codec)
|
||||||
{
|
{
|
||||||
var args = "-vcodec " + codec;
|
var args = "-vcodec " + codec;
|
||||||
|
|
||||||
|
@ -157,7 +156,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
|
if (request.Width.HasValue || request.Height.HasValue || request.MaxHeight.HasValue || request.MaxWidth.HasValue)
|
||||||
{
|
{
|
||||||
args += GetOutputSizeParam(state, codec, performSubtitleConversion, CancellationToken.None);
|
args += GetOutputSizeParam(state, codec, CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,9 @@
|
||||||
|
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ChannelLatestMediaSearch
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -14,4 +14,15 @@ namespace MediaBrowser.Controller.Channels
|
||||||
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
||||||
Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
|
Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ISupportsLatestMedia
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the latest media.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
||||||
|
Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ using MediaBrowser.Model.Chapters;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Chapters
|
namespace MediaBrowser.Controller.Chapters
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,22 @@ namespace MediaBrowser.Controller.Chapters
|
||||||
/// <param name="chapterProviders">The chapter providers.</param>
|
/// <param name="chapterProviders">The chapter providers.</param>
|
||||||
void AddParts(IEnumerable<IChapterProvider> chapterProviders);
|
void AddParts(IEnumerable<IChapterProvider> chapterProviders);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the chapters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item identifier.</param>
|
||||||
|
/// <returns>List{ChapterInfo}.</returns>
|
||||||
|
IEnumerable<ChapterInfo> GetChapters(string itemId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the chapters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item identifier.</param>
|
||||||
|
/// <param name="chapters">The chapters.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
Task SaveChapters(string itemId, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches the specified video.
|
/// Searches the specified video.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -274,5 +274,11 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>BaseItemDto.</returns>
|
/// <returns>BaseItemDto.</returns>
|
||||||
Task<BaseItemDto> GetLiveTvFolder(string userId, CancellationToken cancellationToken);
|
Task<BaseItemDto> GetLiveTvFolder(string userId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the enabled users.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>IEnumerable{User}.</returns>
|
||||||
|
IEnumerable<User> GetEnabledUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,7 @@
|
||||||
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
|
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
|
||||||
<Compile Include="MediaEncoding\IMediaEncoder.cs" />
|
<Compile Include="MediaEncoding\IMediaEncoder.cs" />
|
||||||
<Compile Include="MediaEncoding\InternalMediaInfoResult.cs" />
|
<Compile Include="MediaEncoding\InternalMediaInfoResult.cs" />
|
||||||
|
<Compile Include="MediaEncoding\ISubtitleEncoder.cs" />
|
||||||
<Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
|
<Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
|
||||||
<Compile Include="Net\IHasResultFactory.cs" />
|
<Compile Include="Net\IHasResultFactory.cs" />
|
||||||
<Compile Include="Net\IHttpResultFactory.cs" />
|
<Compile Include="Net\IHttpResultFactory.cs" />
|
||||||
|
|
|
@ -5,23 +5,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
public interface IEncodingManager
|
public interface IEncodingManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets the subtitle cache path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="originalSubtitlePath">The original subtitle path.</param>
|
|
||||||
/// <param name="outputSubtitleExtension">The output subtitle extension.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetSubtitleCachePath(string originalSubtitlePath, string outputSubtitleExtension);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the subtitle cache path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mediaPath">The media path.</param>
|
|
||||||
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
|
|
||||||
/// <param name="outputSubtitleExtension">The output subtitle extension.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the chapter images.
|
/// Refreshes the chapter images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -55,14 +55,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken);
|
Task ExtractTextSubtitle(string[] inputFiles, InputType type, int subtitleStreamIndex, bool copySubtitleStream, string outputPath, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the text subtitle to ass.
|
/// Gets the subtitle language encoding parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
|
||||||
/// <param name="language">The language.</param>
|
/// <param name="language">The language.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <returns>System.String.</returns>
|
||||||
/// <returns>Task.</returns>
|
string GetSubtitleLanguageEncodingParam(string path, string language);
|
||||||
Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the media info.
|
/// Gets the media info.
|
||||||
|
@ -88,14 +86,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetInputArgument(string[] inputFiles, InputType type);
|
string GetInputArgument(string[] inputFiles, InputType type);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Encodes the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The options.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task{Stream}.</returns>
|
|
||||||
Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
15
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
Normal file
15
MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
{
|
||||||
|
public interface ISubtitleEncoder
|
||||||
|
{
|
||||||
|
Task<Stream> ConvertTextSubtitle(String stream,
|
||||||
|
string inputFormat,
|
||||||
|
string outputFormat,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
|
@ -261,168 +261,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
((Process)sender).Dispose();
|
((Process)sender).Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the text subtitle to ass.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="inputPath">The input path.</param>
|
|
||||||
/// <param name="outputPath">The output path.</param>
|
|
||||||
/// <param name="language">The language.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public async Task ConvertTextSubtitleToAss(string inputPath, string outputPath, string language,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var semaphore = GetLock(outputPath);
|
|
||||||
|
|
||||||
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!File.Exists(outputPath))
|
|
||||||
{
|
|
||||||
await ConvertTextSubtitleToAssInternal(inputPath, outputPath, language).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int FastSeekOffsetSeconds = 1;
|
private const int FastSeekOffsetSeconds = 1;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the text subtitle to ass.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="inputPath">The input path.</param>
|
|
||||||
/// <param name="outputPath">The output path.</param>
|
|
||||||
/// <param name="language">The language.</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)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(inputPath))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("inputPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(outputPath))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("outputPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var encodingParam = string.IsNullOrEmpty(language)
|
|
||||||
? string.Empty
|
|
||||||
: GetSubtitleLanguageEncodingParam(language) + " ";
|
|
||||||
|
|
||||||
var process = new Process
|
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
RedirectStandardOutput = false,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
FileName = FFMpegPath,
|
|
||||||
Arguments =
|
|
||||||
string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
|
|
||||||
|
|
||||||
WindowStyle = ProcessWindowStyle.Hidden,
|
|
||||||
ErrorDialog = false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
||||||
|
|
||||||
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt");
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
|
||||||
|
|
||||||
var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
|
|
||||||
true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
process.Start();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logFileStream.Dispose();
|
|
||||||
|
|
||||||
_logger.ErrorException("Error starting ffmpeg", ex);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
var logTask = process.StandardError.BaseStream.CopyToAsync(logFileStream);
|
|
||||||
|
|
||||||
var ranToCompletion = process.WaitForExit(60000);
|
|
||||||
|
|
||||||
if (!ranToCompletion)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.Info("Killing ffmpeg subtitle conversion process");
|
|
||||||
|
|
||||||
process.Kill();
|
|
||||||
|
|
||||||
process.WaitForExit(1000);
|
|
||||||
|
|
||||||
await logTask.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error killing subtitle conversion process", ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
logFileStream.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var exitCode = ranToCompletion ? process.ExitCode : -1;
|
|
||||||
|
|
||||||
process.Dispose();
|
|
||||||
|
|
||||||
var failed = false;
|
|
||||||
|
|
||||||
if (exitCode == -1)
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
|
|
||||||
if (File.Exists(outputPath))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.Info("Deleting converted subtitle due to failure: ", outputPath);
|
|
||||||
File.Delete(outputPath);
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error deleting converted subtitle {0}", ex, outputPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!File.Exists(outputPath))
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failed)
|
|
||||||
{
|
|
||||||
var msg = string.Format("ffmpeg subtitle converted failed for {0}", inputPath);
|
|
||||||
|
|
||||||
_logger.Error(msg);
|
|
||||||
|
|
||||||
throw new ApplicationException(msg);
|
|
||||||
}
|
|
||||||
await SetAssFont(outputPath).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string GetFastSeekCommandLineParameter(TimeSpan offset)
|
protected string GetFastSeekCommandLineParameter(TimeSpan offset)
|
||||||
{
|
{
|
||||||
var seconds = offset.TotalSeconds - FastSeekOffsetSeconds;
|
var seconds = offset.TotalSeconds - FastSeekOffsetSeconds;
|
||||||
|
@ -448,10 +288,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the subtitle language encoding param.
|
/// Gets the subtitle language encoding param.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="language">The language.</param>
|
/// <param name="language">The language.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
private string GetSubtitleLanguageEncodingParam(string language)
|
public string GetSubtitleLanguageEncodingParam(string path, string language)
|
||||||
{
|
{
|
||||||
|
if (GetFileEncoding(path).Equals(Encoding.UTF8))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
switch (language.ToLower())
|
switch (language.ToLower())
|
||||||
{
|
{
|
||||||
case "pol":
|
case "pol":
|
||||||
|
@ -468,29 +314,52 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
case "rup":
|
case "rup":
|
||||||
case "alb":
|
case "alb":
|
||||||
case "sqi":
|
case "sqi":
|
||||||
return "-sub_charenc windows-1250";
|
return "windows-1250";
|
||||||
case "ara":
|
case "ara":
|
||||||
return "-sub_charenc windows-1256";
|
return "windows-1256";
|
||||||
case "heb":
|
case "heb":
|
||||||
return "-sub_charenc windows-1255";
|
return "windows-1255";
|
||||||
case "grc":
|
case "grc":
|
||||||
case "gre":
|
case "gre":
|
||||||
return "-sub_charenc windows-1253";
|
return "windows-1253";
|
||||||
case "crh":
|
case "crh":
|
||||||
case "ota":
|
case "ota":
|
||||||
case "tur":
|
case "tur":
|
||||||
return "-sub_charenc windows-1254";
|
return "windows-1254";
|
||||||
case "rus":
|
case "rus":
|
||||||
return "-sub_charenc windows-1251";
|
return "windows-1251";
|
||||||
case "vie":
|
case "vie":
|
||||||
return "-sub_charenc windows-1258";
|
return "windows-1258";
|
||||||
case "kor":
|
case "kor":
|
||||||
return "-sub_charenc cp949";
|
return "cp949";
|
||||||
default:
|
default:
|
||||||
return "-sub_charenc windows-1252";
|
return "windows-1252";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Encoding GetFileEncoding(string srcFile)
|
||||||
|
{
|
||||||
|
// *** Detect byte order mark if any - otherwise assume default
|
||||||
|
var buffer = new byte[5];
|
||||||
|
|
||||||
|
using (var file = new FileStream(srcFile, FileMode.Open))
|
||||||
|
{
|
||||||
|
file.Read(buffer, 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
|
||||||
|
return Encoding.UTF8;
|
||||||
|
if (buffer[0] == 0xfe && buffer[1] == 0xff)
|
||||||
|
return Encoding.Unicode;
|
||||||
|
if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
|
||||||
|
return Encoding.UTF32;
|
||||||
|
if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
|
||||||
|
return Encoding.UTF7;
|
||||||
|
|
||||||
|
// It's ok - anything aside from utf is ok since that's what we're looking for
|
||||||
|
return Encoding.Default;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the text subtitle.
|
/// Extracts the text subtitle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -50,6 +50,12 @@ namespace MediaBrowser.Model.Channels
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsSortOrderToggle { get; set; }
|
public bool SupportsSortOrderToggle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether [supports latest media].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [supports latest media]; otherwise, <c>false</c>.</value>
|
||||||
|
public bool SupportsLatestMedia { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance can filter.
|
/// Gets or sets a value indicating whether this instance can filter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -5,8 +5,10 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Chapters;
|
using MediaBrowser.Model.Chapters;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -22,12 +24,14 @@ namespace MediaBrowser.Providers.Chapters
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
|
private readonly IItemRepository _itemRepo;
|
||||||
|
|
||||||
public ChapterManager(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config)
|
public ChapterManager(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IItemRepository itemRepo)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_itemRepo = itemRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddParts(IEnumerable<IChapterProvider> chapterProviders)
|
public void AddParts(IEnumerable<IChapterProvider> chapterProviders)
|
||||||
|
@ -236,5 +240,15 @@ namespace MediaBrowser.Providers.Chapters
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ChapterInfo> GetChapters(string itemId)
|
||||||
|
{
|
||||||
|
return _itemRepo.GetChapters(new Guid(itemId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SaveChapters(string itemId, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return _itemRepo.SaveChapters(new Guid(itemId), chapters, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,7 +261,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
|
await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +279,14 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit accuracy to milliseconds to match xml saving
|
// Limit accuracy to milliseconds to match xml saving
|
||||||
var ms = Math.Round(TimeSpan.FromTicks(chapter.start / 100).TotalMilliseconds);
|
var secondsString = chapter.start_time;
|
||||||
|
double seconds;
|
||||||
|
|
||||||
|
if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out seconds))
|
||||||
|
{
|
||||||
|
var ms = Math.Round(TimeSpan.FromSeconds(seconds).TotalMilliseconds);
|
||||||
info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
|
info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
|
||||||
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -453,6 +453,7 @@ namespace MediaBrowser.Server.Implementations.Channels
|
||||||
MaxPageSize = features.MaxPageSize,
|
MaxPageSize = features.MaxPageSize,
|
||||||
MediaTypes = features.MediaTypes,
|
MediaTypes = features.MediaTypes,
|
||||||
SupportsSortOrderToggle = features.SupportsSortOrderToggle,
|
SupportsSortOrderToggle = features.SupportsSortOrderToggle,
|
||||||
|
SupportsLatestMedia = provider is ISupportsLatestMedia,
|
||||||
Name = channel.Name,
|
Name = channel.Name,
|
||||||
Id = channel.Id.ToString("N"),
|
Id = channel.Id.ToString("N"),
|
||||||
CanDownloadAllMedia = isIndexable
|
CanDownloadAllMedia = isIndexable
|
||||||
|
|
|
@ -133,8 +133,10 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
? null
|
? null
|
||||||
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
|
: source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
|
||||||
|
|
||||||
source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, preferredSubs,
|
source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams,
|
||||||
user.Configuration.SubtitleMode, audioLangage);
|
preferredSubs,
|
||||||
|
user.Configuration.SubtitleMode,
|
||||||
|
audioLangage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
SubtitlePlaybackMode mode,
|
SubtitlePlaybackMode mode,
|
||||||
string audioTrackLanguage)
|
string audioTrackLanguage)
|
||||||
{
|
{
|
||||||
var languages = preferredLanguages as List<string> ?? preferredLanguages.ToList();
|
var languages = preferredLanguages.ToList();
|
||||||
streams = GetSortedStreams(streams, MediaStreamType.Subtitle, languages).ToList();
|
streams = GetSortedStreams(streams, MediaStreamType.Subtitle, languages).ToList();
|
||||||
|
|
||||||
var full = streams.Where(s => !s.IsForced);
|
var full = streams.Where(s => !s.IsForced);
|
||||||
|
@ -87,7 +87,12 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
.Where(i => i.Type == type);
|
.Where(i => i.Type == type);
|
||||||
|
|
||||||
// Give some preferance to external text subs for better performance
|
// Give some preferance to external text subs for better performance
|
||||||
return orderStreams.OrderBy(i => languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase)))
|
return orderStreams.OrderBy(i =>
|
||||||
|
{
|
||||||
|
var index = languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
return index == -1 ? 100 : index;
|
||||||
|
})
|
||||||
.ThenBy(i => i.IsDefault)
|
.ThenBy(i => i.IsDefault)
|
||||||
.ThenBy(i => !i.IsGraphicalSubtitleStream)
|
.ThenBy(i => !i.IsGraphicalSubtitleStream)
|
||||||
.ThenBy(i => i.IsExternal)
|
.ThenBy(i => i.IsExternal)
|
||||||
|
|
|
@ -88,29 +88,19 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
if (query.IncludeExternalContent)
|
if (query.IncludeExternalContent)
|
||||||
{
|
{
|
||||||
var channelsTask = Task.Run(() => _channelManager.GetChannels(new ChannelQuery
|
var channelResult = await _channelManager.GetChannels(new ChannelQuery
|
||||||
{
|
{
|
||||||
Limit = 0,
|
Limit = 0,
|
||||||
UserId = query.UserId
|
UserId = query.UserId
|
||||||
|
|
||||||
}, cancellationToken), cancellationToken);
|
}, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// Avoid implicitly captured closure.
|
|
||||||
var token = cancellationToken;
|
|
||||||
var liveTvTask = Task.Run(() => _liveTvManager.GetLiveTvInfo(token), cancellationToken);
|
|
||||||
|
|
||||||
await Task.WhenAll(channelsTask, liveTvTask).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var channelResult = channelsTask.Result;
|
|
||||||
|
|
||||||
if (channelResult.TotalRecordCount > 0)
|
if (channelResult.TotalRecordCount > 0)
|
||||||
{
|
{
|
||||||
list.Add(await _channelManager.GetInternalChannelFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
list.Add(await _channelManager.GetInternalChannelFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
var liveTvInfo = liveTvTask.Result;
|
if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
|
||||||
|
|
||||||
if (liveTvInfo.EnabledUsers.Contains(query.UserId))
|
|
||||||
{
|
{
|
||||||
list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1734,6 +1734,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<User> GetEnabledUsers()
|
||||||
|
{
|
||||||
|
var service = ActiveService;
|
||||||
|
|
||||||
|
return _userManager.Users
|
||||||
|
.Where(i => i.Configuration.EnableLiveTvAccess && service != null);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the tuner.
|
/// Resets the tuner.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -733,7 +733,7 @@
|
||||||
"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
|
"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
|
||||||
"HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.",
|
"HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.",
|
||||||
"HeaderDownloadSubtitlesFor": "Download subtitles for:",
|
"HeaderDownloadSubtitlesFor": "Download subtitles for:",
|
||||||
"MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb or tagChimp to enable additional chapter metadata options.",
|
"MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb or tagChimp to enable additional chapter options.",
|
||||||
"LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles",
|
"LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles",
|
||||||
"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.",
|
"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.",
|
||||||
"TabSubtitles": "Subtitles",
|
"TabSubtitles": "Subtitles",
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Controller.Chapters;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Controller.Persistence;
|
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
@ -24,48 +23,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
private readonly IMediaEncoder _encoder;
|
private readonly IMediaEncoder _encoder;
|
||||||
|
private readonly IChapterManager _chapterManager;
|
||||||
|
|
||||||
public EncodingManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IMediaEncoder encoder)
|
public EncodingManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IMediaEncoder encoder, IChapterManager chapterManager)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_itemRepo = itemRepo;
|
|
||||||
_encoder = encoder;
|
_encoder = encoder;
|
||||||
}
|
_chapterManager = chapterManager;
|
||||||
|
|
||||||
private string SubtitleCachePath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Path.Combine(_config.ApplicationPaths.CachePath, "subtitles");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetSubtitleCachePath(string originalSubtitlePath, string outputSubtitleExtension)
|
|
||||||
{
|
|
||||||
var ticksParam = _fileSystem.GetLastWriteTimeUtc(originalSubtitlePath).Ticks;
|
|
||||||
|
|
||||||
var filename = (originalSubtitlePath + ticksParam).GetMD5() + outputSubtitleExtension;
|
|
||||||
|
|
||||||
var prefix = filename.Substring(0, 1);
|
|
||||||
|
|
||||||
return Path.Combine(SubtitleCachePath, prefix, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetSubtitleCachePath(string mediaPath, int subtitleStreamIndex, string outputSubtitleExtension)
|
|
||||||
{
|
|
||||||
var ticksParam = string.Empty;
|
|
||||||
|
|
||||||
var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
|
|
||||||
|
|
||||||
var filename = (mediaPath + "_" + subtitleStreamIndex.ToString(_usCulture) + "_" + date.Ticks.ToString(_usCulture) + ticksParam).GetMD5() + outputSubtitleExtension;
|
|
||||||
|
|
||||||
var prefix = filename.Substring(0, 1);
|
|
||||||
|
|
||||||
return Path.Combine(SubtitleCachePath, prefix, filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -202,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
||||||
|
|
||||||
if (saveChapters && changesMade)
|
if (saveChapters && changesMade)
|
||||||
{
|
{
|
||||||
await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
|
await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteDeadImages(currentImages, chapters);
|
DeleteDeadImages(currentImages, chapters);
|
||||||
|
|
|
@ -511,10 +511,6 @@ namespace MediaBrowser.ServerApplication
|
||||||
|
|
||||||
progress.Report(15);
|
progress.Report(15);
|
||||||
|
|
||||||
EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository,
|
|
||||||
MediaEncoder);
|
|
||||||
RegisterSingleInstance(EncodingManager);
|
|
||||||
|
|
||||||
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager);
|
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager);
|
||||||
RegisterSingleInstance(ChannelManager);
|
RegisterSingleInstance(ChannelManager);
|
||||||
|
|
||||||
|
@ -547,9 +543,13 @@ namespace MediaBrowser.ServerApplication
|
||||||
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, ItemRepository);
|
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, ItemRepository);
|
||||||
RegisterSingleInstance(SubtitleManager);
|
RegisterSingleInstance(SubtitleManager);
|
||||||
|
|
||||||
ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager);
|
ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
|
||||||
RegisterSingleInstance(ChapterManager);
|
RegisterSingleInstance(ChapterManager);
|
||||||
|
|
||||||
|
EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger,
|
||||||
|
MediaEncoder, ChapterManager);
|
||||||
|
RegisterSingleInstance(EncodingManager);
|
||||||
|
|
||||||
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
||||||
var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
|
var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
|
||||||
var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));
|
var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user