feat: normalize release-cardinal fields out of track into a Release entity (Phase 8 §8.0)

This commit is contained in:
daniel-c-harvey
2026-06-11 12:51:21 -04:00
parent 16f356a760
commit f767d288c5
33 changed files with 1032 additions and 297 deletions
+35 -3
View File
@@ -65,11 +65,43 @@ public class UnifiedTrackService
return ResultContainer<TrackDto>.CreateFailResult("Failed to process and store WAV.");
}
unpersisted.CreatedByUserId = createdByUserId;
unpersisted.ReleaseType = releaseType;
unpersisted.TrackNumber = trackNumber;
var saveResult = await _sqlTrackService.Create(TrackConverter.Convert(unpersisted));
// Resolve the release FK before persisting the track. An upload with an album lands on the
// shared release (created on first sighting); an upload without one stays a loose track with
// a null ReleaseId. Release-cardinal metadata (artist/genre/releaseDate/type/uploader) rides
// on the release, not the track.
long? releaseId = null;
if (!string.IsNullOrWhiteSpace(album))
{
var releaseData = new ReleaseDto
{
Title = album,
Artist = artist,
Genre = genre,
ReleaseDate = releaseDate,
ReleaseType = releaseType,
CreatedByUserId = createdByUserId,
};
var releaseResult = await _sqlTrackService.FindOrCreateRelease(album, artist, releaseData, ct);
if (!releaseResult.Success || releaseResult.Value is null)
{
var error = releaseResult.Messages.FirstOrDefault()?.Message ?? "Unknown error";
_logger.LogError(
"Track persisted to vault but release resolution failed. Orphaned entry: {EntryKey}. Error: {Error}",
unpersisted.EntryKey, error);
return ResultContainer<TrackDto>.CreateFailResult($"Track was uploaded but could not be saved: {error}");
}
releaseId = releaseResult.Value.Id;
}
var trackDto = TrackConverter.Convert(unpersisted);
trackDto.ReleaseId = releaseId;
trackDto.Release = null; // FK already resolved; Create must not re-resolve a detached graph.
var saveResult = await _sqlTrackService.Create(trackDto);
if (!saveResult.Success || saveResult.Value is null)
{
// Vault write succeeded, SQL persist failed — audio is orphaned in the tracks vault