Change editor rule

This commit is contained in:
2026-09-24 13:24:43 +02:00
parent 16478678b5
commit 73e37dbecd
401 changed files with 14735 additions and 12995 deletions
@@ -4,27 +4,28 @@ using Microsoft.Extensions.Logging;
using Musify.Application.Contracts;
using Musify.Application.Mixes;
namespace Musify.Infrastructure.Jobs;
public class DailyMixGenerationJob(
IDatabase database,
IMediator mediator,
ILogger<DailyMixGenerationJob> logger)
namespace Musify.Infrastructure.Jobs
{
public async Task RunAsync(CancellationToken cancellationToken)
public class DailyMixGenerationJob(
IDatabase database,
IMediator mediator,
ILogger<DailyMixGenerationJob> logger)
{
var userIds = await database.ListeningHistories
.AsNoTracking()
.Where(history => history.IsCounted)
.Select(history => history.UserId)
.Distinct()
.ToListAsync(cancellationToken);
public async Task RunAsync(CancellationToken cancellationToken)
{
var userIds = await database.ListeningHistories
.AsNoTracking()
.Where(history => history.IsCounted)
.Select(history => history.UserId)
.Distinct()
.ToListAsync(cancellationToken);
logger.LogInformation("Starting daily mix generation for {Count} users", userIds.Count);
logger.LogInformation("Starting daily mix generation for {Count} users", userIds.Count);
foreach (var userId in userIds)
await mediator.Send(new GenerateMixesForUserCommand(userId), cancellationToken);
foreach (var userId in userIds)
await mediator.Send(new GenerateMixesForUserCommand(userId), cancellationToken);
logger.LogInformation("Finished daily mix generation");
logger.LogInformation("Finished daily mix generation");
}
}
}
@@ -1,12 +1,13 @@
using Mediator;
using Musify.Application.Tracks;
namespace Musify.Infrastructure.Jobs;
public class ListeningHistoryCleanupJob(IMediator mediator)
namespace Musify.Infrastructure.Jobs
{
public async Task RunAsync(CancellationToken cancellationToken)
public class ListeningHistoryCleanupJob(IMediator mediator)
{
await mediator.Send(new DeleteStaleUncountedListensCommand(), cancellationToken);
public async Task RunAsync(CancellationToken cancellationToken)
{
await mediator.Send(new DeleteStaleUncountedListensCommand(), cancellationToken);
}
}
}
@@ -2,40 +2,41 @@ using Hangfire;
using Microsoft.Extensions.Hosting;
using Musify.Application.Configuration;
namespace Musify.Infrastructure.Jobs;
/// <summary>Registers every recurring Hangfire job when the host starts.</summary>
public sealed class RecurringJobsRegistrar(
IRecurringJobManager recurringJobManager,
UploadIntentConfiguration uploadIntentConfiguration)
: IHostedService
namespace Musify.Infrastructure.Jobs
{
public Task StartAsync(CancellationToken cancellationToken)
/// <summary>Registers every recurring Hangfire job when the host starts.</summary>
public sealed class RecurringJobsRegistrar(
IRecurringJobManager recurringJobManager,
UploadIntentConfiguration uploadIntentConfiguration)
: IHostedService
{
var options = new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc };
public Task StartAsync(CancellationToken cancellationToken)
{
var options = new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc };
recurringJobManager.AddOrUpdate<DailyMixGenerationJob>(
"daily-mix-generation", job => job.RunAsync(CancellationToken.None), Cron.Daily(), options);
recurringJobManager.AddOrUpdate<ListeningHistoryCleanupJob>(
"listening-history-cleanup", job => job.RunAsync(CancellationToken.None), Cron.Daily(3), options);
recurringJobManager.AddOrUpdate<UploadIntentExpirationJob>(
"upload-intent-expiration", job => job.RunAsync(CancellationToken.None),
EveryCron(uploadIntentConfiguration.ExpirationJobIntervalSeconds), options);
recurringJobManager.AddOrUpdate<TempUploadsCleanupJob>(
"temp-uploads-cleanup", job => job.RunAsync(CancellationToken.None),
EveryCron(uploadIntentConfiguration.TempCleanupJobIntervalSeconds), options);
recurringJobManager.AddOrUpdate<DailyMixGenerationJob>(
"daily-mix-generation", job => job.RunAsync(CancellationToken.None), Cron.Daily(), options);
recurringJobManager.AddOrUpdate<ListeningHistoryCleanupJob>(
"listening-history-cleanup", job => job.RunAsync(CancellationToken.None), Cron.Daily(3), options);
recurringJobManager.AddOrUpdate<UploadIntentExpirationJob>(
"upload-intent-expiration", job => job.RunAsync(CancellationToken.None),
EveryCron(uploadIntentConfiguration.ExpirationJobIntervalSeconds), options);
recurringJobManager.AddOrUpdate<TempUploadsCleanupJob>(
"temp-uploads-cleanup", job => job.RunAsync(CancellationToken.None),
EveryCron(uploadIntentConfiguration.TempCleanupJobIntervalSeconds), options);
return Task.CompletedTask;
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <summary>Hangfire cron has a one-minute resolution, so the interval is rounded up to whole minutes.</summary>
internal static string EveryCron(int intervalSeconds)
{
var minutes = Math.Max(1, (int)Math.Ceiling(intervalSeconds / 60.0));
return minutes < 60
? $"*/{minutes} * * * *"
: $"0 */{Math.Max(1, minutes / 60)} * * *";
/// <summary>Hangfire cron has a one-minute resolution, so the interval is rounded up to whole minutes.</summary>
internal static string EveryCron(int intervalSeconds)
{
var minutes = Math.Max(1, (int)Math.Ceiling(intervalSeconds / 60.0));
return minutes < 60
? $"*/{minutes} * * * *"
: $"0 */{Math.Max(1, minutes / 60)} * * *";
}
}
}
@@ -1,10 +1,11 @@
using Mediator;
using Musify.Application.UploadIntents;
namespace Musify.Infrastructure.Jobs;
public class TempUploadsCleanupJob(IMediator mediator)
namespace Musify.Infrastructure.Jobs
{
public async Task RunAsync(CancellationToken cancellationToken) =>
await mediator.Send(new DeleteStaleTempUploadsCommand(), cancellationToken);
public class TempUploadsCleanupJob(IMediator mediator)
{
public async Task RunAsync(CancellationToken cancellationToken) =>
await mediator.Send(new DeleteStaleTempUploadsCommand(), cancellationToken);
}
}
@@ -1,10 +1,11 @@
using Mediator;
using Musify.Application.UploadIntents;
namespace Musify.Infrastructure.Jobs;
public class UploadIntentExpirationJob(IMediator mediator)
namespace Musify.Infrastructure.Jobs
{
public async Task RunAsync(CancellationToken cancellationToken) =>
await mediator.Send(new ExpireUploadIntentsCommand(), cancellationToken);
public class UploadIntentExpirationJob(IMediator mediator)
{
public async Task RunAsync(CancellationToken cancellationToken) =>
await mediator.Send(new ExpireUploadIntentsCommand(), cancellationToken);
}
}