Session upload now carries a deferred hero-image input; the submit handler
creates the release then POSTs the held hero to the existing resource-addressed
endpoint. Hero is optional with a non-blocking warn-if-missing gate. The
per-row hero upload in CmsSessionBrowser remains the replace/correct path.
BatchTrackDetail gains ShowTrackName parameter (default true); BatchUpload removes the
Track Name input on the single-track path; BatchEdit suppresses it there too. Both sync
_tracks[0].TrackName = _albumName on submit/save so names can never diverge. Cut path
unchanged.
Covers AlbumHeaderFields MudTextField label + RequiredError, and the matching
code-side validation messages in BatchEdit and BatchUpload for consistency.
Cut rows continue to show ReleaseType (Single/EP/Album). Session/Mix rows
now read from a MediumTypeLabels dictionary so a future medium needs only
one new entry, no markup change.
Promote the Session/Mix single-track rule from a CMS-form convention to a
domain invariant: declare cardinality as data in MediumRules, enforce it in
UnifiedTrackService before the vault write (no orphan), return 409, and read
the same rule in the batch-form collapse.
Mirror BatchUpload.OnMediumChanged: switching to Session/Mix trims the
track list to one row and hides the add-track affordance, enforcing the
§9.3 single-track invariant on the edit path. Cut releases unchanged.
Session/Mix browsers share base (load/state/thumb) and a shared table shell carrying the per-row Edit link to BatchEdit; subclasses supply only their medium action.
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.
- Add CmsTrackBrowserViewModel.Invalidate(); called from TrackEdit/BatchEdit on save or delete so album/genre cache is invalidated and re-fetches on next mode switch
- CmsAlbumBrowser now handles 0-track releases: confirm dialog + DeleteReleaseAsync instead of early return; partial-failure path also fires OnReleasesChanged to trigger cache invalidation
- TrackList.OnAlbumsChanged now calls VM.Invalidate() so genres stay fresh after any album delete
- UnifiedTrackService.DeleteAsync cascades release soft-delete when last live track is removed (non-fatal; logs on failure)
- New DELETE api/track/release/{id} endpoint (ApiKeyAuthorize) for direct release soft-delete
- EF migration SoftDeleteOrphanedReleases backfills existing orphaned release rows via raw SQL (data-only, no schema change)
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.