Design Prot Merge #1

Merged
NotS1mp merged 217 commits from design-prot into master 2026-09-21 15:48:16 +00:00
3 changed files with 48 additions and 6 deletions
Showing only changes of commit 1bd6f18c5e - Show all commits
@@ -8,7 +8,7 @@ using Musify.Domain.ValueObjects;
namespace Musify.Application.Services;
public sealed record YouTubeTrackProvision(Track Track, bool Created, YouTubeSongResult? Song);
public sealed record YouTubeTrackProvision(Track Track, YouTubeSongResult? Song);
public sealed class YouTubeTrackProvisioner(
IDatabase database,
@@ -26,7 +26,7 @@ public sealed class YouTubeTrackProvisioner(
{
var existing = await FindAsync(videoId, cancellationToken);
if (existing is not null)
return new YouTubeTrackProvision(existing, Created: false, Song: null);
return new YouTubeTrackProvision(existing, Song: null);
var songResult = await youTubeMusicService.GetSongAsync(videoId, cancellationToken);
if (songResult.IsError)
@@ -81,7 +81,7 @@ public sealed class YouTubeTrackProvisioner(
{
await database.SaveChangesAsync(cancellationToken);
logger.LogInformation("Created YouTube track {VideoId} ({TrackId})", videoId, track.Id);
return new YouTubeTrackProvision(track, Created: true, Song: song);
return new YouTubeTrackProvision(track, Song: song);
}
catch (DbUpdateException)
{
@@ -92,7 +92,7 @@ public sealed class YouTubeTrackProvisioner(
logger.LogWarning("YouTube track {VideoId} creation raced but could not be refetched", videoId);
return Error.Conflict(description: "The track was just created by another request. Retry the operation.");
}
return new YouTubeTrackProvision(refetched, Created: false, Song: null);
return new YouTubeTrackProvision(refetched, Song: null);
}
}
@@ -108,6 +108,30 @@ public sealed class YouTubeTrackProvisioner(
if (existing is not null)
return existing;
if (byExternalId)
{
var adoptable = await database.Artists
.SingleOrDefaultAsync(a => a.ExternalId == null && a.UserId == null && a.NormalizedName == normalized, cancellationToken);
if (adoptable is not null)
{
var previousName = adoptable.Name;
adoptable.ExternalId = reference.Id;
adoptable.Name = name;
try
{
await database.SaveChangesAsync(cancellationToken);
return adoptable;
}
catch (DbUpdateException)
{
adoptable.ExternalId = null;
adoptable.Name = previousName;
return await database.Artists.SingleAsync(a => a.ExternalId == reference.Id, cancellationToken);
}
}
}
var artist = new Artist
{
Name = name,
@@ -47,6 +47,14 @@ namespace Musify.Application.Users
user.SecondName = request.SecondName;
user.ProfilePictureUrl = request.ProfilePictureUrl;
var artist = await database.Artists
.SingleOrDefaultAsync(a => a.UserId == request.Id, cancellationToken);
if (artist is not null)
{
artist.Name = request.Name;
artist.NormalizedName = request.Name.ToUpperInvariant();
}
var written = await database.SaveChangesAsync(cancellationToken);
if (written > 0)
logger.LogInformation("Synced profile for user {UserId}", request.Id);
@@ -108,6 +108,11 @@ namespace Musify.Infrastructure.Persistence.Migrations
WHERE t."Source" = 0 AND ut."TrackId" = t."Id";
""");
migrationBuilder.Sql(
"""
DELETE FROM "Tracks" WHERE "Source" = 0 AND "OwnerUserId" IS NULL;
""");
migrationBuilder.Sql(
"""
INSERT INTO "Artists" ("Id", "Name", "NormalizedName", "UserId", "ExternalId", "CreatedAt", "UpdatedAt")
@@ -120,7 +125,12 @@ namespace Musify.Infrastructure.Persistence.Migrations
"""
INSERT INTO "Artists" ("Id", "Name", "NormalizedName", "UserId", "ExternalId", "CreatedAt", "UpdatedAt")
SELECT gen_random_uuid(), s."Artist", upper(s."Artist"), NULL, NULL, now(), now()
FROM (SELECT DISTINCT "Artist" FROM "Tracks" WHERE "Source" = 1 AND "Artist" IS NOT NULL) s;
FROM (
SELECT DISTINCT ON (upper("Artist")) "Artist"
FROM "Tracks"
WHERE "Source" = 1 AND "Artist" IS NOT NULL
ORDER BY upper("Artist"), "Artist"
) s;
""");
migrationBuilder.Sql(
@@ -135,7 +145,7 @@ namespace Musify.Infrastructure.Persistence.Migrations
"""
INSERT INTO "TrackArtist" ("TrackId", "ArtistId", "Position")
SELECT t."Id", a."Id", 0 FROM "Tracks" t
JOIN "Artists" a ON a."UserId" IS NULL AND a."ExternalId" IS NULL AND a."Name" = t."Artist"
JOIN "Artists" a ON a."UserId" IS NULL AND a."ExternalId" IS NULL AND a."NormalizedName" = upper(t."Artist")
WHERE t."Source" = 1 AND t."Artist" IS NOT NULL;
""");