Block duplicate-release uploads by (title, artist): pre-flight check + server 409 backstop, with within-batch Cut attach via releaseId
This commit is contained in:
@@ -298,6 +298,29 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-flight duplicate guard (primary block): the upload form creates new releases only, so a
|
||||
// (title, artist) that already exists in the catalogue is refused BEFORE any bytes transfer —
|
||||
// the admin is not surprised at the end of a long upload. The server backstops this on the
|
||||
// create path, but checking here keeps the failure fast and visible. The values passed match
|
||||
// exactly what the upload sends (untrimmed _albumName/_artist) so the pre-flight and the server
|
||||
// agree on the match. A check failure (API unreachable) blocks rather than proceeding blind.
|
||||
var duplicateCheck = await CmsTrackService.GetExistingReleaseAsync(_albumName, _artist);
|
||||
if (!duplicateCheck.Success)
|
||||
{
|
||||
var checkError = duplicateCheck.Messages.FirstOrDefault()?.Message ?? "Unknown error";
|
||||
_errorMessage = $"Could not verify the release name: {checkError}";
|
||||
Snackbar.Add(_errorMessage, Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (duplicateCheck.Value is { } existing)
|
||||
{
|
||||
_errorMessage = $"A release titled '{existing.Title}' by {existing.Artist} already exists. "
|
||||
+ "The upload form creates new releases only — use the edit tools to change an existing one.";
|
||||
Snackbar.Add(_errorMessage, Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// For single-track media (Session/Mix) the track name is derived from the Release Name —
|
||||
// no separate Track Name input is shown. Sync here so the stored name always matches.
|
||||
if (MediumRules.CardinalityOf(_medium).IsSingleTrack && _tracks.Count > 0)
|
||||
@@ -327,6 +350,11 @@
|
||||
}
|
||||
|
||||
int succeeded = 0, failed = 0;
|
||||
// Within-batch attach: row 1 creates the release (no releaseId → CREATE path); once it
|
||||
// succeeds we carry its ReleaseId into rows 2..N so they ATTACH to the just-created release
|
||||
// rather than tripping the server's pre-existing-duplicate block. Only a multi-track Cut
|
||||
// reaches row 2 (single-track media collapse to one row).
|
||||
long? batchReleaseId = null;
|
||||
for (int i = 0; i < _tracks.Count; i++)
|
||||
{
|
||||
var row = _tracks[i];
|
||||
@@ -375,6 +403,7 @@
|
||||
_releaseType,
|
||||
trackNumber,
|
||||
_medium,
|
||||
batchReleaseId,
|
||||
progress);
|
||||
|
||||
if (!result.Success || result.Value is null)
|
||||
@@ -387,6 +416,15 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
// Capture the release id created by the first successful row so subsequent rows
|
||||
// attach to it (the within-batch multi-track Cut path). Only set once — later
|
||||
// rows must not overwrite it. A null ReleaseId here (loose track) leaves it null,
|
||||
// which is correct: a release-less upload has no within-batch release to attach to.
|
||||
if (batchReleaseId is null && result.Value.ReleaseId is { } createdReleaseId)
|
||||
{
|
||||
batchReleaseId = createdReleaseId;
|
||||
}
|
||||
|
||||
// The upload endpoint does not accept an imagePath, so link the cover art with
|
||||
// a follow-up metadata update — same two-step pattern BatchEdit uses.
|
||||
if (_imagePath is { } imgPath)
|
||||
@@ -487,7 +525,13 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Uploaded {succeeded} track(s); {failed} failed — review errors below.", Severity.Warning);
|
||||
// Surface the actual reason, not just counts — a server rejection (duplicate, cardinality)
|
||||
// relays a human-readable message via row.ErrorMessage. Show the first failure's reason so
|
||||
// the admin sees WHY without scanning the rows; the per-row errors remain as detail.
|
||||
var firstError = _tracks.FirstOrDefault(t => t.Status == BatchRowStatus.Failed)?.ErrorMessage;
|
||||
var reason = string.IsNullOrWhiteSpace(firstError) ? "review errors below" : firstError;
|
||||
_errorMessage = succeeded == 0 ? reason : $"{succeeded} uploaded; {failed} failed: {reason}";
|
||||
Snackbar.Add($"Uploaded {succeeded} track(s); {failed} failed — {reason}", Severity.Warning);
|
||||
// Stay on page so the admin can see the failed rows.
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user