parent
18afeb131b
commit
bf55f7222e
@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging;
|
|||||||
using NLog.Extensions.Logging;
|
using NLog.Extensions.Logging;
|
||||||
using Selector.Cache.Extensions;
|
using Selector.Cache.Extensions;
|
||||||
using Selector.CLI.Extensions;
|
using Selector.CLI.Extensions;
|
||||||
using Selector.CLI.Services;
|
|
||||||
using Selector.Events;
|
using Selector.Events;
|
||||||
using Selector.Extensions;
|
using Selector.Extensions;
|
||||||
using System;
|
using System;
|
||||||
@ -89,7 +88,8 @@ namespace Selector.CLI
|
|||||||
.AddSpotify();
|
.AddSpotify();
|
||||||
|
|
||||||
services.ConfigureLastFm(config)
|
services.ConfigureLastFm(config)
|
||||||
.ConfigureEqual(config);
|
.ConfigureEqual(config)
|
||||||
|
.ConfigureJobs(config);
|
||||||
|
|
||||||
if (config.RedisOptions.Enabled)
|
if (config.RedisOptions.Enabled)
|
||||||
{
|
{
|
||||||
@ -119,13 +119,6 @@ namespace Selector.CLI
|
|||||||
services.AddHostedService<DbWatcherService>();
|
services.AddHostedService<DbWatcherService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.ScrobbleOptions.Enabled)
|
|
||||||
{
|
|
||||||
Console.WriteLine("> Adding Scrobble Monitor Service");
|
|
||||||
|
|
||||||
services.AddHostedService<ScrobbleMonitor>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureDefaultNlog(HostBuilderContext context, ILoggingBuilder builder)
|
public static void ConfigureDefaultNlog(HostBuilderContext context, ILoggingBuilder builder)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Quartz;
|
||||||
using Selector.Cache.Extensions;
|
using Selector.Cache.Extensions;
|
||||||
|
using Selector.CLI.Jobs;
|
||||||
using Selector.Extensions;
|
using Selector.Extensions;
|
||||||
using Selector.Model;
|
using Selector.Model;
|
||||||
using Selector.Model.Services;
|
using Selector.Model.Services;
|
||||||
@ -36,6 +38,51 @@ namespace Selector.CLI.Extensions
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection ConfigureJobs(this IServiceCollection services, RootOptions config)
|
||||||
|
{
|
||||||
|
if (config.JobOptions.Enabled)
|
||||||
|
{
|
||||||
|
Console.WriteLine("> Adding Jobs...");
|
||||||
|
|
||||||
|
services.AddQuartz(options => {
|
||||||
|
|
||||||
|
options.UseMicrosoftDependencyInjectionJobFactory();
|
||||||
|
|
||||||
|
options.UseSimpleTypeLoader();
|
||||||
|
options.UseInMemoryStore();
|
||||||
|
options.UseDefaultThreadPool(tp =>
|
||||||
|
{
|
||||||
|
tp.MaxConcurrency = 5;
|
||||||
|
});
|
||||||
|
|
||||||
|
var scrobbleKey = new JobKey("scrobble-watcher", "scrobble");
|
||||||
|
|
||||||
|
options.AddJob<ScrobbleWatcherJob>(j => j
|
||||||
|
.WithDescription("Watch recent scrobbles and mirror to database")
|
||||||
|
.WithIdentity(scrobbleKey)
|
||||||
|
);
|
||||||
|
|
||||||
|
options.AddTrigger(t => t
|
||||||
|
.WithIdentity("scrobble-watcher-trigger")
|
||||||
|
.ForJob(scrobbleKey)
|
||||||
|
.StartNow()
|
||||||
|
.WithSimpleSchedule(x => x.WithInterval(config.JobOptions.Scrobble.InterJobDelay).RepeatForever())
|
||||||
|
.WithDescription("Periodic trigger for scrobble watcher")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddQuartzHostedService(options =>{
|
||||||
|
|
||||||
|
options.WaitForJobsToComplete = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddTransient<ScrobbleWatcherJob>();
|
||||||
|
services.AddTransient<IJob, ScrobbleWatcherJob>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection ConfigureDb(this IServiceCollection services, RootOptions config)
|
public static IServiceCollection ConfigureDb(this IServiceCollection services, RootOptions config)
|
||||||
{
|
{
|
||||||
if (config.DatabaseOptions.Enabled)
|
if (config.DatabaseOptions.Enabled)
|
||||||
|
88
Selector.CLI/Jobs/ScrobbleWatcherJob.cs
Normal file
88
Selector.CLI/Jobs/ScrobbleWatcherJob.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
using IF.Lastfm.Core.Api;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Quartz;
|
||||||
|
using Selector.Model;
|
||||||
|
using Selector.Model.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Selector.CLI.Jobs
|
||||||
|
{
|
||||||
|
public class ScrobbleWatcherJob : IJob
|
||||||
|
{
|
||||||
|
private readonly ILogger<ScrobbleWatcherJob> logger;
|
||||||
|
private readonly ILoggerFactory loggerFactory;
|
||||||
|
|
||||||
|
private readonly IUserApi userApi;
|
||||||
|
private readonly IScrobbleRepository scrobbleRepo;
|
||||||
|
private readonly ApplicationDbContext db;
|
||||||
|
private readonly ScrobbleWatcherJobOptions options;
|
||||||
|
|
||||||
|
public ScrobbleWatcherJob(
|
||||||
|
IUserApi _userApi,
|
||||||
|
IScrobbleRepository _scrobbleRepo,
|
||||||
|
ApplicationDbContext _db,
|
||||||
|
IOptions<ScrobbleWatcherJobOptions> _options,
|
||||||
|
ILogger<ScrobbleWatcherJob> _logger,
|
||||||
|
ILoggerFactory _loggerFactory)
|
||||||
|
{
|
||||||
|
logger = _logger;
|
||||||
|
loggerFactory = _loggerFactory;
|
||||||
|
|
||||||
|
userApi = _userApi;
|
||||||
|
scrobbleRepo = _scrobbleRepo;
|
||||||
|
db = _db;
|
||||||
|
options = _options.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Starting scrobble watching job");
|
||||||
|
|
||||||
|
var users = db.Users
|
||||||
|
.AsEnumerable()
|
||||||
|
.Where(u => u.ScrobbleSavingEnabled())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Saving scrobbles for {}/{}", user.UserName, user.LastFmUsername);
|
||||||
|
|
||||||
|
if (options.From is not null && options.From.Value.Kind != DateTimeKind.Utc)
|
||||||
|
{
|
||||||
|
options.From = options.From.Value.ToUniversalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.To is not null && options.To.Value.Kind != DateTimeKind.Utc)
|
||||||
|
{
|
||||||
|
options.To = options.To.Value.ToUniversalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
var saver = new ScrobbleSaver(
|
||||||
|
userApi,
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
User = user,
|
||||||
|
InterRequestDelay = options.InterRequestDelay,
|
||||||
|
From = options.From,
|
||||||
|
To = options.To,
|
||||||
|
PageSize = options.PageSize,
|
||||||
|
DontAdd = false,
|
||||||
|
DontRemove = false,
|
||||||
|
SimultaneousConnections = options.Simultaneous
|
||||||
|
},
|
||||||
|
scrobbleRepo,
|
||||||
|
loggerFactory.CreateLogger<ScrobbleSaver>(),
|
||||||
|
loggerFactory);
|
||||||
|
|
||||||
|
await saver.Execute(context.CancellationToken);
|
||||||
|
|
||||||
|
logger.LogInformation("Finished scrobbles for {}/{}", user.UserName, user.LastFmUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,8 @@ namespace Selector.CLI
|
|||||||
config.GetSection(FormatKeys( new[] { RootOptions.Key, WatcherOptions.Key})).Bind(options.WatcherOptions);
|
config.GetSection(FormatKeys( new[] { RootOptions.Key, WatcherOptions.Key})).Bind(options.WatcherOptions);
|
||||||
config.GetSection(FormatKeys( new[] { RootOptions.Key, DatabaseOptions.Key})).Bind(options.DatabaseOptions);
|
config.GetSection(FormatKeys( new[] { RootOptions.Key, DatabaseOptions.Key})).Bind(options.DatabaseOptions);
|
||||||
config.GetSection(FormatKeys( new[] { RootOptions.Key, RedisOptions.Key})).Bind(options.RedisOptions);
|
config.GetSection(FormatKeys( new[] { RootOptions.Key, RedisOptions.Key})).Bind(options.RedisOptions);
|
||||||
config.GetSection(FormatKeys( new[] { RootOptions.Key, ScrobbleMonitorOptions.Key})).Bind(options.ScrobbleOptions);
|
config.GetSection(FormatKeys( new[] { RootOptions.Key, JobsOptions.Key})).Bind(options.JobOptions);
|
||||||
|
config.GetSection(FormatKeys( new[] { RootOptions.Key, JobsOptions.Key, ScrobbleWatcherJobOptions.Key})).Bind(options.JobOptions.Scrobble);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RootOptions ConfigureOptions(this IConfiguration config)
|
public static RootOptions ConfigureOptions(this IConfiguration config)
|
||||||
@ -43,7 +44,7 @@ namespace Selector.CLI
|
|||||||
public string LastfmClient { get; set; }
|
public string LastfmClient { get; set; }
|
||||||
public string LastfmSecret { get; set; }
|
public string LastfmSecret { get; set; }
|
||||||
public WatcherOptions WatcherOptions { get; set; } = new();
|
public WatcherOptions WatcherOptions { get; set; } = new();
|
||||||
public ScrobbleMonitorOptions ScrobbleOptions { get; set; } = new();
|
public JobsOptions JobOptions { get; set; } = new();
|
||||||
public DatabaseOptions DatabaseOptions { get; set; } = new();
|
public DatabaseOptions DatabaseOptions { get; set; } = new();
|
||||||
public RedisOptions RedisOptions { get; set; } = new();
|
public RedisOptions RedisOptions { get; set; } = new();
|
||||||
public EqualityChecker Equality { get; set; } = EqualityChecker.Uri;
|
public EqualityChecker Equality { get; set; } = EqualityChecker.Uri;
|
||||||
@ -93,11 +94,23 @@ namespace Selector.CLI
|
|||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ScrobbleMonitorOptions
|
public class JobsOptions
|
||||||
|
{
|
||||||
|
public const string Key = "Job";
|
||||||
|
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
public ScrobbleWatcherJobOptions Scrobble { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScrobbleWatcherJobOptions
|
||||||
{
|
{
|
||||||
public const string Key = "Scrobble";
|
public const string Key = "Scrobble";
|
||||||
|
|
||||||
public bool Enabled { get; set; } = true;
|
public TimeSpan InterJobDelay { get; set; } = TimeSpan.FromMinutes(5);
|
||||||
public TimeSpan InterRequestDelay { get; set; } = new(0, 0, 0, 1, 0);
|
public TimeSpan InterRequestDelay { get; set; } = TimeSpan.FromMilliseconds(100);
|
||||||
|
public DateTime? From { get; set; } = DateTime.UtcNow.AddDays(-14);
|
||||||
|
public DateTime? To { get; set; }
|
||||||
|
public int PageSize { get; set; } = 200;
|
||||||
|
public int Simultaneous { get; set; } = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
||||||
<PackageReference Include="NLog" Version="4.7.13" />
|
<PackageReference Include="NLog" Version="4.7.13" />
|
||||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||||
|
<PackageReference Include="Quartz" Version="3.3.3" />
|
||||||
|
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.3.3" />
|
||||||
<PackageReference Include="SpotifyAPI.Web" Version="6.2.2" />
|
<PackageReference Include="SpotifyAPI.Web" Version="6.2.2" />
|
||||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21308.1" />
|
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21308.1" />
|
||||||
<PackageReference Include="System.CommandLine.Hosting" Version="0.3.0-alpha.21216.1" />
|
<PackageReference Include="System.CommandLine.Hosting" Version="0.3.0-alpha.21216.1" />
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
using IF.Lastfm.Core.Api;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Selector.Model;
|
|
||||||
using Selector.Model.Extensions;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Selector.CLI.Services
|
|
||||||
{
|
|
||||||
public class ScrobbleMonitor : IHostedService
|
|
||||||
{
|
|
||||||
private readonly ILogger<ScrobbleMonitor> logger;
|
|
||||||
private readonly ILoggerFactory loggerFactory;
|
|
||||||
private readonly ScrobbleMonitorOptions config;
|
|
||||||
private readonly IUserApi userApi;
|
|
||||||
private readonly IServiceScopeFactory serviceScopeFactory;
|
|
||||||
|
|
||||||
public ScrobbleMonitor(ILogger<ScrobbleMonitor> _logger, IOptions<ScrobbleMonitorOptions> _options, IUserApi _userApi, IServiceScopeFactory _serviceScopeFactory, ILoggerFactory _loggerFactory)
|
|
||||||
{
|
|
||||||
logger = _logger;
|
|
||||||
userApi = _userApi;
|
|
||||||
config = _options.Value;
|
|
||||||
serviceScopeFactory = _serviceScopeFactory;
|
|
||||||
loggerFactory = _loggerFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Starting scrobble monitor");
|
|
||||||
|
|
||||||
using var scope = serviceScopeFactory.CreateScope();
|
|
||||||
using var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
||||||
|
|
||||||
await RunScrobbleSavers(db, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RunScrobbleSavers(ApplicationDbContext db, CancellationToken token)
|
|
||||||
{
|
|
||||||
using var scope = serviceScopeFactory.CreateScope();
|
|
||||||
|
|
||||||
foreach (var user in db.Users
|
|
||||||
.AsNoTracking()
|
|
||||||
.AsEnumerable()
|
|
||||||
.Where(u => u.ScrobbleSavingEnabled()))
|
|
||||||
{
|
|
||||||
//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user