mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
refactor: extract subsonic controller logic into specialized services
- Extract SubsonicRequestParser for HTTP parameter extraction - Extract SubsonicResponseBuilder for XML/JSON response formatting - Extract SubsonicModelMapper for search result parsing and merging - Extract SubsonicProxyService for upstream Subsonic server communication - Add comprehensive test coverage (45 tests) for all new services - Reduce SubsonicController from 1174 to 666 lines (-43%) All tests passing. Build succeeds with 0 errors.
This commit is contained in:
100
octo-fiesta/Services/Subsonic/SubsonicProxyService.cs
Normal file
100
octo-fiesta/Services/Subsonic/SubsonicProxyService.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using octo_fiesta.Models.Settings;
|
||||
|
||||
namespace octo_fiesta.Services.Subsonic;
|
||||
|
||||
/// <summary>
|
||||
/// Handles proxying requests to the underlying Subsonic server.
|
||||
/// </summary>
|
||||
public class SubsonicProxyService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly SubsonicSettings _subsonicSettings;
|
||||
|
||||
public SubsonicProxyService(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
Microsoft.Extensions.Options.IOptions<SubsonicSettings> subsonicSettings)
|
||||
{
|
||||
_httpClient = httpClientFactory.CreateClient();
|
||||
_subsonicSettings = subsonicSettings.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relays a request to the Subsonic server and returns the response.
|
||||
/// </summary>
|
||||
public async Task<(byte[] Body, string? ContentType)> RelayAsync(
|
||||
string endpoint,
|
||||
Dictionary<string, string> parameters)
|
||||
{
|
||||
var query = string.Join("&", parameters.Select(kv =>
|
||||
$"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
|
||||
var url = $"{_subsonicSettings.Url}/{endpoint}?{query}";
|
||||
|
||||
HttpResponseMessage response = await _httpClient.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var body = await response.Content.ReadAsByteArrayAsync();
|
||||
var contentType = response.Content.Headers.ContentType?.ToString();
|
||||
|
||||
return (body, contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely relays a request to the Subsonic server, returning null on failure.
|
||||
/// </summary>
|
||||
public async Task<(byte[]? Body, string? ContentType, bool Success)> RelaySafeAsync(
|
||||
string endpoint,
|
||||
Dictionary<string, string> parameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await RelayAsync(endpoint, parameters);
|
||||
return (result.Body, result.ContentType, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (null, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relays a stream request to the Subsonic server with range processing support.
|
||||
/// </summary>
|
||||
public async Task<IActionResult> RelayStreamAsync(
|
||||
Dictionary<string, string> parameters,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
var response = await _httpClient.SendAsync(
|
||||
request,
|
||||
HttpCompletionOption.ResponseHeadersRead,
|
||||
cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return new StatusCodeResult((int)response.StatusCode);
|
||||
}
|
||||
|
||||
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
var contentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";
|
||||
|
||||
return new FileStreamResult(stream, contentType)
|
||||
{
|
||||
EnableRangeProcessing = true
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(new { error = $"Error streaming from Subsonic: {ex.Message}" })
|
||||
{
|
||||
StatusCode = 500
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user