From 06f33b8e89d242719ed29b8e084b593211fa8557 Mon Sep 17 00:00:00 2001 From: V1ck3s Date: Sun, 4 Jan 2026 22:13:04 +0100 Subject: [PATCH] feat: add form-urlencoded body parsing for Feishin compatibility --- octo-fiesta/Controllers/SubSonicController.cs | 89 +++++++++++++------ 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/octo-fiesta/Controllers/SubSonicController.cs b/octo-fiesta/Controllers/SubSonicController.cs index 019e1a3..c3398d3 100644 --- a/octo-fiesta/Controllers/SubSonicController.cs +++ b/octo-fiesta/Controllers/SubSonicController.cs @@ -40,45 +40,78 @@ public class SubsonicController : ControllerBase } } - // Extract all parameters (query + body) - private async Task> ExtractAllParameters() - { - var parameters = new Dictionary(); - - // Get query parameters - foreach (var query in Request.Query) - { - parameters[query.Key] = query.Value.ToString(); - } - - // Get body parameters (JSON) - if (Request.ContentLength > 0 && Request.ContentType?.Contains("application/json") == true) - { - using var reader = new StreamReader(Request.Body); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrEmpty(body)) + // Extract all parameters (query + body) + private async Task> ExtractAllParameters() + { + var parameters = new Dictionary(); + + // Get query parameters + foreach (var query in Request.Query) + { + parameters[query.Key] = query.Value.ToString(); + } + + // Get body parameters + if (Request.ContentLength > 0 || Request.ContentType != null) + { + // Handle application/x-www-form-urlencoded (OpenSubsonic formPost extension) + if (Request.HasFormContentType) { try { - var bodyParams = JsonSerializer.Deserialize>(body); - if (bodyParams != null) + var form = await Request.ReadFormAsync(); + foreach (var field in form) { - foreach (var param in bodyParams) + parameters[field.Key] = field.Value.ToString(); + } + } + catch + { + // Fall back to manual parsing if ReadFormAsync fails + Request.EnableBuffering(); + using var reader = new StreamReader(Request.Body, leaveOpen: true); + var body = await reader.ReadToEndAsync(); + Request.Body.Position = 0; + + if (!string.IsNullOrEmpty(body)) + { + var formParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(body); + foreach (var param in formParams) { - parameters[param.Key] = param.Value?.ToString() ?? ""; + parameters[param.Key] = param.Value.ToString(); } } } - catch (JsonException) + } + // Handle application/json + else if (Request.ContentType?.Contains("application/json") == true) + { + using var reader = new StreamReader(Request.Body); + var body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrEmpty(body)) { - + try + { + var bodyParams = JsonSerializer.Deserialize>(body); + if (bodyParams != null) + { + foreach (var param in bodyParams) + { + parameters[param.Key] = param.Value?.ToString() ?? ""; + } + } + } + catch (JsonException) + { + + } } } - } - - return parameters; - } + } + + return parameters; + } private async Task<(object Body, string? ContentType)> RelayToSubsonic(string endpoint, Dictionary parameters) {