Wire Opus end-to-end playback + Backfill-Opus action (Phase 18.5)

Player picks Opus when the browser can decode it and a sidecar exists (else lossless), injecting the sidecar before stream init; seek reuses the same format. Adds the Backfill-Opus bulk API endpoint + CMS action.
This commit is contained in:
daniel-c-harvey
2026-06-23 12:39:13 -04:00
parent dce5530890
commit 2bde4908d7
12 changed files with 961 additions and 1 deletions
@@ -765,6 +765,45 @@ public class CmsTrackService : ICmsTrackService
}
}
public async Task<ResultContainer<OpusBackfillResult>> BackfillOpusAsync(CancellationToken ct = default)
{
var client = _httpClientFactory.CreateClient(ContentCmsClientName);
HttpResponseMessage response;
try
{
response = await client.PostAsync("api/track/opus/backfill", null, ct);
}
catch (Exception ex)
{
_logger.LogError(ex, "Content API call failed for Opus backfill");
return ResultContainer<OpusBackfillResult>.CreateFailResult("Content API is unreachable.");
}
using (response)
{
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync(ct);
_logger.LogError("Content API Opus backfill failed: {Status} {Body}", (int)response.StatusCode, body);
return ResultContainer<OpusBackfillResult>.CreateFailResult("Failed to start the Opus backfill.");
}
OpusBackfillResult payload;
try
{
payload = await response.Content.ReadFromJsonAsync<OpusBackfillResult>(ct);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to deserialize Opus backfill response from Content API");
return ResultContainer<OpusBackfillResult>.CreateFailResult("Content API returned an unexpected response.");
}
return ResultContainer<OpusBackfillResult>.CreatePassResult(payload);
}
}
public async Task<ResultContainer<List<ReleaseDto>>> GetReleasesAsync(CancellationToken ct = default)
{
var client = _httpClientFactory.CreateClient(ContentCmsClientName);