mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-21 17:44:43 +01:00
Make probesize and analyzeduration configurable and simplify circular
dependencies Makes the probesize and analyzeduration configurable with env args. (`JELLYFIN_FFmpeg_probesize` and `FFmpeg_analyzeduration`)
This commit is contained in:
@@ -5,7 +5,7 @@ using MediaBrowser.Model.MediaInfo;
|
||||
namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface ISubtitleWriter
|
||||
/// Interface ISubtitleWriter.
|
||||
/// </summary>
|
||||
public interface ISubtitleWriter
|
||||
{
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
/// <summary>
|
||||
/// JSON subtitle writer.
|
||||
/// </summary>
|
||||
public class JsonWriter : ISubtitleWriter
|
||||
{
|
||||
private readonly IJsonSerializer _json;
|
||||
|
||||
public JsonWriter(IJsonSerializer json)
|
||||
{
|
||||
_json = json;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||
using (var writer = new Utf8JsonWriter(stream))
|
||||
{
|
||||
var json = _json.SerializeToString(info);
|
||||
var trackevents = info.TrackEvents;
|
||||
writer.WriteStartArray("TrackEvents");
|
||||
|
||||
writer.Write(json);
|
||||
for (int i = 0; i < trackevents.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var current = trackevents[i];
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WriteString("Id", current.Id);
|
||||
writer.WriteString("Text", current.Text);
|
||||
writer.WriteNumber("StartPositionTicks", current.StartPositionTicks);
|
||||
writer.WriteNumber("EndPositionTicks", current.EndPositionTicks);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,19 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||
{
|
||||
var index = 1;
|
||||
var trackEvents = info.TrackEvents;
|
||||
|
||||
foreach (var trackEvent in info.TrackEvents)
|
||||
for (int i = 0; i < trackEvents.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
writer.WriteLine(index.ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(@"{0:hh\:mm\:ss\,fff} --> {1:hh\:mm\:ss\,fff}", TimeSpan.FromTicks(trackEvent.StartPositionTicks), TimeSpan.FromTicks(trackEvent.EndPositionTicks));
|
||||
var trackEvent = trackEvents[i];
|
||||
|
||||
writer.WriteLine((i + 1).ToString(CultureInfo.InvariantCulture));
|
||||
writer.WriteLine(
|
||||
@"{0:hh\:mm\:ss\,fff} --> {1:hh\:mm\:ss\,fff}",
|
||||
TimeSpan.FromTicks(trackEvent.StartPositionTicks),
|
||||
TimeSpan.FromTicks(trackEvent.EndPositionTicks));
|
||||
|
||||
var text = trackEvent.Text;
|
||||
|
||||
@@ -29,9 +34,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
|
||||
|
||||
writer.WriteLine(text);
|
||||
writer.WriteLine(string.Empty);
|
||||
|
||||
index++;
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using UtfUnknown;
|
||||
|
||||
@@ -30,28 +29,25 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly IJsonSerializer _json;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IProcessFactory _processFactory;
|
||||
|
||||
public SubtitleEncoder(
|
||||
ILibraryManager libraryManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILogger<SubtitleEncoder> logger,
|
||||
IApplicationPaths appPaths,
|
||||
IFileSystem fileSystem,
|
||||
IMediaEncoder mediaEncoder,
|
||||
IJsonSerializer json,
|
||||
IHttpClient httpClient,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IProcessFactory processFactory)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_logger = loggerFactory.CreateLogger(nameof(SubtitleEncoder));
|
||||
_logger = logger;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_json = json;
|
||||
_httpClient = httpClient;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_processFactory = processFactory;
|
||||
@@ -59,7 +55,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles");
|
||||
|
||||
private Stream ConvertSubtitles(Stream stream,
|
||||
private Stream ConvertSubtitles(
|
||||
Stream stream,
|
||||
string inputFormat,
|
||||
string outputFormat,
|
||||
long startTimeTicks,
|
||||
@@ -170,7 +167,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
&& (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd))
|
||||
{
|
||||
var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id));
|
||||
inputFiles = mediaSourceItem.GetPlayableStreamFileNames(_mediaEncoder);
|
||||
inputFiles = mediaSourceItem.GetPlayableStreamFileNames();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -179,32 +176,27 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var stream = await GetSubtitleStream(fileInfo.Path, subtitleStream.Language, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false);
|
||||
var stream = await GetSubtitleStream(fileInfo.Path, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return (stream, fileInfo.Format);
|
||||
}
|
||||
|
||||
private async Task<Stream> GetSubtitleStream(string path, string language, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken)
|
||||
private async Task<Stream> GetSubtitleStream(string path, MediaProtocol protocol, bool requiresCharset, CancellationToken cancellationToken)
|
||||
{
|
||||
if (requiresCharset)
|
||||
{
|
||||
var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var charset = CharsetDetector.DetectFromBytes(bytes).Detected?.EncodingName;
|
||||
_logger.LogDebug("charset {CharSet} detected for {Path}", charset ?? "null", path);
|
||||
|
||||
if (!string.IsNullOrEmpty(charset))
|
||||
using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
// Make sure we have all the code pages we can get
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
using (var inputStream = new MemoryStream(bytes))
|
||||
using (var reader = new StreamReader(inputStream, Encoding.GetEncoding(charset)))
|
||||
var result = CharsetDetector.DetectFromStream(stream).Detected;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
_logger.LogDebug("charset {CharSet} detected for {Path}", result.EncodingName, path);
|
||||
|
||||
using var reader = new StreamReader(stream, result.Encoding);
|
||||
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
|
||||
bytes = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
return new MemoryStream(bytes);
|
||||
return new MemoryStream(Encoding.UTF8.GetBytes(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,7 +315,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new JsonWriter(_json);
|
||||
return new JsonWriter();
|
||||
}
|
||||
if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -544,7 +536,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
if (!File.Exists(outputPath))
|
||||
{
|
||||
await ExtractTextSubtitleInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), subtitleStreamIndex, outputCodec, outputPath, cancellationToken).ConfigureAwait(false);
|
||||
await ExtractTextSubtitleInternal(
|
||||
_mediaEncoder.GetInputArgument(inputFiles, protocol),
|
||||
subtitleStreamIndex,
|
||||
outputCodec,
|
||||
outputPath,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -572,8 +569,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
|
||||
var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath,
|
||||
subtitleStreamIndex, outputCodec, outputPath);
|
||||
var processArgs = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"",
|
||||
inputPath,
|
||||
subtitleStreamIndex,
|
||||
outputCodec,
|
||||
outputPath);
|
||||
|
||||
var process = _processFactory.Create(new ProcessOptions
|
||||
{
|
||||
@@ -721,41 +723,38 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken)
|
||||
{
|
||||
var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false);
|
||||
using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
|
||||
|
||||
var charset = CharsetDetector.DetectFromBytes(bytes).Detected?.EncodingName;
|
||||
_logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path);
|
||||
|
||||
_logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path);
|
||||
|
||||
return charset;
|
||||
return charset;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<byte[]> GetBytes(string path, MediaProtocol protocol, CancellationToken cancellationToken)
|
||||
private Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
|
||||
{
|
||||
if (protocol == MediaProtocol.Http)
|
||||
switch (protocol)
|
||||
{
|
||||
var opts = new HttpRequestOptions()
|
||||
{
|
||||
Url = path,
|
||||
CancellationToken = cancellationToken
|
||||
};
|
||||
using (var file = await _httpClient.Get(opts).ConfigureAwait(false))
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await file.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
case MediaProtocol.Http:
|
||||
var opts = new HttpRequestOptions()
|
||||
{
|
||||
Url = path,
|
||||
CancellationToken = cancellationToken,
|
||||
BufferContent = true
|
||||
};
|
||||
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
if (protocol == MediaProtocol.File)
|
||||
{
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
return _httpClient.Get(opts);
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(protocol));
|
||||
case MediaProtocol.File:
|
||||
return Task.FromResult<Stream>(File.OpenRead(path));
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(protocol));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,5 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
writer.WriteLine("</tt>");
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatTime(long ticks)
|
||||
{
|
||||
var time = TimeSpan.FromTicks(ticks);
|
||||
|
||||
return string.Format(@"{0:hh\:mm\:ss\,fff}", time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user