Changes
This commit is contained in:
@@ -34,30 +34,12 @@ namespace Musify.Application.Configuration
|
||||
[Required]
|
||||
public string LargePicturesFolder { get; set; } = "LargePictures";
|
||||
|
||||
[Required]
|
||||
public string PresetOriginalPicture { get; set; } = "PresetOriginalPicture.webp";
|
||||
|
||||
[Required]
|
||||
public string PresetSmallPicture { get; set; } = "PresetSmallPicture.webp";
|
||||
|
||||
[Required]
|
||||
public string PresetMediumPicture { get; set; } = "PresetMediumPicture.webp";
|
||||
|
||||
[Required]
|
||||
public string PresetLargePicture { get; set; } = "PresetLargePicture.webp";
|
||||
|
||||
public string SmallPicturesPath => StorageKey.Combine(ParentFolders, SmallPicturesFolder);
|
||||
|
||||
public string MediumPicturesPath => StorageKey.Combine(ParentFolders, MediumPicturesFolder);
|
||||
|
||||
public string LargePicturesPath => StorageKey.Combine(ParentFolders, LargePicturesFolder);
|
||||
|
||||
public string PresetSmallPicturePath => StorageKey.Combine(ParentFolders, PresetSmallPicture);
|
||||
|
||||
public string PresetMediumPicturePath => StorageKey.Combine(ParentFolders, PresetMediumPicture);
|
||||
|
||||
public string PresetLargePicturePath => StorageKey.Combine(ParentFolders, PresetLargePicture);
|
||||
|
||||
public string OriginalPicturesPath => StorageKey.Combine(ParentFolders, OriginalPicturesFolder);
|
||||
|
||||
public string BuildOriginalPicturePath(string pictureName) => StorageKey.Combine(ParentFolders, OriginalPicturesFolder, pictureName);
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace Musify.Application.Contracts
|
||||
|
||||
DbSet<Track> Tracks { get; }
|
||||
|
||||
DbSet<LocalTrack> LocalTracks { get; }
|
||||
|
||||
DbSet<ExternalTrack> ExternalTracks { get; }
|
||||
|
||||
DbSet<Artist> Artists { get; }
|
||||
|
||||
DbSet<TrackArtist> TrackArtists { get; }
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace Musify.Application.PlayLists
|
||||
}
|
||||
|
||||
UploadIntent? pictureIntent = null;
|
||||
string originalPictureName;
|
||||
|
||||
if (request.PictureIntentId.HasValue)
|
||||
{
|
||||
var validation = await uploadIntentValidator.ValidateAndLoadAsync(
|
||||
@@ -53,11 +51,6 @@ namespace Musify.Application.PlayLists
|
||||
return validation.Errors;
|
||||
|
||||
pictureIntent = validation.Value;
|
||||
originalPictureName = pictureIntent.ObjectName;
|
||||
}
|
||||
else
|
||||
{
|
||||
originalPictureName = playListConfiguration.Routes.PresetOriginalPicture;
|
||||
}
|
||||
|
||||
var playList = new PlayList
|
||||
@@ -66,20 +59,15 @@ namespace Musify.Application.PlayLists
|
||||
Name = request.Name,
|
||||
NormalizedName = request.Name.Trim().ToUpperInvariant(),
|
||||
Description = request.Description,
|
||||
Pictures = new PlayListPictures
|
||||
{
|
||||
OriginalName = originalPictureName,
|
||||
SmallName = playListConfiguration.Routes.PresetSmallPicture,
|
||||
MediumName = playListConfiguration.Routes.PresetMediumPicture,
|
||||
LargeName = playListConfiguration.Routes.PresetLargePicture
|
||||
}
|
||||
Pictures = pictureIntent == null ? null
|
||||
: new PlayListPictures { OriginalName = pictureIntent.ObjectName }
|
||||
};
|
||||
|
||||
await database.PlayLists.AddAsync(playList, cancellationToken);
|
||||
|
||||
if (pictureIntent is not null)
|
||||
if (pictureIntent != null)
|
||||
{
|
||||
var finalPictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(request.UserId, originalPictureName);
|
||||
var finalPictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(request.UserId, pictureIntent.ObjectName);
|
||||
var publishResult = await PublishCreatePlayListEventAsync(
|
||||
playList.Id, pictureIntent, finalPictureKey, cancellationToken);
|
||||
if (publishResult.IsError)
|
||||
|
||||
@@ -20,23 +20,23 @@ namespace Musify.Application.PlayLists
|
||||
{
|
||||
public async ValueTask<ErrorOr<PlayListCoverLocation>> Handle(GetPlayListCoverQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var playList = await database.PlayLists.AsNoTracking()
|
||||
var pictures = await database.PlayLists.AsNoTracking()
|
||||
.Where(p => p.Id == request.PlayListId)
|
||||
.Select(p => new { p.Pictures.SmallName, p.Pictures.MediumName, p.Pictures.LargeName })
|
||||
.Select(p => p.Pictures)
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (playList == null)
|
||||
if (pictures is null)
|
||||
return Error.NotFound();
|
||||
|
||||
var routes = playListConfiguration.Routes;
|
||||
var (name, key, presetName) = request.Size.ToLowerInvariant() switch
|
||||
var (name, key) = request.Size.ToLowerInvariant() switch
|
||||
{
|
||||
"small" => (playList.SmallName, BuildKey(routes.BuildSmallPicturePath, playList.SmallName), routes.PresetSmallPicture),
|
||||
"large" => (playList.LargeName, BuildKey(routes.BuildLargePicturePath, playList.LargeName), routes.PresetLargePicture),
|
||||
_ => (playList.MediumName, BuildKey(routes.BuildMediumPicturePath, playList.MediumName), routes.PresetMediumPicture)
|
||||
"small" => (pictures.SmallName, BuildKey(routes.BuildSmallPicturePath, pictures.SmallName)),
|
||||
"large" => (pictures.LargeName, BuildKey(routes.BuildLargePicturePath, pictures.LargeName)),
|
||||
_ => (pictures.MediumName, BuildKey(routes.BuildMediumPicturePath, pictures.MediumName))
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(key) || name == presetName)
|
||||
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(key))
|
||||
return Error.NotFound();
|
||||
|
||||
var contentType = MimeUtility.GetMimeMapping(name);
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Domain.Entities;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
|
||||
@@ -28,9 +29,9 @@ namespace Musify.Application.PlayLists
|
||||
|
||||
var tracksQuery = database.PlayListHasTracks
|
||||
.AsNoTracking()
|
||||
.Include(plt => plt.Track)
|
||||
.ThenInclude(track => track.TrackArtists)
|
||||
.Include(plt => ((ExternalTrack)plt.Track).TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.Include(plt => ((LocalTrack)plt.Track).Owner)
|
||||
.Where(plt => plt.PlayListId == request.PlayListId);
|
||||
|
||||
var totalCount = await tracksQuery.CountAsync(cancellationToken);
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace Musify.Application.PlayLists.Responses
|
||||
public record PlayListApplicationResponse(
|
||||
Guid Id,
|
||||
string Name,
|
||||
string Description,
|
||||
string SmallImageKeyName,
|
||||
string MediumImageKeyName,
|
||||
string LargeImageKeyName,
|
||||
string? Description,
|
||||
string? SmallImageKeyName,
|
||||
string? MediumImageKeyName,
|
||||
string? LargeImageKeyName,
|
||||
DateTime CreatedAt,
|
||||
DateTime UpdatedAt)
|
||||
{
|
||||
@@ -18,9 +18,9 @@ namespace Musify.Application.PlayLists.Responses
|
||||
playList.Id,
|
||||
playList.Name,
|
||||
playList.Description,
|
||||
playList.Pictures.SmallName,
|
||||
playList.Pictures.MediumName,
|
||||
playList.Pictures.LargeName,
|
||||
playList.Pictures?.SmallName,
|
||||
playList.Pictures?.MediumName,
|
||||
playList.Pictures?.LargeName,
|
||||
playList.CreatedAt,
|
||||
playList.UpdatedAt);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Musify.Application.Events;
|
||||
using Musify.Application.PlayLists.Responses;
|
||||
using Musify.Application.Services;
|
||||
using Musify.Domain.Entities;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
namespace Musify.Application.PlayLists
|
||||
@@ -68,7 +69,7 @@ namespace Musify.Application.PlayLists
|
||||
return validation.Errors;
|
||||
|
||||
pictureIntent = validation.Value;
|
||||
playListEntity.Pictures.OriginalName = pictureIntent.ObjectName;
|
||||
playListEntity.Pictures = new PlayListPictures { OriginalName = pictureIntent.ObjectName };
|
||||
finalPictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(request.UserId, pictureIntent.ObjectName);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ using Musify.Domain.ValueObjects;
|
||||
|
||||
namespace Musify.Application.Services;
|
||||
|
||||
public sealed record YouTubeTrackProvision(Track Track, YouTubeSongResult? Song);
|
||||
public sealed record YouTubeTrackProvision(ExternalTrack Track, YouTubeSongResult? Song);
|
||||
|
||||
public sealed class YouTubeTrackProvisioner(
|
||||
IDatabase database,
|
||||
@@ -16,8 +16,8 @@ public sealed class YouTubeTrackProvisioner(
|
||||
TrackConfiguration trackConfiguration,
|
||||
ILogger<YouTubeTrackProvisioner> logger)
|
||||
{
|
||||
public Task<Track?> FindAsync(string videoId, CancellationToken cancellationToken) =>
|
||||
database.Tracks
|
||||
public Task<ExternalTrack?> FindAsync(string videoId, CancellationToken cancellationToken) =>
|
||||
database.ExternalTracks
|
||||
.Include(track => track.TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.SingleOrDefaultAsync(t => t.Source == TrackSource.YouTube && t.ExternalId == videoId, cancellationToken);
|
||||
@@ -44,7 +44,7 @@ public sealed class YouTubeTrackProvisioner(
|
||||
artists.Add(await GetOrCreateArtistAsync(artistRef, cancellationToken));
|
||||
|
||||
var title = Truncate(song.Title, 50);
|
||||
var track = new Track
|
||||
var track = new ExternalTrack
|
||||
{
|
||||
Title = title,
|
||||
NormalizedTitle = title.ToUpperInvariant(),
|
||||
@@ -65,7 +65,7 @@ public sealed class YouTubeTrackProvisioner(
|
||||
}
|
||||
};
|
||||
|
||||
await database.Tracks.AddAsync(track, cancellationToken);
|
||||
await database.ExternalTracks.AddAsync(track, cancellationToken);
|
||||
|
||||
for (var position = 0; position < artists.Count; position++)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ public sealed class YouTubeTrackProvisioner(
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
database.Tracks.Remove(track);
|
||||
database.ExternalTracks.Remove(track);
|
||||
var refetched = await FindAsync(videoId, cancellationToken);
|
||||
if (refetched is null)
|
||||
{
|
||||
@@ -100,43 +100,17 @@ public sealed class YouTubeTrackProvisioner(
|
||||
{
|
||||
var name = Truncate(reference.Name, 200);
|
||||
var normalized = name.ToUpperInvariant();
|
||||
var byExternalId = !string.IsNullOrEmpty(reference.Id);
|
||||
var externalId = reference.Id ?? $"name:{normalized}";
|
||||
|
||||
var existing = byExternalId
|
||||
? await database.Artists.SingleOrDefaultAsync(a => a.ExternalId == reference.Id, cancellationToken)
|
||||
: await database.Artists.SingleOrDefaultAsync(a => a.ExternalId == null && a.UserId == null && a.NormalizedName == normalized, cancellationToken);
|
||||
var existing = await database.Artists.SingleOrDefaultAsync(a => a.ExternalId == externalId, cancellationToken);
|
||||
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,
|
||||
NormalizedName = normalized,
|
||||
ExternalId = byExternalId ? reference.Id : null
|
||||
ExternalId = externalId
|
||||
};
|
||||
|
||||
await database.Artists.AddAsync(artist, cancellationToken);
|
||||
@@ -149,9 +123,7 @@ public sealed class YouTubeTrackProvisioner(
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
database.Artists.Remove(artist);
|
||||
return byExternalId
|
||||
? await database.Artists.SingleAsync(a => a.ExternalId == reference.Id, cancellationToken)
|
||||
: await database.Artists.SingleAsync(a => a.ExternalId == null && a.UserId == null && a.NormalizedName == normalized, cancellationToken);
|
||||
return await database.Artists.SingleAsync(a => a.ExternalId == externalId, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Musify.Application.Tracks
|
||||
{
|
||||
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(CreateTrackCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await database.Users.AsNoTracking()
|
||||
var user = await database.Users
|
||||
.SingleOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||
if (user is null)
|
||||
{
|
||||
@@ -59,13 +59,12 @@ namespace Musify.Application.Tracks
|
||||
var finalAudioKey = trackConfiguration.Routes.BuildOriginalAudioPath(request.UserId, audioIntent.ObjectName);
|
||||
var audioProcessedFolderKey = trackConfiguration.Routes.BuildProcessedAudioPath(Guid.NewGuid().ToString());
|
||||
|
||||
var artist = await GetOrCreateUserArtistAsync(user, cancellationToken);
|
||||
|
||||
var trackEntity = new Track
|
||||
var trackEntity = new LocalTrack
|
||||
{
|
||||
Title = request.Title,
|
||||
NormalizedTitle = request.Title.ToUpperInvariant(),
|
||||
OwnerUserId = user.Id,
|
||||
Owner = user,
|
||||
Pictures = new TrackPictures
|
||||
{
|
||||
OriginalName = pictureIntent.ObjectName,
|
||||
@@ -81,14 +80,7 @@ namespace Musify.Application.Tracks
|
||||
}
|
||||
};
|
||||
|
||||
await database.Tracks.AddAsync(trackEntity, cancellationToken);
|
||||
|
||||
await database.TrackArtists.AddAsync(new TrackArtist
|
||||
{
|
||||
TrackId = trackEntity.Id,
|
||||
ArtistId = artist.Id,
|
||||
Position = 0
|
||||
}, cancellationToken);
|
||||
await database.LocalTracks.AddAsync(trackEntity, cancellationToken);
|
||||
|
||||
await database.UserHasTracks.AddAsync(new UserHasTrack
|
||||
{
|
||||
@@ -141,32 +133,5 @@ namespace Musify.Application.Tracks
|
||||
|
||||
return TrackApplicationResponse.FromEntity(trackEntity, listensCount: 0);
|
||||
}
|
||||
|
||||
private async Task<Artist> GetOrCreateUserArtistAsync(User user, CancellationToken cancellationToken)
|
||||
{
|
||||
var existing = await database.Artists.SingleOrDefaultAsync(a => a.UserId == user.Id, cancellationToken);
|
||||
if (existing is not null)
|
||||
return existing;
|
||||
|
||||
var artist = new Artist
|
||||
{
|
||||
Name = user.Name,
|
||||
NormalizedName = user.NormalizedName,
|
||||
UserId = user.Id
|
||||
};
|
||||
|
||||
await database.Artists.AddAsync(artist, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await database.SaveChangesAsync(cancellationToken);
|
||||
return artist;
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
database.Artists.Remove(artist);
|
||||
return await database.Artists.SingleAsync(a => a.UserId == user.Id, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using ErrorOr;
|
||||
using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Musify.Domain.Entities;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Application.Events;
|
||||
using Musify.Application.Contracts;
|
||||
@@ -28,7 +29,7 @@ namespace Musify.Application.Tracks
|
||||
return Error.NotFound();
|
||||
}
|
||||
|
||||
if (track.OwnerUserId != request.UserId)
|
||||
if (track is not LocalTrack localTrack || localTrack.OwnerUserId != request.UserId)
|
||||
{
|
||||
logger.LogWarning("User {UserId} unauthorized to delete track {TrackId}", request.UserId, request.TrackId);
|
||||
return Error.Unauthorized();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Domain.Entities;
|
||||
|
||||
namespace Musify.Application.Tracks
|
||||
{
|
||||
@@ -24,8 +25,9 @@ namespace Musify.Application.Tracks
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var tracksById = await database.Tracks
|
||||
.Include(t => t.TrackArtists)
|
||||
.Include(t => ((ExternalTrack)t).TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.Include(t => ((LocalTrack)t).Owner)
|
||||
.Where(t => recentTrackIds.Contains(t.Id))
|
||||
.Select(t => new { t.Id, Track = t, ListensCount = t.ListeningHistories.Count } )
|
||||
.ToDictionaryAsync(t => t.Id, cancellationToken);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Domain.Entities;
|
||||
|
||||
namespace Musify.Application.Tracks
|
||||
{
|
||||
@@ -15,8 +16,9 @@ namespace Musify.Application.Tracks
|
||||
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(GetTrackByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await database.Tracks.AsNoTracking()
|
||||
.Include(t => t.TrackArtists)
|
||||
.Include(t => ((ExternalTrack)t).TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.Include(t => ((LocalTrack)t).Owner)
|
||||
.Where(t => t.Id == request.TrackId)
|
||||
.Select(t => new { Track = t, ListensCount = t.ListeningHistories.Count })
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
@@ -6,6 +6,7 @@ using Musify.Application.Tracks.Responses;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Domain.Entities;
|
||||
|
||||
namespace Musify.Application.Tracks
|
||||
{
|
||||
@@ -19,9 +20,9 @@ namespace Musify.Application.Tracks
|
||||
{
|
||||
var tracksQuery = database.UserHasTracks
|
||||
.AsNoTracking()
|
||||
.Include(ut => ut.Track)
|
||||
.ThenInclude(track => track.TrackArtists)
|
||||
.Include(ut => ((ExternalTrack)ut.Track).TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.Include(ut => ((LocalTrack)ut.Track).Owner)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Name))
|
||||
|
||||
@@ -6,6 +6,7 @@ using Musify.Application.Tracks.Responses;
|
||||
using X.PagedList;
|
||||
using X.PagedList.EF;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Domain.Entities;
|
||||
|
||||
namespace Musify.Application.Tracks
|
||||
{
|
||||
@@ -26,9 +27,9 @@ namespace Musify.Application.Tracks
|
||||
|
||||
var tracksQuery = database.UserHasTracks
|
||||
.AsNoTracking()
|
||||
.Include(ut => ut.Track)
|
||||
.ThenInclude(track => track.TrackArtists)
|
||||
.Include(ut => ((ExternalTrack)ut.Track).TrackArtists)
|
||||
.ThenInclude(trackArtist => trackArtist.Artist)
|
||||
.Include(ut => ((LocalTrack)ut.Track).Owner)
|
||||
.Where(p => p.UserId == request.UserId);
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Name))
|
||||
|
||||
@@ -22,23 +22,23 @@ namespace Musify.Application.Tracks.Responses
|
||||
track.Id,
|
||||
track.Title,
|
||||
FormatArtist(track),
|
||||
track.Source,
|
||||
track.ExternalId,
|
||||
track is ExternalTrack external ? external.Source : TrackSource.Local,
|
||||
(track as ExternalTrack)?.ExternalId,
|
||||
track.Audio.TranscodeStatus,
|
||||
track.DurationSeconds,
|
||||
listensCount,
|
||||
track.OwnerUserId,
|
||||
(track as LocalTrack)?.OwnerUserId,
|
||||
track.CreatedAt,
|
||||
track.UpdatedAt);
|
||||
|
||||
private static string? FormatArtist(Track track)
|
||||
private static string? FormatArtist(Track track) => track switch
|
||||
{
|
||||
if (track.TrackArtists.Count == 0)
|
||||
return null;
|
||||
|
||||
return string.Join(", ", track.TrackArtists
|
||||
.OrderBy(trackArtist => trackArtist.Position)
|
||||
.Select(trackArtist => trackArtist.Artist.Name));
|
||||
}
|
||||
LocalTrack local => local.Owner.Name,
|
||||
ExternalTrack { TrackArtists.Count: > 0 } external =>
|
||||
string.Join(", ", external.TrackArtists
|
||||
.OrderBy(trackArtist => trackArtist.Position)
|
||||
.Select(trackArtist => trackArtist.Artist.Name)),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,6 @@ 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);
|
||||
|
||||
Reference in New Issue
Block a user