Raise RequestSizeLimit/MultipartBodyLengthLimit on upload+replace-audio,
MaxUploadBytes in BatchUpload/BatchEdit, and DefaultResponseTimeoutSeconds to
1200s. Add client_max_body_size 2000m and proxy_read/send_timeout 1200s to the
nginx manager/public confs.
On partial failure the old path deleted the original audio before
confirming the new write succeeded. Now: load old extension, register
new audio first (original untouched on failure), then clean up stale
backing file only on success and only when extension changed.
Swap a track's audio by EntryKey (metadata/release/position preserved, waveform regenerated); hide per-track remove on a release's sole persisted track so it can only be replaced or release-deleted.
Retire the Tracks list view; promote the Releases view to /releases with
medium tabs (ALL/CUTS/SESSIONS/MIXES). Migrate bulk profile/high-res
backfill and per-track waveform columns into the releases grids. Point
catalogue cards at the three mediums. Remove dead BrowseMode/ViewModel.
Replace the 100s default HttpClient timeout (set Timeout=Infinite) with an idle/heartbeat
deadline driven by a ProgressStreamContent wrapper that reports bytes-on-the-wire. Each tick
resets the idle window and advances a MudProgressLinear per upload row. Idle window is
configurable via Upload:IdleTimeoutSeconds (default 90s).
New nullable Description column (max 4000) on ReleaseEntity, rides the Genre write channel through upload + edit; multiline CMS input. Migration authored, not applied.
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.
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.
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)