feat: OnScanCompleted event & output dir & send photos

This commit is contained in:
Guillermo Marcel 2025-02-18 19:21:19 -03:00
parent ea02a726ef
commit a6ac0187f1
14 changed files with 70 additions and 50 deletions

View File

@ -15,6 +15,8 @@ public class AutoScanApp
private readonly IScheduler _scheduler;
private readonly IChainerListenerFactory _chainerListenerFactory;
public Func<AutoScanOptions, Task>? OnScanCompleted { get; set; }
public AutoScanApp(IOptions<AutoScanOptions> options, ILogger<AutoScanApp> logger, IScheduler scheduler, IChainerListenerFactory chainerListenerFactory)
{
_options = options.Value;
@ -60,6 +62,7 @@ public class AutoScanApp
var chainer = _chainerListenerFactory.CreateChainerListener("Scan Chainer");
chainer.AddJobChainLink(downloaderJob.Key, scannerJob.Key);
chainer.OnJobChainFinished = async () => await OnScanCompleted?.Invoke(_options);
_scheduler.ListenerManager.AddJobListener(chainer, GroupMatcher<JobKey>.GroupEquals(groupName));

View File

@ -28,7 +28,7 @@ public class DVRScanner : IDVRScanner
{
_logger.LogDebug("Scanning videos...");
var folderParam = Path.Combine(_options.MediaFolder!, "*.mp4");
var arguments = $"-i {folderParam} -c {_options.Scanner.ConfigFile} --thumbnails highscore";
var arguments = $"-i {folderParam} -c {_options.Scanner.ConfigFile} --output-dir {_options.Scanner.DetectionFolder} --thumbnails highscore";
_logger.LogDebug("Executing command: {_dvrScannerFile} {arguments}", _options.Scanner.Exe, arguments);
var process = new Process
{
@ -36,7 +36,7 @@ public class DVRScanner : IDVRScanner
{
FileName = _options.Scanner.Exe,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
UseShellExecute = false,
CreateNoWindow = true,

View File

@ -48,7 +48,7 @@ public class ShinobiConnector : IDVRConnector
}).OrderBy(x => x.time).ToList();
}
public async Task DownloadMonitorVideo(VideoDetail video, string downloadFolder, CancellationToken cancellationToken = default)
public async Task DownloadMonitorVideo(VideoDetail video, string downloadFolder, bool runDry, CancellationToken cancellationToken = default)
{
var endpoint = $"{_options.URL}{video.href}";
_logger.LogDebug("Fetching video from endpoint: {Endpoint}", endpoint);
@ -62,7 +62,7 @@ public class ShinobiConnector : IDVRConnector
_logger.LogDebug("Downloading video...");
var videoData = await _httpClient.GetByteArrayAsync(endpoint, cancellationToken);
if(_options.RunDry)
if(runDry)
{
_logger.LogInformation("RunDry is enabled, skipping video download");
return;

View File

@ -5,5 +5,5 @@ namespace AutoScan.Interfaces;
public interface IDVRConnector
{
Task<List<VideoDetail>> FetchMonitorVideosBetween(DateTime from, DateTime to, CancellationToken cancellationToken = default);
Task DownloadMonitorVideo(VideoDetail video, string downloadFolder, CancellationToken cancellationToken = default);
Task DownloadMonitorVideo(VideoDetail video, string downloadFolder, bool runDry = false, CancellationToken cancellationToken = default);
}

View File

@ -65,7 +65,7 @@ public class DownloaderJob : IJob
foreach (var video in videos)
{
_logger.LogDebug("Downloading video {Filename}", video.filename);
await _dvrConnector.DownloadMonitorVideo(video, _options.MediaFolder, context.CancellationToken);
await _dvrConnector.DownloadMonitorVideo(video, _options.MediaFolder, _options.RunDry, context.CancellationToken);
}
context.Result = new JobResult()
@ -78,8 +78,9 @@ public class DownloaderJob : IJob
private void CleanMediaFolder()
{
if (_options.MediaFolder is not null)
if (_options.MediaFolder is not null && !_options.RunDry)
{
_logger.LogDebug("Cleaning media folder {MediaFolder}", _options.MediaFolder);
Directory.CreateDirectory(Path.GetDirectoryName(_options.MediaFolder)!);
foreach (var file in Directory.GetFiles(_options.MediaFolder))
{
@ -87,8 +88,9 @@ public class DownloaderJob : IJob
}
}
if(_options.Scanner?.DetectionFolder is not null)
if(_options.Scanner?.DetectionFolder is not null && !_options.Scanner.RunDry)
{
_logger.LogDebug("Cleaning detection folder {DetectionFolder}", _options.Scanner.DetectionFolder);
Directory.CreateDirectory(Path.GetDirectoryName(_options.Scanner.DetectionFolder)!);
foreach (var file in Directory.GetFiles(_options.Scanner.DetectionFolder))
{
@ -96,15 +98,5 @@ public class DownloaderJob : IJob
}
}
if(_options.Screenshot?.Folder is not null)
{
Directory.CreateDirectory(Path.GetDirectoryName(_options.Screenshot.Folder)!);
foreach (var file in Directory.GetFiles(_options.Screenshot.Folder))
{
File.Delete(file);
}
}
}
}

View File

@ -14,12 +14,10 @@ public class ScannerJob : IJob
_logger = logger;
_scanner = scanner;
}
public Task Execute(IJobExecutionContext context)
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Scanner {scannerName} is ready to scan!", _scanner.GetType().Name);
_scanner.ScanVideos();
return Task.CompletedTask;
await _scanner.ScanVideos();
}
}

View File

@ -9,6 +9,7 @@ public class ChainerListener : JobListenerSupport
{
private readonly ILogger<ChainerListener> _logger;
private readonly Dictionary<JobKey, JobKey> _chainLinks;
public Func<Task> OnJobChainFinished { get; set; }
public ChainerListener(string name, ILogger<ChainerListener> logger)
{
@ -51,8 +52,14 @@ public class ChainerListener : JobListenerSupport
if (sj == null)
{
//check if it's the last one in the chain
if (_chainLinks.ContainsValue(context.JobDetail.Key))
{
_logger.LogInformation("Job '{JobKey}' is the last in the chain", context.JobDetail.Key);
await OnJobChainFinished?.Invoke();
return;
}
}
_logger.LogInformation("Job '{JobKey}' will now chain to Job '{sj}'", context.JobDetail.Key, sj);

View File

@ -2,6 +2,7 @@ namespace AutoScan.Options;
public record AutoScanOptions
{
public bool RunDry { get; set; } = false;
public bool Enabled { get; set; }
public string At { get; set; } = "06:00";
public bool FromDayBefore { get; set; }
@ -10,5 +11,4 @@ public record AutoScanOptions
public int MaxAmount { get; set; }
public string? MediaFolder { get; set; }
public ScannerOptions? Scanner { get; set; }
public ScreenshotOptions? Screenshot { get; set; }
}

View File

@ -1,7 +0,0 @@
namespace AutoScan.Options;
public class ScreenshotOptions
{
public string? Folder { get; set; }
public int OffsetSeconds { get; set; }
}

View File

@ -6,5 +6,4 @@ public class ShinobiOptions
public string? APIKey { get; set; }
public string? GroupId { get; set; }
public string? MonitorId { get; set; }
public bool RunDry { get; set; } = false;
}

View File

@ -89,6 +89,32 @@ public class BotHandler : IUpdateHandler
await SndPhoto(subscriber, stream);
}
}
public async Task UpdatePhotos(string[] paths)
{
if (_subscribers.Count == 0)
{
_logger.LogWarning("No subscribers to send message to");
return;
}
var streams = paths.Select(File.OpenRead).ToList();
var photos = streams.Select(stream => new PhotoFile(stream)).Cast<IGroupableMedia>().ToList();
foreach (var subscriber in _subscribers)
{
var request = new SendMediaGroup(subscriber.Id.ToString(), photos);
await _bot.HandleAsync(request);
}
foreach (var stream in streams)
{
stream.Close();
await stream.DisposeAsync();
}
}
private async Task SendImageTest(long id)
{
await using var stream = File.OpenRead(@"C:\Users\GuillermoMarcel\Pictures\prueba.jpeg");

View File

@ -18,6 +18,11 @@ RUN dotnet publish "CasaBotApp/CasaBotApp.csproj" -a $TARGETARCH --no-restore -o
# Runtime stage
FROM mcr.microsoft.com/dotnet/runtime:9.0
WORKDIR /app
# I need to run this "python3 -m pip install dvr-scan[opencv]" install everything needed
RUN apt-get update && apt-get install -y python3 python3-pip
RUN python3 -m pip install dvr-scan[opencv-headless] --break-system-packages
COPY --link --from=build /app .
USER $APP_UID
ENTRYPOINT ["dotnet", "CasaBotApp.dll"]

View File

@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Telegram.Bots;
using Telegram.Bots.Extensions.Polling;
@ -60,7 +61,17 @@ using var cts = new CancellationTokenSource();
_ = autoScanApp.Run(cts.Token);
botHandler.Start(cts.Token);
_ = SendMessageToSubscribers(cts.Token);
autoScanApp.OnScanCompleted = async options =>
{
logger.LogInformation("Scan completed at {At}", DateTime.Now);
//list all the images in the detection folder
if (options.Scanner?.DetectionFolder is null)
return;
var images = Directory.GetFiles(options.Scanner.DetectionFolder , "*.jpg");
botHandler.UpdatePhotos(images);
};
_ = host.RunAsync(cts.Token);
@ -72,15 +83,5 @@ await cts.CancelAsync(); // stop the bot
return;
async Task SendMessageToSubscribers(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
logger.LogInformation("Sending message to subscribers");
await botHandler.Update($"Hello from CasaBot! at {DateTime.Now}");
}
}

View File

@ -18,10 +18,10 @@
"APIKey": "APIKEY",
"GroupId": "Group",
"MonitorId": "Monitor",
"RunDry": false
},
"AutoScan": {
"Enabled": false,
"Enabled": true,
"RunDry": true,
"At": "07:00",
"FromDayBefore": true,
"From": "23:00",
@ -32,11 +32,7 @@
"Exe": "./dvr-scanner/dvr-scan.exe",
"ConfigFile": "./dvr-scanner/dvr-scan.cfg",
"DetectionFolder": "./media/detections/",
"RunDry": false
},
"Screenshot": {
"Folder": "./media/screenshots/",
"OffsetSeconds": 0
"RunDry": true
}
}
}