From 60434abd01c90c06062fa65b6d47c423cb58be69 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Tue, 30 Dec 2025 13:31:13 +0100 Subject: [PATCH] Use utf-8 decode everywhere --- Jellyfin.Plugin.SRFPlay/Api/SRFApiClient.cs | 42 ++++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/Jellyfin.Plugin.SRFPlay/Api/SRFApiClient.cs b/Jellyfin.Plugin.SRFPlay/Api/SRFApiClient.cs index b58f424..3d73d59 100644 --- a/Jellyfin.Plugin.SRFPlay/Api/SRFApiClient.cs +++ b/Jellyfin.Plugin.SRFPlay/Api/SRFApiClient.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.Net; using System.Net.Http; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; @@ -57,6 +58,18 @@ public class SRFApiClient : IDisposable }; } + /// + /// Reads HTTP response content as UTF-8 string. + /// + /// The HTTP content to read. + /// The cancellation token. + /// The content as a UTF-8 decoded string. + private async Task ReadAsUtf8StringAsync(HttpContent content, CancellationToken cancellationToken = default) + { + var bytes = await content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); + return Encoding.UTF8.GetString(bytes); + } + /// /// Creates an HttpClient with optional proxy configuration. /// @@ -187,7 +200,8 @@ public class SRFApiClient : IDisposable RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, - CreateNoWindow = true + CreateNoWindow = true, + StandardOutputEncoding = Encoding.UTF8 }; using var process = new System.Diagnostics.Process { StartInfo = processStartInfo }; @@ -247,12 +261,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogDebug("Latest videos response length: {Length}", content.Length); var result = JsonSerializer.Deserialize(content, _jsonOptions); @@ -286,12 +300,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogDebug("Trending videos response length: {Length}", content.Length); var result = JsonSerializer.Deserialize(content, _jsonOptions); @@ -321,7 +335,7 @@ public class SRFApiClient : IDisposable var response = await _httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); return content; } catch (Exception ex) @@ -349,12 +363,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); var result = JsonSerializer.Deserialize>(content, _jsonOptions); _logger.LogInformation("Successfully fetched {Count} shows for business unit: {BusinessUnit}", result?.Data?.Count ?? 0, businessUnit); @@ -385,12 +399,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); var result = JsonSerializer.Deserialize>(content, _jsonOptions); _logger.LogInformation("Successfully fetched {Count} topics for business unit: {BusinessUnit}", result?.Data?.Count ?? 0, businessUnit); @@ -422,12 +436,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); var result = JsonSerializer.Deserialize>(content, _jsonOptions); _logger.LogDebug("Successfully fetched {Count} videos for show {ShowId}", result?.Data?.Data?.Count ?? 0, showId); @@ -462,12 +476,12 @@ public class SRFApiClient : IDisposable if (!response.IsSuccessStatusCode) { - var errorContent = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var errorContent = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); _logger.LogError("API returned error {StatusCode}: {Error}", response.StatusCode, errorContent); return null; } - var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + var content = await ReadAsUtf8StringAsync(response.Content, cancellationToken).ConfigureAwait(false); // The response structure is: { "data": { "scheduledLivestreams": [...] } } var jsonDoc = JsonSerializer.Deserialize(content, _jsonOptions);