diff options
| author | Niels van Velzen <nielsvanvelzen@users.noreply.github.com> | 2026-01-26 12:00:14 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-26 12:00:14 +0100 |
| commit | 360b6bcce1fc3f353ec23fa82ca415c3035b5710 (patch) | |
| tree | 23851a7e6fdc8436e07436fff2c3d45d5360cdd6 | |
| parent | 92f3c8cf15b265020bca9386f434a8b968a338af (diff) | |
| parent | 7f1a0ff6fce22d4e2f3919e92548d43cdc0229c9 (diff) | |
fix: prevent premature disposal of HTTP subtitle streams
| -rw-r--r-- | MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 78 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dlna/StreamInfo.cs | 4 |
2 files changed, 43 insertions, 39 deletions
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 49ac0fa03..bf7ec05a9 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -172,23 +172,25 @@ namespace MediaBrowser.MediaEncoding.Subtitles 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); - await using (stream.ConfigureAwait(false)) + var result = await DetectCharset(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false); + var detected = result.Detected; + + if (detected is not null) { - var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false); - var detected = result.Detected; - stream.Position = 0; + _logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path); - if (detected is not null) - { - _logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path); + using var stream = await _httpClientFactory.CreateClient(NamedClient.Default) + .GetStreamAsync(new Uri(fileInfo.Path), cancellationToken) + .ConfigureAwait(false); - using var reader = new StreamReader(stream, detected.Encoding); - var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + await using (stream.ConfigureAwait(false)) + { + using var reader = new StreamReader(stream, detected.Encoding); + var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); - return new MemoryStream(Encoding.UTF8.GetBytes(text)); + 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('.'); // Handle PGS subtitles as raw streams for the client to render @@ -941,42 +943,44 @@ namespace MediaBrowser.MediaEncoding.Subtitles .ConfigureAwait(false); } - var stream = await GetStream(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false); - await using (stream.ConfigureAwait(false)) - { - var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false); - var charset = result.Detected?.EncodingName ?? string.Empty; + var result = await DetectCharset(path, mediaSource.Protocol, cancellationToken).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))) - { - charset = 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); + _logger.LogDebug("charset {0} detected for {Path}", charset, path); - return charset; - } + 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) { case MediaProtocol.Http: - { - using var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .GetAsync(new Uri(path), cancellationToken) - .ConfigureAwait(false); - return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - } + { + using var stream = await _httpClientFactory + .CreateClient(NamedClient.Default) + .GetStreamAsync(new Uri(path), cancellationToken) + .ConfigureAwait(false); + + return await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false); + } case MediaProtocol.File: - return AsyncFile.OpenRead(path); + { + return await CharsetDetector.DetectFromFileAsync(path, cancellationToken) + .ConfigureAwait(false); + } + default: - throw new ArgumentOutOfRangeException(nameof(protocol)); + throw new ArgumentOutOfRangeException(nameof(protocol), protocol, "Unsupported protocol"); } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 9edb4115c..551bee89e 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -1252,11 +1252,11 @@ public class StreamInfo stream.Index.ToString(CultureInfo.InvariantCulture), startPositionTicks.ToString(CultureInfo.InvariantCulture), subtitleProfile.Format); - info.IsExternalUrl = false; // Default to API URL + info.IsExternalUrl = false; // Check conditions for potentially using the direct path 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.IsNullOrEmpty(stream.Path) // Path must exist && Uri.TryCreate(stream.Path, UriKind.Absolute, out Uri? uriResult) // Path must be an absolute URI |
