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:
@@ -51,6 +51,26 @@
|
||||
<span>Backfill High-res (@MissingHighResCount)</span>
|
||||
}
|
||||
</MudButton>
|
||||
@* Backfill-Opus (Phase 18.5). Unlike the two waveform buttons, the Opus derive runs on a
|
||||
server-side background worker: the API decides which tracks lack Opus and enqueues them, so
|
||||
there is no client-side "missing N" count to gate on and no per-track progress to render — the
|
||||
action schedules the work and reports the (enqueued / skipped) outcome. Re-runnable: a second
|
||||
press only enqueues tracks still missing Opus. Disabled while a press is in flight. *@
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.GraphicEq"
|
||||
Disabled="@(_bulkRunning || _highResBulkRunning || _opusBackfillRunning)"
|
||||
OnClick="BackfillOpusAsync">
|
||||
@if (_opusBackfillRunning)
|
||||
{
|
||||
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
|
||||
<span>Scheduling…</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Backfill Opus</span>
|
||||
}
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
|
||||
@@ -150,6 +170,11 @@
|
||||
private int _highResBulkTotal;
|
||||
private int _highResBulkDone;
|
||||
|
||||
// Local state for the "Backfill Opus" action. The Opus derive is server-side and background-queued, so
|
||||
// there is no client-side per-track loop or progress total — this flag only guards the button while the
|
||||
// single scheduling call is in flight.
|
||||
private bool _opusBackfillRunning;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Seed the active tab from ?medium= so a catalogue card deep-links straight to its medium. Panel 0
|
||||
@@ -291,4 +316,50 @@
|
||||
Snackbar.Add($"Backfilled {succeeded} high-res datum(s); {failures} failed.", Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kick off the catalogue-wide Backfill-Opus pass. The API enumerates the tracks lacking a complete Opus
|
||||
/// artifact, enqueues a background derive for each, and returns the (enqueued, skipped) counts. This is a
|
||||
/// single scheduling call — the transcodes run server-side afterward — so there is no per-track progress
|
||||
/// to render here, just a busy flag and a result snackbar. Re-runnable: a second press only schedules
|
||||
/// tracks still missing Opus.
|
||||
/// </summary>
|
||||
private async Task BackfillOpusAsync()
|
||||
{
|
||||
_opusBackfillRunning = true;
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
var result = await CmsTrackService.BackfillOpusAsync();
|
||||
if (!result.Success)
|
||||
{
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Failed to start the Opus backfill.";
|
||||
Snackbar.Add(error, Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var (enqueued, skipped) = (result.Value.Enqueued, result.Value.Skipped);
|
||||
if (enqueued == 0)
|
||||
{
|
||||
Snackbar.Add($"All {skipped} track(s) already have Opus — nothing to backfill.", Severity.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add(
|
||||
$"Scheduled {enqueued} Opus transcode(s) in the background ({skipped} already had Opus). " +
|
||||
"They will appear as each finishes.",
|
||||
Severity.Success);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Opus backfill failed to start");
|
||||
Snackbar.Add("Failed to start the Opus backfill.", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_opusBackfillRunning = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user