feat: extract scan library classes and add scheduler

This commit is contained in:
Guillermo Marcel 2025-02-14 20:32:04 -03:00
parent 57c0bb660c
commit be89bddf1b
13 changed files with 169 additions and 68 deletions

View File

@ -8,6 +8,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="Quartz" Version="3.13.1" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.13.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.13.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,7 @@
using AutoScan.Options; using AutoScan.Options;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace AutoScan; namespace AutoScan;
@ -7,16 +9,44 @@ public class AutoScanApp
{ {
private readonly AutoScanOptions _options; private readonly AutoScanOptions _options;
private readonly ILogger<AutoScanApp> _logger; private readonly ILogger<AutoScanApp> _logger;
private readonly IScheduler _scheduler;
public AutoScanApp(AutoScanOptions options, ILogger<AutoScanApp> logger) public AutoScanApp(IOptions<AutoScanOptions> options, ILogger<AutoScanApp> logger, IScheduler scheduler)
{ {
_options = options; _options = options.Value;
_logger = logger; _logger = logger;
_scheduler = scheduler;
} }
private void Run() public async Task Run(CancellationToken cancellationToken)
{ {
_logger.LogInformation("AutoScanApp is running..."); _logger.LogInformation("AutoScanApp is running...");
_logger.LogInformation("Waiting for next scan at {At}.", _options.At);
var at = DateTime.Now.AddMinutes(1).ToString("HH:mm");
var cron = CronFromAt(at);
_logger.LogInformation("Waiting for next scan at {At} [{cron}].", at, cron);
await _scheduler.Start(cancellationToken);
_logger.LogInformation("Scheduler started successfully!");
// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<ScanJob>()
.WithIdentity("job1", "group1")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithCronSchedule(cron)
.Build();
await _scheduler.ScheduleJob(job, trigger, cancellationToken);
_logger.LogInformation("Scheduled job successfully!");
} }
private string CronFromAt(string at)
{
var parts = at.Split(':');
return $"0 {parts[1]} {parts[0]} * * ?";
}
} }

View File

@ -0,0 +1,31 @@
using CasaBotApp;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
namespace AutoScan;
public static class DependencyInjectionExtensions
{
public static void AddAutoScan(this IServiceCollection services)
{
services.AddSingleton<ShinobiConnector>();
services.AddSingleton<DVRScanner>();
services.AddSingleton<FfmpegWrapper>();
services.AddSingleton<AutoScanApp>();
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
});
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
services.AddSingleton<IScheduler>(sp =>
{
var schedulerFactory = sp.GetRequiredService<ISchedulerFactory>();
var tsk = schedulerFactory.GetScheduler();
tsk.Wait();
return tsk.Result;
});
}
}

View File

@ -1,6 +1,6 @@
namespace AutoScan.Options; namespace AutoScan.Options;
public class AutoScanOptions public record AutoScanOptions
{ {
public bool Enabled { get; set; } public bool Enabled { get; set; }
public string? At { get; set; } public string? At { get; set; }

View File

@ -0,0 +1,24 @@
using AutoScan.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace AutoScan;
public class ScanJob : IJob
{
private readonly ILogger<ScanJob> _logger;
private readonly AutoScanOptions _options;
public ScanJob(ILogger<ScanJob> logger, IOptionsSnapshot<AutoScanOptions> options)
{
_logger = logger;
_options = options.Value;
}
public Task Execute(IJobExecutionContext context)
{
_logger.LogWarning("Scheduled scan executed with ops: {Options}", _options);
return Task.CompletedTask;
}
}

View File

@ -6,10 +6,10 @@ public class ShinobiConnector
{ {
//TODO move class to auto scan library //TODO move class to auto scan library
private readonly ILogger<ShinobiConnector> _logger; private readonly ILogger<ShinobiConnector> _logger;
private readonly string _shinobivUrl = "https://shinobi.francelsoft.com"; private readonly string _shinobivUrl = "";
private readonly string _apikey = "OGD6nsGGzA1NL48M5Tg7Wbzto62oPl"; private readonly string _apikey = "";
private readonly string _groupId = "aInxuWCYLI"; private readonly string _groupId = "";
private readonly string _monitorId = "mQ3kQ5qjKK"; private readonly string _monitorId = "";
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public ShinobiConnector(ILogger<ShinobiConnector> logger, HttpClient httpClient) public ShinobiConnector(ILogger<ShinobiConnector> logger, HttpClient httpClient)

View File

@ -1,6 +0,0 @@
namespace CasaBotApp;
public class BotConfiguration
{
public required string Token { get; set; }
}

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Polling; using Telegram.Bot.Polling;
using Telegram.Bot.Types; using Telegram.Bot.Types;
@ -10,28 +11,28 @@ namespace CasaBotApp;
public class BotHandler public class BotHandler
{ {
private readonly ILogger<BotHandler> _logger; private readonly ILogger<BotHandler> _logger;
private readonly BotConfiguration _botConfiguration; private readonly TelegramOptions _telegramOptions;
private readonly List<ChatId> _subscribers = []; private readonly List<ChatId> _subscribers = [];
private TelegramBotClient? _bot; private TelegramBotClient? _bot;
public BotHandler(BotConfiguration botConfiguration, ILogger<BotHandler> logger) public BotHandler(IOptions<TelegramOptions> telegramConfiguration, ILogger<BotHandler> logger)
{ {
_logger = logger; _logger = logger;
if (string.IsNullOrEmpty(botConfiguration.Token)) if (string.IsNullOrEmpty(telegramConfiguration.Value.BotToken))
{ {
_logger.LogError("Bot token is not provided"); _logger.LogError("Bot token is not provided");
throw new ArgumentException("Bot token is required", nameof(botConfiguration)); throw new ArgumentException("Bot token is required", nameof(telegramConfiguration));
} }
_botConfiguration = botConfiguration; _telegramOptions = telegramConfiguration.Value;
} }
public void Start(CancellationToken cancellationToken) public void Start(CancellationToken cancellationToken)
{ {
_logger.LogInformation("Starting bot..."); _logger.LogInformation("Starting bot...");
_bot = new TelegramBotClient(_botConfiguration.Token, cancellationToken: cancellationToken); _bot = new TelegramBotClient(_telegramOptions.BotToken, cancellationToken: cancellationToken);
_bot.OnError += OnError; _bot.OnError += OnError;

View File

@ -13,73 +13,79 @@ IConfigurationRoot configuration = new ConfigurationBuilder()
.AddEnvironmentVariables() .AddEnvironmentVariables()
.Build(); .Build();
var serviceCollection = new ServiceCollection(); var services = new ServiceCollection();
serviceCollection.AddSingleton(configuration); services.AddSingleton(configuration);
serviceCollection.AddLogging(builder => services.AddLogging(builder =>
{ {
builder.AddConfiguration(configuration.GetSection("Logging")); builder.AddConfiguration(configuration.GetSection("Logging"));
builder.AddConsole(); // builder.AddConsole();
}); //add time to logs
serviceCollection.AddSingleton(new BotConfiguration() builder.AddSimpleConsole(options =>
{ {
Token = configuration.GetValue<string>("TelegramToken") ?? "" options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "[HH:mm:ss] ";
});
}); });
serviceCollection.Configure<AutoScan.Options.AutoScanOptions>(configuration.GetSection("AutoScan"));
serviceCollection.AddSingleton<BotHandler>(); services.Configure<TelegramOptions>(configuration.GetSection("Telegram"));
serviceCollection.AddSingleton<ShinobiConnector>(); services.Configure<AutoScan.Options.AutoScanOptions>(configuration.GetSection("AutoScan"));
serviceCollection.AddSingleton<DVRScanner>();
serviceCollection.AddSingleton<FfmpegWrapper>();
serviceCollection.AddSingleton<AutoScanApp>();
serviceCollection.AddHttpClient();
var serviceProvider = serviceCollection.BuildServiceProvider(); services.AddSingleton<BotHandler>();
services.AddAutoScan();
services.AddHttpClient();
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>()!; var logger = serviceProvider.GetService<ILogger<Program>>()!;
var botHandler = serviceProvider.GetService<BotHandler>()!; var botHandler = serviceProvider.GetService<BotHandler>()!;
var autoScanApp = serviceProvider.GetService<AutoScanApp>()!;
using var cts = new CancellationTokenSource(); using var cts = new CancellationTokenSource();
botHandler.Start(cts.Token); autoScanApp.Run(cts.Token);
// botHandler.Start(cts.Token);
_ = SendMessageToSubscribers(cts.Token); _ = SendMessageToSubscribers(cts.Token);
var videoFileName = configuration.GetValue<string>("VideoFileName") ?? "video.mp4"; // var videoFileName = configuration.GetValue<string>("VideoFileName") ?? "video.mp4";
if (configuration.GetValue<bool>("Fetch")) // if (configuration.GetValue<bool>("Fetch"))
{ // {
var shinobiConnector = serviceProvider.GetService<ShinobiConnector>()!; // var shinobiConnector = serviceProvider.GetService<ShinobiConnector>()!;
await shinobiConnector.FetchLastVideo(videoFileName); // await shinobiConnector.FetchLastVideo(videoFileName);
} // }
//
if (configuration.GetValue<bool>("Scan")) // if (configuration.GetValue<bool>("Scan"))
{ // {
var dvrScanner = serviceProvider.GetService<DVRScanner>()!; // var dvrScanner = serviceProvider.GetService<DVRScanner>()!;
await dvrScanner.ScanVideos(cts.Token); // await dvrScanner.ScanVideos(cts.Token);
} // }
//
if(configuration.GetValue<bool>("Screenshot")) // if(configuration.GetValue<bool>("Screenshot"))
{ // {
var detected = "2025-02-12T07-00-02.DSME_0001.avi"; // var detected = "2025-02-12T07-00-02.DSME_0001.avi";
var ffmpegWrapper = serviceProvider.GetService<FfmpegWrapper>()!; // var ffmpegWrapper = serviceProvider.GetService<FfmpegWrapper>()!;
var duration = await ffmpegWrapper.GetVideoDuration($@".\media\detected\{detected}"); // var duration = await ffmpegWrapper.GetVideoDuration($@".\media\detected\{detected}");
logger.LogInformation("Video duration: {Duration}", duration); // logger.LogInformation("Video duration: {Duration}", duration);
var middleTime = (duration / 2).Add(TimeSpan.FromSeconds(0.5)); // var middleTime = (duration / 2).Add(TimeSpan.FromSeconds(0.5));
//
//Extract frame at middle time // //Extract frame at middle time
var screenshotPath = $@".\media\detected\{detected}-ss.png"; // var screenshotPath = $@".\media\detected\{detected}-ss.png";
await ffmpegWrapper.ExtractFrame($@".\media\detected\{detected}", screenshotPath, middleTime); // await ffmpegWrapper.ExtractFrame($@".\media\detected\{detected}", screenshotPath, middleTime);
logger.LogInformation("Screenshot extracted at {MiddleTime}", middleTime); // logger.LogInformation("Screenshot extracted at {MiddleTime}", middleTime);
//
//botHandler.Subscribe(115151151); // //botHandler.Subscribe(115151151);
await botHandler.UpdatePhoto(screenshotPath); // await botHandler.UpdatePhoto(screenshotPath);
} // }
logger.LogInformation("Bot started"); logger.LogInformation("Bot started");
logger.LogInformation("Press any key to stop the bot..."); logger.LogInformation("Press any key to stop the bot...");
Console.ReadLine(); Console.ReadLine();
cts.Cancel(); // stop the bot cts.Cancel(); // stop the bot
return; return;

View File

@ -0,0 +1,7 @@
namespace CasaBotApp;
public class TelegramOptions
{
public string? BotToken { get; set; }
public long[] SubscribedChatIds { get; set; } = [];
}

View File

@ -3,9 +3,14 @@
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Debug",
"System": "Information", "System": "Information",
"Microsoft": "Information" "Microsoft": "Information",
"Quartz": "Information"
} }
}, },
"Telegram":{
"BotToken": "__token__",
"SubscribedChatIds": []
},
"AutoScan": { "AutoScan": {
"Enabled": false, "Enabled": false,
"At": "07:00", "At": "07:00",