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>
<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>
</Project>

View File

@ -1,5 +1,7 @@
using AutoScan.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace AutoScan;
@ -7,16 +9,44 @@ public class AutoScanApp
{
private readonly AutoScanOptions _options;
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;
_scheduler = scheduler;
}
private void Run()
public async Task Run(CancellationToken cancellationToken)
{
_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;
public class AutoScanOptions
public record AutoScanOptions
{
public bool Enabled { 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
private readonly ILogger<ShinobiConnector> _logger;
private readonly string _shinobivUrl = "https://shinobi.francelsoft.com";
private readonly string _apikey = "OGD6nsGGzA1NL48M5Tg7Wbzto62oPl";
private readonly string _groupId = "aInxuWCYLI";
private readonly string _monitorId = "mQ3kQ5qjKK";
private readonly string _shinobivUrl = "";
private readonly string _apikey = "";
private readonly string _groupId = "";
private readonly string _monitorId = "";
private readonly 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.Options;
using Telegram.Bot;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
@ -10,28 +11,28 @@ namespace CasaBotApp;
public class BotHandler
{
private readonly ILogger<BotHandler> _logger;
private readonly BotConfiguration _botConfiguration;
private readonly TelegramOptions _telegramOptions;
private readonly List<ChatId> _subscribers = [];
private TelegramBotClient? _bot;
public BotHandler(BotConfiguration botConfiguration, ILogger<BotHandler> logger)
public BotHandler(IOptions<TelegramOptions> telegramConfiguration, ILogger<BotHandler> logger)
{
_logger = logger;
if (string.IsNullOrEmpty(botConfiguration.Token))
if (string.IsNullOrEmpty(telegramConfiguration.Value.BotToken))
{
_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)
{
_logger.LogInformation("Starting bot...");
_bot = new TelegramBotClient(_botConfiguration.Token, cancellationToken: cancellationToken);
_bot = new TelegramBotClient(_telegramOptions.BotToken, cancellationToken: cancellationToken);
_bot.OnError += OnError;

View File

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