CMS Phase 9 Wave 3: Release Archive tab, medium selector, Session/Mix browsers

Renames Genre tab to Release Archive with switch-free medium card group
(Enum.GetValues-driven). Adds MediumFields single dispatch + CutFields/SessionFields/
MixFields per-medium sections embedded by all five upload/edit forms. BatchUpload
enforces single-track invariant for Session/Mix. Adds CmsSessionBrowser (hero-image
upload) and CmsMixBrowser (waveform status + per-row Generate trigger).
ICmsReleaseService/CmsReleaseService wraps api/release endpoints.
Note: medium selector is forward-compat only — API write path pending.
This commit is contained in:
daniel-c-harvey
2026-06-12 23:07:15 -04:00
parent 5f7eaed112
commit 2f47efeb46
22 changed files with 970 additions and 45 deletions
@@ -5,6 +5,7 @@
@attribute [Authorize]
@inject ICmsTrackService CmsTrackService
@inject ICmsReleaseService CmsReleaseService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager Navigation
@inject ISnackbar Snackbar
@@ -32,6 +33,8 @@
<MudTextField @bind-Value="_album" Label="Album" Variant="Variant.Outlined" />
<MudTextField @bind-Value="_genre" Label="Genre" Variant="Variant.Outlined" />
<MediumFields @bind-Medium="_medium" @bind-ReleaseType="_releaseType" Disabled="_isUploading" />
<MudField Label="Cover Art" Variant="Variant.Outlined" InnerPadding="false">
<MudStack Spacing="3">
@if (_selectedImageFile is { } selectedImage)
@@ -104,6 +107,8 @@
private string _album = string.Empty;
private string _genre = string.Empty;
private string _releaseDate = string.Empty;
private ReleaseType _releaseType = ReleaseType.Single;
private ReleaseMedium _medium = ReleaseMedium.Cut;
private string? _errorMessage;
private bool _isUploading;
@@ -205,11 +210,24 @@
string.IsNullOrWhiteSpace(_releaseDate) ? null : _releaseDate,
_selectedFile.Name,
createdByUserId,
releaseType: ReleaseType.Single,
trackNumber: 1);
_releaseType,
trackNumber: 1,
_medium);
if (result.Success)
{
// Mix uploads fire the server-side high-res waveform trigger (§3.4) — the CMS computes
// nothing. Non-blocking: a failed trigger is recoverable from the Mixes browser.
if (_medium == ReleaseMedium.Mix && result.Value?.ReleaseId is { } mixReleaseId)
{
var waveformResult = await CmsReleaseService.GenerateMixWaveformAsync(mixReleaseId);
if (!waveformResult.Success)
{
Logger.LogWarning("TrackNew: mix waveform trigger failed for release {ReleaseId}", mixReleaseId);
Snackbar.Add("Mix uploaded, but waveform generation failed. Retry from the Mixes browser.", Severity.Warning);
}
}
// The upload endpoint does not accept an imagePath, so link the cover art with a
// follow-up metadata update — same two-step pattern TrackEdit uses.
if (_imagePath is { } imgPath && result.Value is { } created)