feature: Phase 18.3 — Opus delivery transport (?format= stream + seek sidecar endpoint)
This commit is contained in:
@@ -205,21 +205,26 @@ public class TrackProxyController : ControllerBase
|
||||
|
||||
/// <summary>
|
||||
/// Proxies audio streaming from DeepDrftAPI as a transparent HTTP Range relay.
|
||||
/// Forwards the incoming Range header upstream and relays the upstream status
|
||||
/// (200 full, 206 partial, 416 unsatisfiable) and range-related response headers
|
||||
/// back to the browser verbatim. The proxy does not slice — the upstream already did.
|
||||
/// Forwards the incoming Range header upstream and the optional <c>format</c> selector
|
||||
/// (Phase 18.3 — <c>opus|lossless</c>, threaded the same way the listing params are),
|
||||
/// and relays the upstream status (200 full, 206 partial, 416 unsatisfiable) and
|
||||
/// range-related response headers back to the browser verbatim. The proxy does not
|
||||
/// slice — the upstream already did.
|
||||
/// </summary>
|
||||
[HttpGet("{trackId}")]
|
||||
public async Task<ActionResult> GetTrack(
|
||||
string trackId,
|
||||
[FromQuery] string? format = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var rangeHeader = Request.Headers.Range.ToString();
|
||||
_logger.LogInformation("Proxying track {TrackId} range '{Range}'", trackId, rangeHeader);
|
||||
_logger.LogInformation("Proxying track {TrackId} range '{Range}' format '{Format}'", trackId, rangeHeader, format);
|
||||
|
||||
var request = new HttpRequestMessage(
|
||||
HttpMethod.Get,
|
||||
$"api/track/{Uri.EscapeDataString(trackId)}");
|
||||
var path = $"api/track/{Uri.EscapeDataString(trackId)}";
|
||||
if (!string.IsNullOrWhiteSpace(format))
|
||||
path += $"?format={Uri.EscapeDataString(format)}";
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, path);
|
||||
|
||||
// Forward the browser's Range header upstream so DeepDrftAPI slices the file.
|
||||
// TryAddWithoutValidation avoids RangeHeaderValue reparsing — we relay the raw
|
||||
@@ -355,4 +360,40 @@ public class TrackProxyController : ControllerBase
|
||||
return Content(json, "application/json");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxies a track's Opus seek/setup sidecar (raw bytes) from DeepDrftAPI (Phase 18.3). Unauthenticated,
|
||||
/// same posture as the audio stream forward. The sidecar is a small one-time fetch (≤ ~115 KB), so it is
|
||||
/// buffered and relayed; a 404 (no Opus artifact / no sidecar stored) passes through so the client
|
||||
/// degrades to lossless rather than treating it as an error. The "opus/seekdata" 3-segment route makes a
|
||||
/// collision with the parameterized "{trackId}" audio route impossible.
|
||||
/// </summary>
|
||||
[HttpGet("{trackId}/opus/seekdata")]
|
||||
public async Task<ActionResult> GetOpusSeekData(string trackId, CancellationToken ct = default)
|
||||
{
|
||||
var path = $"api/track/{Uri.EscapeDataString(trackId)}/opus/seekdata";
|
||||
|
||||
HttpResponseMessage upstream;
|
||||
try
|
||||
{
|
||||
upstream = await _upstream.GetAsync(path, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Upstream call to DeepDrftAPI track/{TrackId}/opus/seekdata failed", trackId);
|
||||
return StatusCode(502, "Upstream unavailable");
|
||||
}
|
||||
|
||||
using (upstream)
|
||||
{
|
||||
if (!upstream.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogWarning("DeepDrftAPI track/{TrackId}/opus/seekdata returned {Status}", trackId, (int)upstream.StatusCode);
|
||||
return StatusCode((int)upstream.StatusCode);
|
||||
}
|
||||
|
||||
var bytes = await upstream.Content.ReadAsByteArrayAsync(ct);
|
||||
return File(bytes, "application/octet-stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user