@page "/tracks" @page "/tracks/albums" @page "/tracks/genres" @using DeepDrftManager.Services @inject CmsTrackBrowserViewModel VM @inject ICmsTrackService CmsTrackService @inject ISnackbar Snackbar @inject ILogger Logger @inject NavigationManager NavigationManager @attribute [Authorize] Tracks — DeepDrft CMS Tracks @if (VM.Mode == BrowseMode.Tracks) { @if (_bulkRunning) { Generating @_bulkDone / @_bulkTotal… } else { Generate All Missing (@(_grid?.GetMissingCount() ?? 0)) } } Tracks Albums Genres @if (VM.Mode == BrowseMode.Tracks) { } else if (VM.Mode == BrowseMode.Albums) { Album browser — coming in the next wave. } else { } @code { private CmsTrackGrid? _grid; // Local state for the parent-owned "Generate All Missing" bulk run. private bool _bulkRunning; private int _bulkTotal; private int _bulkDone; protected override async Task OnInitializedAsync() { var uri = NavigationManager.Uri; var initial = uri.Contains("/tracks/albums", StringComparison.OrdinalIgnoreCase) ? BrowseMode.Albums : uri.Contains("/tracks/genres", StringComparison.OrdinalIgnoreCase) ? BrowseMode.Genres : BrowseMode.Tracks; await VM.SwitchModeAsync(initial); } private async Task OnModeChanged(BrowseMode mode) { await VM.SwitchModeAsync(mode); var path = mode switch { BrowseMode.Albums => "/tracks/albums", BrowseMode.Genres => "/tracks/genres", _ => "/tracks" }; NavigationManager.NavigateTo(path, replace: true); StateHasChanged(); } private void OnExpandedGenreChanged(string? genre) { VM.SetExpandedGenre(genre); StateHasChanged(); } /// /// Backfill every track missing a waveform profile, one request at a time so a large backfill /// does not flood the API with concurrent WAV decodes. On completion, refreshes the grid's /// status map so the per-row icons reflect the new state. /// private async Task GenerateAllMissingAsync() { var statusResult = await CmsTrackService.GetWaveformStatusAsync(); if (!statusResult.Success || statusResult.Value is null) { var error = statusResult.Messages.FirstOrDefault()?.Message ?? "Unknown error"; Snackbar.Add($"Failed to load waveform status: {error}", Severity.Error); return; } var missing = statusResult.Value.Where(s => !s.HasProfile).ToList(); if (missing.Count == 0) { return; } _bulkRunning = true; _bulkTotal = missing.Count; _bulkDone = 0; _grid?.SetBulkRunning(true); var failures = 0; foreach (var status in missing) { try { var result = await CmsTrackService.GenerateWaveformProfileAsync(status.EntryKey); if (!result.Success) { failures++; } } catch (Exception ex) { Logger.LogError(ex, "Waveform generation failed for {EntryKey}", status.EntryKey); failures++; } _bulkDone++; StateHasChanged(); } _bulkRunning = false; _grid?.SetBulkRunning(false); if (_grid is not null) { await _grid.RefreshWaveformStatusAsync(); } 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); } } }