Added worker app.
This commit is contained in:
28
JSMR.Worker/Services/FileCheckpointStore.cs
Normal file
28
JSMR.Worker/Services/FileCheckpointStore.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace JSMR.Worker.Services;
|
||||
|
||||
public sealed class FileCheckpointStore : ICheckpointStore
|
||||
{
|
||||
private readonly string _root = Path.Combine(AppContext.BaseDirectory, "State");
|
||||
|
||||
public FileCheckpointStore() => Directory.CreateDirectory(_root);
|
||||
|
||||
public Task<int?> GetLastPageAsync(string locale, CancellationToken ct)
|
||||
{
|
||||
string path = Path.Combine(_root, $"scan.{locale}.page");
|
||||
if (!File.Exists(path))
|
||||
return Task.FromResult<int?>(null);
|
||||
|
||||
if (int.TryParse(File.ReadAllText(path).Trim(), out var n))
|
||||
return Task.FromResult<int?>(n);
|
||||
|
||||
return Task.FromResult<int?>(null);
|
||||
}
|
||||
|
||||
public Task SaveLastPageAsync(string locale, int page, CancellationToken ct)
|
||||
{
|
||||
string path = Path.Combine(_root, $"scan.{locale}.page");
|
||||
File.WriteAllText(path, page.ToString());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
7
JSMR.Worker/Services/ICheckpointStore.cs
Normal file
7
JSMR.Worker/Services/ICheckpointStore.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace JSMR.Worker.Services;
|
||||
|
||||
public interface ICheckpointStore
|
||||
{
|
||||
Task<int?> GetLastPageAsync(string locale, CancellationToken cancellationToken);
|
||||
Task SaveLastPageAsync(string locale, int page, CancellationToken cancellationToken);
|
||||
}
|
||||
28
JSMR.Worker/Services/ScanJob.cs
Normal file
28
JSMR.Worker/Services/ScanJob.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using JSMR.Application.Enums;
|
||||
using JSMR.Application.Scanning;
|
||||
using JSMR.Worker.Options;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace JSMR.Worker.Services;
|
||||
|
||||
public sealed class ScanJob(ILogger<ScanJob> log, IOptions<ScanOptions> options, ScanVoiceWorksHandler scanVoiceWorksHandler)
|
||||
{
|
||||
private readonly ScanOptions _options = options.Value;
|
||||
|
||||
public async Task RunOnceAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
log.LogInformation("Starting scan: Locale={Locale}, Start Page={StartPage}, EndPage={EndPage}",
|
||||
_options.Locale, _options.StartPage, _options.EndPage);
|
||||
|
||||
ScanVoiceWorksRequest request = new(
|
||||
PageNumber: 1,
|
||||
PageSize: 100,
|
||||
Locale: Enum.Parse<Locale>(_options.Locale, true)
|
||||
);
|
||||
|
||||
await scanVoiceWorksHandler.HandleAsync(request, cancellationToken);
|
||||
|
||||
log.LogInformation("Scan completed.");
|
||||
}
|
||||
}
|
||||
59
JSMR.Worker/Services/ScanRunner.cs
Normal file
59
JSMR.Worker/Services/ScanRunner.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using JSMR.Application.Enums;
|
||||
using JSMR.Application.Scanning;
|
||||
using JSMR.Worker.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JSMR.Worker.Services;
|
||||
|
||||
public sealed class PagedScanRunner(
|
||||
ILogger<PagedScanRunner> log,
|
||||
IServiceProvider serviceProvider,
|
||||
ICheckpointStore checkpoints)
|
||||
{
|
||||
public async Task RunAsync(ScanOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Enum.TryParse(options.Locale, ignoreCase: true, out Locale locale) == false)
|
||||
throw new ArgumentException($"Unknown locale '{options.Locale}'.");
|
||||
|
||||
int pageSize = options.PageSize ?? 100;
|
||||
|
||||
int startPage = options.StartPage
|
||||
?? (await checkpoints.GetLastPageAsync(options.Locale, cancellationToken)).GetValueOrDefault(0) + 1;
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
int currentPage = startPage;
|
||||
int? end = options.EndPage;
|
||||
|
||||
// Iterate until empty page or end reached
|
||||
for (; !cancellationToken.IsCancellationRequested && (!end.HasValue || currentPage <= end.Value); currentPage++)
|
||||
{
|
||||
ScanVoiceWorksHandler handler = serviceProvider.GetRequiredService<ScanVoiceWorksHandler>();
|
||||
|
||||
log.LogInformation("Scanning page {Page} (size {Size}, locale {Locale})…", currentPage, pageSize, locale);
|
||||
|
||||
ScanVoiceWorksRequest request = new(
|
||||
PageNumber: currentPage,
|
||||
PageSize: 100,
|
||||
Locale: locale
|
||||
);
|
||||
|
||||
ScanVoiceWorksResponse response = await handler.HandleAsync(request, cancellationToken);
|
||||
|
||||
// Save checkpoint
|
||||
await checkpoints.SaveLastPageAsync(options.Locale, currentPage, cancellationToken);
|
||||
}
|
||||
|
||||
if (!options.Watch) break;
|
||||
|
||||
log.LogInformation("Watch mode: sleeping {Interval}…", options.Interval);
|
||||
await Task.Delay(options.Interval, cancellationToken);
|
||||
|
||||
// Compute next “start” for next cycle:
|
||||
// - If you want to re-scan the latest N pages every loop to catch late updates,
|
||||
// modify logic here (e.g., start = Math.Max(1, current - 2))
|
||||
startPage = currentPage; // continue from where we left off
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user