mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-02-05 00:31:51 +03:00
Merge pull request #14809 from lostb1t/fix/subtitleencoder
fix: prevent premature disposal of HTTP subtitle streams
This commit is contained in:
@@ -172,23 +172,25 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
|
|
||||||
private async Task<Stream> GetSubtitleStream(SubtitleInfo fileInfo, CancellationToken cancellationToken)
|
private async Task<Stream> GetSubtitleStream(SubtitleInfo fileInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (fileInfo.IsExternal)
|
if (fileInfo.Protocol == MediaProtocol.Http)
|
||||||
{
|
{
|
||||||
var stream = await GetStream(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false);
|
var result = await DetectCharset(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false);
|
||||||
await using (stream.ConfigureAwait(false))
|
var detected = result.Detected;
|
||||||
|
|
||||||
|
if (detected is not null)
|
||||||
{
|
{
|
||||||
var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false);
|
_logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path);
|
||||||
var detected = result.Detected;
|
|
||||||
stream.Position = 0;
|
|
||||||
|
|
||||||
if (detected is not null)
|
using var stream = await _httpClientFactory.CreateClient(NamedClient.Default)
|
||||||
|
.GetStreamAsync(new Uri(fileInfo.Path), cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
await using (stream.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path);
|
using var reader = new StreamReader(stream, detected.Encoding);
|
||||||
|
var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
using var reader = new StreamReader(stream, detected.Encoding);
|
return new MemoryStream(Encoding.UTF8.GetBytes(text));
|
||||||
var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return new MemoryStream(Encoding.UTF8.GetBytes(text));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +220,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec)
|
var currentFormat = subtitleStream.Codec ?? Path.GetExtension(subtitleStream.Path)
|
||||||
.TrimStart('.');
|
.TrimStart('.');
|
||||||
|
|
||||||
// Handle PGS subtitles as raw streams for the client to render
|
// Handle PGS subtitles as raw streams for the client to render
|
||||||
@@ -941,42 +943,44 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = await GetStream(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false);
|
var result = await DetectCharset(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false);
|
||||||
await using (stream.ConfigureAwait(false))
|
var charset = result.Detected?.EncodingName ?? string.Empty;
|
||||||
|
|
||||||
|
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
|
||||||
|
if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal))
|
||||||
|
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false);
|
charset = string.Empty;
|
||||||
var charset = result.Detected?.EncodingName ?? string.Empty;
|
|
||||||
|
|
||||||
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
|
|
||||||
if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal))
|
|
||||||
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
charset = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogDebug("charset {0} detected for {Path}", charset, path);
|
|
||||||
|
|
||||||
return charset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("charset {0} detected for {Path}", charset, path);
|
||||||
|
|
||||||
|
return charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
|
private async Task<DetectionResult> DetectCharset(string path, MediaProtocol protocol, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
switch (protocol)
|
switch (protocol)
|
||||||
{
|
{
|
||||||
case MediaProtocol.Http:
|
case MediaProtocol.Http:
|
||||||
{
|
{
|
||||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
using var stream = await _httpClientFactory
|
||||||
.GetAsync(new Uri(path), cancellationToken)
|
.CreateClient(NamedClient.Default)
|
||||||
.ConfigureAwait(false);
|
.GetStreamAsync(new Uri(path), cancellationToken)
|
||||||
return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
return await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
case MediaProtocol.File:
|
case MediaProtocol.File:
|
||||||
return AsyncFile.OpenRead(path);
|
{
|
||||||
|
return await CharsetDetector.DetectFromFileAsync(path, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(protocol));
|
throw new ArgumentOutOfRangeException(nameof(protocol), protocol, "Unsupported protocol");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1252,11 +1252,11 @@ public class StreamInfo
|
|||||||
stream.Index.ToString(CultureInfo.InvariantCulture),
|
stream.Index.ToString(CultureInfo.InvariantCulture),
|
||||||
startPositionTicks.ToString(CultureInfo.InvariantCulture),
|
startPositionTicks.ToString(CultureInfo.InvariantCulture),
|
||||||
subtitleProfile.Format);
|
subtitleProfile.Format);
|
||||||
info.IsExternalUrl = false; // Default to API URL
|
info.IsExternalUrl = false;
|
||||||
|
|
||||||
// Check conditions for potentially using the direct path
|
// Check conditions for potentially using the direct path
|
||||||
if (stream.IsExternal // Must be external
|
if (stream.IsExternal // Must be external
|
||||||
&& MediaSource?.Protocol != MediaProtocol.File // Main media must not be a local file
|
&& stream.SupportsExternalStream
|
||||||
&& string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) // Format must match (no conversion needed)
|
&& string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) // Format must match (no conversion needed)
|
||||||
&& !string.IsNullOrEmpty(stream.Path) // Path must exist
|
&& !string.IsNullOrEmpty(stream.Path) // Path must exist
|
||||||
&& Uri.TryCreate(stream.Path, UriKind.Absolute, out Uri? uriResult) // Path must be an absolute URI
|
&& Uri.TryCreate(stream.Path, UriKind.Absolute, out Uri? uriResult) // Path must be an absolute URI
|
||||||
|
|||||||
Reference in New Issue
Block a user