Duration as double

This commit is contained in:
2026-07-24 19:01:19 +02:00
parent 2385ec38e8
commit f633a0c5c8
10 changed files with 1224 additions and 20 deletions
@@ -22,7 +22,7 @@ namespace Musify.Application.Mixes
{ {
private sealed record SeedArtist(Guid Id, string Name); private sealed record SeedArtist(Guid Id, string Name);
private sealed record LocalCandidate(Guid TrackId, string Title, string? Artist, int DurationSeconds); private sealed record LocalCandidate(Guid TrackId, string Title, string? Artist, double DurationSeconds);
private sealed record MixDraft(string Title, string? Subtitle, IReadOnlyList<MixItem> Items); private sealed record MixDraft(string Title, string? Subtitle, IReadOnlyList<MixItem> Items);
@@ -99,7 +99,7 @@ namespace Musify.Application.Mixes
.AsNoTracking() .AsNoTracking()
.Where(trackArtist => historyTrackIds.Contains(trackArtist.TrackId)) .Where(trackArtist => historyTrackIds.Contains(trackArtist.TrackId))
.GroupBy(trackArtist => trackArtist.ArtistId) .GroupBy(trackArtist => trackArtist.ArtistId)
.Select(group => new { ArtistId = group.Key, Listens = group.Count() }) .Select(group => new { ArtistId = group.Key, Listens = group.Count() } )
.OrderByDescending(entry => entry.Listens) .OrderByDescending(entry => entry.Listens)
.Take(mixConfiguration.SeedArtistCount) .Take(mixConfiguration.SeedArtistCount)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -113,10 +113,10 @@ namespace Musify.Application.Mixes
return [.. ranked return [.. ranked
.Where(entry => names.ContainsKey(entry.ArtistId)) .Where(entry => names.ContainsKey(entry.ArtistId))
.Select(entry => new SeedArtist(entry.ArtistId, names[entry.ArtistId]))]; .Select(entry => new SeedArtist(entry.ArtistId, names[entry.ArtistId])) ];
} }
private async Task<Dictionary<Guid, List<MixItem>>> SearchYouTubeAsync( private async Task<Dictionary<Guid, List<MixItem>> > SearchYouTubeAsync(
IReadOnlyList<SeedArtist> seedArtists, IReadOnlyList<SeedArtist> seedArtists,
HashSet<string> excludedVideoIds, HashSet<string> excludedVideoIds,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@@ -150,7 +150,7 @@ namespace Musify.Application.Mixes
ThumbnailUrl = Truncate(youTubeMusicService.ResolveArtworkUrl(song.ThumbnailUrl), ThumbnailUrlLength), ThumbnailUrl = Truncate(youTubeMusicService.ResolveArtworkUrl(song.ThumbnailUrl), ThumbnailUrlLength),
DurationSeconds = song.DurationSeconds, DurationSeconds = song.DurationSeconds,
IsExplicit = song.IsExplicit IsExplicit = song.IsExplicit
}); } );
} }
byArtist[seed.Id] = songs; byArtist[seed.Id] = songs;
@@ -159,7 +159,7 @@ namespace Musify.Application.Mixes
return byArtist; return byArtist;
} }
private async Task<Dictionary<Guid, List<MixItem>>> GetLocalCandidatesAsync( private async Task<Dictionary<Guid, List<MixItem>> > GetLocalCandidatesAsync(
IReadOnlyList<SeedArtist> seedArtists, IReadOnlyList<SeedArtist> seedArtists,
IReadOnlyList<Guid> historyTrackIds, IReadOnlyList<Guid> historyTrackIds,
CancellationToken cancellationToken) CancellationToken cancellationToken)
@@ -169,7 +169,7 @@ namespace Musify.Application.Mixes
var links = await database.TrackArtists var links = await database.TrackArtists
.AsNoTracking() .AsNoTracking()
.Where(trackArtist => artistIds.Contains(trackArtist.ArtistId) && !historyTrackIds.Contains(trackArtist.TrackId)) .Where(trackArtist => artistIds.Contains(trackArtist.ArtistId) && !historyTrackIds.Contains(trackArtist.TrackId))
.Select(trackArtist => new { trackArtist.ArtistId, trackArtist.TrackId }) .Select(trackArtist => new { trackArtist.ArtistId, trackArtist.TrackId } )
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
var trackIds = links.Select(link => link.TrackId).Distinct().ToList(); var trackIds = links.Select(link => link.TrackId).Distinct().ToList();
@@ -279,9 +279,9 @@ namespace Musify.Application.Mixes
for (var index = 0; index < Math.Max(first.Count, second.Count); index++) for (var index = 0; index < Math.Max(first.Count, second.Count); index++)
{ {
if (index < first.Count) if (index < first.Count)
merged.Add(first[index]); merged.Add(first[index] );
if (index < second.Count) if (index < second.Count)
merged.Add(second[index]); merged.Add(second[index] );
} }
return merged; return merged;
@@ -10,7 +10,7 @@ namespace Musify.Application.Mixes.Responses
string Title, string Title,
string? Artist, string? Artist,
string? ThumbnailUrl, string? ThumbnailUrl,
int DurationSeconds, double DurationSeconds,
bool IsExplicit) bool IsExplicit)
{ {
public static MixItemApplicationResponse FromEntity(MixItem item) => public static MixItemApplicationResponse FromEntity(MixItem item) =>
@@ -11,7 +11,7 @@ namespace Musify.Application.Tracks.Responses
TrackSource Source, TrackSource Source,
string? ExternalId, string? ExternalId,
ProcessingStatus AudioStatus, ProcessingStatus AudioStatus,
int Duration, double Duration,
int ListensCount, int ListensCount,
long? OwnerUserId, long? OwnerUserId,
DateTime CreatedAt, DateTime CreatedAt,
+2 -2
View File
@@ -36,10 +36,10 @@ namespace Musify.Domain.Entities
public string? ThumbnailUrl { get; set; } public string? ThumbnailUrl { get; set; }
[Required] [Required]
public int DurationSeconds { get; set; } = 0; public double DurationSeconds { get; set; }
[Required] [Required]
public bool IsExplicit { get; set; } = false; public bool IsExplicit { get; set; }
[ForeignKey(nameof(MixId))] [ForeignKey(nameof(MixId))]
public Mix Mix { get; set; } public Mix Mix { get; set; }
+1 -1
View File
@@ -19,7 +19,7 @@ namespace Musify.Domain.Entities
public required string NormalizedTitle { get; set; } public required string NormalizedTitle { get; set; }
[Required] [Required]
public int DurationSeconds { get; set; } public double DurationSeconds { get; set; }
[Required] [Required]
public LifeCycleStatus LifeCycleStatus { get; set; } = LifeCycleStatus.Active; public LifeCycleStatus LifeCycleStatus { get; set; } = LifeCycleStatus.Active;
@@ -47,7 +47,7 @@ namespace Musify.Infrastructure.MassTransit.Activities.Audio
try try
{ {
var durationSeconds = executeContext.GetVariable<int>(executeContext.Arguments.DurationSecondsVariable) var durationSeconds = executeContext.GetVariable<double>(executeContext.Arguments.DurationSecondsVariable)
?? throw new InvalidOperationException($"Missing routing slip variable {executeContext.Arguments.DurationSecondsVariable}"); ?? throw new InvalidOperationException($"Missing routing slip variable {executeContext.Arguments.DurationSecondsVariable}");
var log = new UpdateTrackAudioLog( var log = new UpdateTrackAudioLog(
@@ -3,5 +3,5 @@
public record UpdateTrackAudioLog( public record UpdateTrackAudioLog(
Guid TrackId, Guid TrackId,
string? PreviousAudioFolderName, string? PreviousAudioFolderName,
int PreviousDurationSeconds); double PreviousDurationSeconds);
} }
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Musify.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class DurationAsDouble : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -360,8 +360,8 @@ namespace Musify.Infrastructure.Persistence.Migrations
.HasMaxLength(300) .HasMaxLength(300)
.HasColumnType("character varying(300)"); .HasColumnType("character varying(300)");
b.Property<int>("DurationSeconds") b.Property<double>("DurationSeconds")
.HasColumnType("integer"); .HasColumnType("double precision");
b.Property<bool>("IsExplicit") b.Property<bool>("IsExplicit")
.HasColumnType("boolean"); .HasColumnType("boolean");
@@ -474,8 +474,8 @@ namespace Musify.Infrastructure.Persistence.Migrations
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.Property<int>("DurationSeconds") b.Property<double>("DurationSeconds")
.HasColumnType("integer"); .HasColumnType("double precision");
b.Property<int>("LifeCycleStatus") b.Property<int>("LifeCycleStatus")
.HasColumnType("integer"); .HasColumnType("integer");
@@ -893,21 +893,25 @@ namespace Musify.Infrastructure.Persistence.Migrations
.HasColumnType("uuid"); .HasColumnType("uuid");
b1.Property<string>("LargeName") b1.Property<string>("LargeName")
.IsRequired()
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
.HasColumnName("LargePictureName"); .HasColumnName("LargePictureName");
b1.Property<string>("MediumName") b1.Property<string>("MediumName")
.IsRequired()
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
.HasColumnName("MediumPictureName"); .HasColumnName("MediumPictureName");
b1.Property<string>("OriginalName") b1.Property<string>("OriginalName")
.IsRequired()
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
.HasColumnName("OriginalPictureName"); .HasColumnName("OriginalPictureName");
b1.Property<string>("SmallName") b1.Property<string>("SmallName")
.IsRequired()
.HasMaxLength(64) .HasMaxLength(64)
.HasColumnType("character varying(64)") .HasColumnType("character varying(64)")
.HasColumnName("SmallPictureName"); .HasColumnName("SmallPictureName");