Compare commits

..

No commits in common. "0a78f444bd22bcc7822bb167ed5044f27a15e367" and "affae8ff48d5360455036bca1b63e4d29a31b794" have entirely different histories.

15 changed files with 8 additions and 421 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
src/CasaBot/CasaBotApp/dvr-scanner/** filter=lfs diff=lfs merge=lfs -text

View File

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
</ItemGroup>
</Project>

View File

@ -1,22 +0,0 @@
using AutoScan.Options;
using Microsoft.Extensions.Logging;
namespace AutoScan;
public class AutoScanApp
{
private readonly AutoScanOptions _options;
private readonly ILogger<AutoScanApp> _logger;
public AutoScanApp(AutoScanOptions options, ILogger<AutoScanApp> logger)
{
_options = options;
_logger = logger;
}
private void Run()
{
_logger.LogInformation("AutoScanApp is running...");
_logger.LogInformation("Waiting for next scan at {At}.", _options.At);
}
}

View File

@ -1,15 +0,0 @@
namespace AutoScan.Options;
public class AutoScanOptions
{
public bool Enabled { get; set; }
public string? At { get; set; }
public bool FromDayBefore { get; set; }
public string? From { get; set; }
public string? To { get; set; }
public int MaxAmount { get; set; }
public string? MediaFolder { get; set; }
public ShinobiOptions? Shinobi { get; set; }
public ScannerOptions? Scanner { get; set; }
public ScreenshotOptions? Screenshot { get; set; }
}

View File

@ -1,8 +0,0 @@
namespace AutoScan.Options;
public class ScannerOptions
{
public string? Exe { get; set; }
public string? ConfigFile { get; set; }
public string? DetectionFolder { 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

@ -1,9 +0,0 @@
namespace AutoScan.Options;
public class ShinobiOptions
{
public string? URL { get; set; }
public string? APIKey { get; set; }
public string? GroupId { get; set; }
public string? MonitorId { get; set; }
}

View File

@ -2,8 +2,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CasaBotApp", "CasaBotApp\CasaBotApp.csproj", "{FF1AF6E7-88E4-488B-B6FB-BDAC126DD94E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoScan", "AutoScan\AutoScan.csproj", "{13D75ACB-7913-4C4B-B696-9BD7383012AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -14,9 +12,5 @@ Global
{FF1AF6E7-88E4-488B-B6FB-BDAC126DD94E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF1AF6E7-88E4-488B-B6FB-BDAC126DD94E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF1AF6E7-88E4-488B-B6FB-BDAC126DD94E}.Release|Any CPU.Build.0 = Release|Any CPU
{13D75ACB-7913-4C4B-B696-9BD7383012AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13D75ACB-7913-4C4B-B696-9BD7383012AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13D75ACB-7913-4C4B-B696-9BD7383012AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13D75ACB-7913-4C4B-B696-9BD7383012AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -58,40 +58,7 @@ public class BotHandler
_bot.SendMessage(subscriber, message);
}
}
public void Subscribe(long id)
{
if (_subscribers.Any(s => s.Identifier == id))
{
_logger.LogWarning("User {Id} is already subscribed", id);
return;
}
_subscribers.Add(new ChatId(id));
_logger.LogInformation("User {Id} subscribed to receive messages", id);
}
public async Task UpdatePhoto(string path)
{
if (_bot is null)
{
_logger.LogWarning("Bot is not initialized yet");
return;
}
if (_subscribers.Count == 0)
{
_logger.LogWarning("No subscribers to send message to");
return;
}
foreach (var subscriber in _subscribers)
{
await using var stream = File.OpenRead(path);
var inputFile = InputFile.FromStream(stream, Path.GetFileName(path));
await _bot.SendPhoto(subscriber, inputFile, "Detected object");
}
}
private async Task SendImageTest(long id)
{
if (_bot is null)

View File

@ -18,22 +18,4 @@
<PackageReference Include="Telegram.Bot" Version="22.3.0" />
</ItemGroup>
<ItemGroup>
<None Update="dvr-scanner\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AutoScan\AutoScan.csproj" />
</ItemGroup>
</Project>

View File

@ -1,64 +0,0 @@
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace CasaBotApp;
public class DVRScanner
{
private readonly ILogger<DVRScanner> _logger;
private const string _mediaPath = @".\media"; // Replace with your desired file path
private const string _dvrScannerFile = @".\dvr-scanner\dvr-scan.exe";
private const string _dvrScannerConfig = @".\dvr-scanner\dvr-scan.cfg";
//.\dvr-scanner\dvr-scan.exe -i .\media\*.mp4 -c .\dvr-scanner\dvr-scan.cfg
public DVRScanner(ILogger<DVRScanner> logger)
{
_logger = logger;
}
public async Task ScanVideos(CancellationToken cancellationToken = default)
{
try
{
//make sure the directory exists
Directory.CreateDirectory(Path.GetDirectoryName(_mediaPath)!);
_logger.LogDebug("Scanning videos...");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = _dvrScannerFile,
Arguments = $"-i {_mediaPath}\\*.mp4 -c {_dvrScannerConfig}",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
cancellationToken.Register(() =>
{
_logger.LogDebug("DVR Process status: ID: {Id}, HasExited: {HasExited}, Responding: {Responding}",
process.Id, process.HasExited, process.Responding);
if(process.HasExited) return;
_logger.LogWarning("DVR Process is still running, killing it...");
process.Kill();
});
_logger.LogDebug("DVR Process started with ID: {Id}", process.Id);
await process.WaitForExitAsync(cancellationToken);
_logger.LogInformation("Videos scanned successfully!");
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while scanning the videos");
}
}
}

View File

@ -1,87 +0,0 @@
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace CasaBotApp;
public class FfmpegWrapper
{
private readonly ILogger<FfmpegWrapper> _logger;
public FfmpegWrapper(ILogger<FfmpegWrapper> logger)
{
_logger = logger;
}
private const string FFMPEG_PATH = @".\dvr-scanner\ffmpeg.exe";
//Include method for get duration of a video, extract the frame at a specific time
public async Task<TimeSpan> GetVideoDuration(string videoPath)
{
try
{
_logger.LogDebug("Getting video duration...");
var ffmpegProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = FFMPEG_PATH,
Arguments = $"-i {videoPath}",
RedirectStandardOutput = false,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true
}
};
ffmpegProcess.Start();
var standardError = await ffmpegProcess.StandardError.ReadToEndAsync();
await ffmpegProcess.WaitForExitAsync();
// Parse the duration from the ffmpeg output
var durationString = System.Text.RegularExpressions.Regex.Match(standardError, @"Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})").Groups;
var ts = new TimeSpan(0,
int.Parse(durationString[1].Value),
int.Parse(durationString[2].Value),
int.Parse(durationString[3].Value),
int.Parse(durationString[4].Value) * 10
);
return ts;
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while getting the video duration");
throw new Exception("There was an error getting the video duration", ex);
}
}
public async Task ExtractFrame(string videoPath, string outputPath, TimeSpan time)
{
try
{
var timeParam = $"{time.Minutes:D2}:{time.Seconds:D2}.{time.Milliseconds:D2}";
_logger.LogDebug("Extracting frame: {videoPath} at {time} to {outputPath}", videoPath, timeParam, outputPath);
var ffmpegProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = FFMPEG_PATH,
Arguments = $"-y -i {videoPath} -ss {timeParam} -vframes 1 {outputPath}",
RedirectStandardOutput = false,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = false
}
};
ffmpegProcess.Start();
_logger.LogDebug("Waiting for frame extraction... process ID: {Id}", ffmpegProcess.Id);
await ffmpegProcess.WaitForExitAsync();
_logger.LogInformation("Frame extracted successfully!");
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while extracting the frame");
throw new Exception("There was an error extracting the frame", ex);
}
}
}

View File

@ -1,5 +1,4 @@
using AutoScan;
using CasaBotApp;
using CasaBotApp;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -15,30 +14,25 @@ IConfigurationRoot configuration = new ConfigurationBuilder()
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton(configuration);
serviceCollection.AddLogging(builder =>
{
builder.AddConfiguration(configuration.GetSection("Logging"));
builder.AddConsole();
});
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();
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>()!;
var botHandler = serviceProvider.GetService<BotHandler>()!;
var botHandler = serviceProvider.GetService<BotHandler>();
if (botHandler is null)
{
Console.WriteLine("Bot is not initialized");
return;
}
using var cts = new CancellationTokenSource();
@ -46,38 +40,8 @@ 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);
}
logger.LogInformation("Bot started");
logger.LogInformation("Press any key to stop the bot...");
Console.WriteLine($"bot is running... Press Enter to terminate");
Console.ReadLine();
cts.Cancel(); // stop the bot
return;

View File

@ -1,61 +0,0 @@
using Microsoft.Extensions.Logging;
namespace CasaBotApp;
public class ShinobiConnector
{
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 HttpClient _httpClient;
public ShinobiConnector(ILogger<ShinobiConnector> logger, HttpClient httpClient)
{
_logger = logger;
_httpClient = httpClient;
}
public async Task FetchLastVideo(string filename = "2025-02-12T08-00-01.mp4")
{
//fetch video from shinobi endpoint using example file "2025-02-12T08-00-01.mp4"
const string fetchVideoEndpoint = "/{0}/videos/{1}/{2}/{3}";
var endpoint = string.Format(_shinobivUrl+fetchVideoEndpoint, _apikey, _groupId, _monitorId, filename);
_logger.LogInformation("Fetching video from endpoint: {Endpoint}", endpoint);
//fetch video
const string mediaPath = @".\media\"; // Replace with your desired file path
var videoPath = mediaPath + filename;
try
{
//make sure the directory exists
Directory.CreateDirectory(Path.GetDirectoryName(mediaPath)!);
_logger.LogDebug("Cleaning media folder");
CleanDirectory(mediaPath);
_logger.LogDebug("Downloading video...");
var videoData = await _httpClient.GetByteArrayAsync(endpoint);
//Write the video to the file
await File.WriteAllBytesAsync(videoPath, videoData);
_logger.LogInformation("Video downloaded successfully!");
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while downloading the video");
}
}
private void CleanDirectory(string path)
{
DirectoryInfo di = new DirectoryInfo(path);
foreach (var file in di.GetFiles())
{
file.Delete();
}
}
}

View File

@ -1,33 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AutoScan": {
"Enabled": false,
"At": "07:00",
"FromDayBefore": true,
"From": "23:00",
"To": "05:00",
"MaxAmount": 1,
"MediaFolder": "./media/originals",
"Shinobi": {
"URL": "http://localhost:8080",
"APIKey": "APIKEY",
"GroupId": "Group",
"MonitorId": "Monitor"
},
"Scanner": {
"Exe": "./dvr-scanner/dvr.exe",
"ConfigFile": "./dvr-scanner/dvr-scan.cfg",
"DetectionFolder": "./media/detections"
},
"Screenshot": {
"Folder": "./media/screenshots",
"OffsetSeconds": 0
}
}
}