Add CMS waveform pre-processing panel with backfill endpoints
GET api/track/waveform-status and POST api/track/{id}/waveform (ApiKey);
CmsTrackService methods; TrackPreProcessing page with per-row and
sequential bulk generation; nav links from TrackList and Index.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
using DeepDrftManager.Services;
|
||||
using DeepDrftModels.DTOs;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace DeepDrftManager.Components.Pages.Tracks;
|
||||
|
||||
public partial class TrackPreProcessing : ComponentBase
|
||||
{
|
||||
private List<WaveformStatusDto> _rows = new();
|
||||
private readonly HashSet<string> _generating = new();
|
||||
private bool _loading = true;
|
||||
private bool _bulkRunning;
|
||||
private int _bulkTotal;
|
||||
private int _bulkDone;
|
||||
|
||||
private int _missingCount => _rows.Count(r => !r.HasProfile);
|
||||
|
||||
[Inject] private ICmsTrackService CmsTrackService { get; set; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; set; } = default!;
|
||||
[Inject] private ILogger<TrackPreProcessing> Logger { get; set; } = default!;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadStatus();
|
||||
}
|
||||
|
||||
private async Task LoadStatus()
|
||||
{
|
||||
_loading = true;
|
||||
var result = await CmsTrackService.GetWaveformStatusAsync();
|
||||
_loading = false;
|
||||
|
||||
if (!result.Success || result.Value is null)
|
||||
{
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
Snackbar.Add($"Failed to load waveform status: {error}", Severity.Error);
|
||||
_rows = new List<WaveformStatusDto>();
|
||||
return;
|
||||
}
|
||||
|
||||
_rows = result.Value.OrderBy(r => r.HasProfile).ThenBy(r => r.TrackName).ToList();
|
||||
}
|
||||
|
||||
private bool IsGenerating(string entryKey) => _generating.Contains(entryKey);
|
||||
|
||||
private async Task GenerateOne(WaveformStatusDto row)
|
||||
{
|
||||
if (!await GenerateForRow(row))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Snackbar.Add($"Generated profile for '{row.TrackName}'.", Severity.Success);
|
||||
}
|
||||
|
||||
private async Task GenerateAllMissing()
|
||||
{
|
||||
var missing = _rows.Where(r => !r.HasProfile).ToList();
|
||||
if (missing.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bulkRunning = true;
|
||||
_bulkTotal = missing.Count;
|
||||
_bulkDone = 0;
|
||||
var failures = 0;
|
||||
|
||||
// Sequential by design: one request at a time so a large backfill does not flood the API
|
||||
// with concurrent WAV decodes.
|
||||
foreach (var row in missing)
|
||||
{
|
||||
if (!await GenerateForRow(row))
|
||||
{
|
||||
failures++;
|
||||
}
|
||||
_bulkDone++;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
_bulkRunning = false;
|
||||
|
||||
var succeeded = missing.Count - failures;
|
||||
if (failures == 0)
|
||||
{
|
||||
Snackbar.Add($"Generated {succeeded} profile(s).", Severity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Generated {succeeded} profile(s); {failures} failed.", Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs generation for a single row, flipping its status on success. Returns false on failure
|
||||
/// (a snackbar is raised here for the per-row path; the bulk path aggregates a summary). Marks
|
||||
/// the row busy for the duration so its button shows a spinner and stays disabled.
|
||||
/// </summary>
|
||||
private async Task<bool> GenerateForRow(WaveformStatusDto row)
|
||||
{
|
||||
_generating.Add(row.EntryKey);
|
||||
StateHasChanged();
|
||||
try
|
||||
{
|
||||
var result = await CmsTrackService.GenerateWaveformProfileAsync(row.EntryKey);
|
||||
if (result.Success)
|
||||
{
|
||||
row.HasProfile = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
if (!_bulkRunning)
|
||||
{
|
||||
Snackbar.Add($"Generate failed for '{row.TrackName}': {error}", Severity.Error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Waveform generation failed for {EntryKey}", row.EntryKey);
|
||||
if (!_bulkRunning)
|
||||
{
|
||||
Snackbar.Add($"Generate failed for '{row.TrackName}' — please try again.", Severity.Error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_generating.Remove(row.EntryKey);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user