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);