From ad4a3af41e8795fbc0fae7b29e3e2cdf101394ba Mon Sep 17 00:00:00 2001 From: bransoned Date: Sun, 11 Jan 2026 18:47:13 -0500 Subject: [PATCH] #31 (upstream) Fix iOS client local streaming issues --- octo-fiesta/Program.cs | 1 + .../Services/Subsonic/SubsonicProxyService.cs | 46 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/octo-fiesta/Program.cs b/octo-fiesta/Program.cs index 6eaddcf..7c08988 100644 --- a/octo-fiesta/Program.cs +++ b/octo-fiesta/Program.cs @@ -16,6 +16,7 @@ builder.Services.AddControllers(); builder.Services.AddHttpClient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddHttpContextAccessor(); // Exception handling builder.Services.AddExceptionHandler(); diff --git a/octo-fiesta/Services/Subsonic/SubsonicProxyService.cs b/octo-fiesta/Services/Subsonic/SubsonicProxyService.cs index ff531f2..6de4595 100644 --- a/octo-fiesta/Services/Subsonic/SubsonicProxyService.cs +++ b/octo-fiesta/Services/Subsonic/SubsonicProxyService.cs @@ -10,13 +10,16 @@ public class SubsonicProxyService { private readonly HttpClient _httpClient; private readonly SubsonicSettings _subsonicSettings; + private readonly IHttpContextAccessor _httpContextAccessor; public SubsonicProxyService( IHttpClientFactory httpClientFactory, - Microsoft.Extensions.Options.IOptions subsonicSettings) + Microsoft.Extensions.Options.IOptions subsonicSettings, + IHttpContextAccessor httpContextAccessor) { _httpClient = httpClientFactory.CreateClient(); _subsonicSettings = subsonicSettings.Value; + _httpContextAccessor = httpContextAccessor; } /// @@ -66,11 +69,33 @@ public class SubsonicProxyService { try { + // Get HTTP context for request/response forwarding + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext == null) + { + return new StatusCodeResult(500); + } + + var incomingRequest = httpContext.Request; + var outgoingResponse = httpContext.Response; + var query = string.Join("&", parameters.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}")); var url = $"{_subsonicSettings.Url}/rest/stream?{query}"; - + using var request = new HttpRequestMessage(HttpMethod.Get, url); + + // Forward Range headers (fix for iOS client) + if (incomingRequest.Headers.TryGetValue("Range", out var range)) + { + request.Headers.TryAddWithoutValidation("Range", range.ToString()); + } + + if (incomingRequest.Headers.TryGetValue("If-Range", out var ifRange)) + { + request.Headers.TryAddWithoutValidation("If-Range", ifRange.ToString()); + } + var response = await _httpClient.SendAsync( request, HttpCompletionOption.ResponseHeadersRead, @@ -81,6 +106,23 @@ public class SubsonicProxyService return new StatusCodeResult((int)response.StatusCode); } + // Iterate over and forward streaming-required headers + foreach (var header in new[] + { + "Accept-Ranges", + "Content-Range", + "Content-Length", + "ETag", + "Last-Modified" + }) + { + if (response.Headers.TryGetValues(header, out var values) || + response.Content.Headers.TryGetValues(header, out values)) + { + outgoingResponse.Headers[header] = values.ToArray(); + } + } + var stream = await response.Content.ReadAsStreamAsync(cancellationToken); var contentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";