diff --git a/KfChatDotNetBot/Services/DLive.cs b/KfChatDotNetBot/Services/DLive.cs index 84f3315..bf54c9d 100644 --- a/KfChatDotNetBot/Services/DLive.cs +++ b/KfChatDotNetBot/Services/DLive.cs @@ -33,9 +33,11 @@ public class DLive(ChatBot kfChatBot) : IDisposable await using var db = new ApplicationDbContext(); var streams = await db.Streams.Where(s => s.Service == StreamService.DLive).Include(s => s.User).ToListAsync(ct); var settings = await SettingsProvider.GetMultipleValuesAsync([ - BuiltIn.Keys.DLivePersistedCurrentlyLiveStreams, BuiltIn.Keys.CaptureEnabled + BuiltIn.Keys.DLivePersistedCurrentlyLiveStreams, BuiltIn.Keys.CaptureEnabled, + BuiltIn.Keys.CaptureLockTable ]); var currentlyLive = settings[BuiltIn.Keys.DLivePersistedCurrentlyLiveStreams].JsonDeserialize>() ?? []; + var locks = settings[BuiltIn.Keys.CaptureLockTable].JsonDeserialize>() ?? new Dictionary(); foreach (var stream in streams) { var username = stream.StreamUrl.Split('/').LastOrDefault(); @@ -45,6 +47,15 @@ public class DLive(ChatBot kfChatBot) : IDisposable continue; } + if (locks.ContainsKey(stream.StreamUrl)) + { + if (File.Exists(locks[stream.StreamUrl])) + { + _logger.Debug($"{stream.StreamUrl} has an existing lock at {locks[stream.StreamUrl]}, ignoring"); + continue; + } + } + DLiveIsLiveModel status; try { diff --git a/KfChatDotNetBot/Services/StreamCapture.cs b/KfChatDotNetBot/Services/StreamCapture.cs index 7d95274..188caec 100644 --- a/KfChatDotNetBot/Services/StreamCapture.cs +++ b/KfChatDotNetBot/Services/StreamCapture.cs @@ -13,10 +13,16 @@ namespace KfChatDotNetBot.Services; public class StreamCapture(string streamUrl, StreamCaptureMethods captureMethod, CaptureOverridesModel? captureOverrides = null, CancellationToken ct = default) { private readonly Dictionary _settings = SettingsProvider - .GetMultipleValuesAsync([BuiltIn.Keys.CaptureYtDlpBinaryPath, BuiltIn.Keys.CaptureYtDlpWorkingDirectory, - BuiltIn.Keys.CaptureYtDlpCookiesFromBrowser, BuiltIn.Keys.CaptureYtDlpOutputFormat, BuiltIn.Keys.CaptureYtDlpParentTerminal, - BuiltIn.Keys.CaptureYtDlpScriptPath, BuiltIn.Keys.CaptureYtDlpUserAgent, BuiltIn.Keys.CaptureStreamlinkBinaryPath, - BuiltIn.Keys.CaptureStreamlinkOutputFormat, BuiltIn.Keys.CaptureStreamlinkRemuxScript, BuiltIn.Keys.CaptureStreamlinkTwitchOptions]).Result; + .GetMultipleValuesAsync([ + BuiltIn.Keys.CaptureYtDlpBinaryPath, BuiltIn.Keys.CaptureYtDlpWorkingDirectory, + BuiltIn.Keys.CaptureYtDlpCookiesFromBrowser, BuiltIn.Keys.CaptureYtDlpOutputFormat, + BuiltIn.Keys.CaptureYtDlpParentTerminal, + BuiltIn.Keys.CaptureYtDlpScriptPath, BuiltIn.Keys.CaptureYtDlpUserAgent, + BuiltIn.Keys.CaptureStreamlinkBinaryPath, + BuiltIn.Keys.CaptureStreamlinkOutputFormat, BuiltIn.Keys.CaptureStreamlinkRemuxScript, + BuiltIn.Keys.CaptureStreamlinkTwitchOptions, + BuiltIn.Keys.CaptureLockTable + ]).Result; private readonly Logger _logger = LogManager.GetCurrentClassLogger(); @@ -97,11 +103,14 @@ public class StreamCapture(string streamUrl, StreamCaptureMethods captureMethod, var random = Convert.ToHexString(Guid.NewGuid().ToByteArray()[..4]); var scriptPath = Path.Join(_settings[captureOverrides?.CaptureYtDlpScriptPath ?? BuiltIn.Keys.CaptureYtDlpScriptPath].Value, $"bot_ytdlp_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}_{random}.sh"); + var lockFile = Path.Join(_settings[captureOverrides?.CaptureYtDlpScriptPath ?? BuiltIn.Keys.CaptureYtDlpScriptPath].Value, + $"capture_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}_{random}.lock"); if (OperatingSystem.IsWindows()) { Path.ChangeExtension(scriptPath, ".bat"); } _logger.Info($"Generated script path: {scriptPath}"); + _logger.Info($"Generated lock file path: {lockFile}"); string captureLine; if (captureMethod == StreamCaptureMethods.YtDlp) @@ -141,17 +150,21 @@ public class StreamCapture(string streamUrl, StreamCaptureMethods captureMethod, { // GetPathRoot on Windows returns the top level directory, e.g. "C:\". Assuming the working directory is on another drive such as D: // we'll need to swap to that drive letter, so this just trims off the \ to transform it to D: or whatever. UNC paths not supported - scriptContent = $"{Path.GetPathRoot(captureOverrides?.CaptureYtDlpWorkingDirectory ?? _settings[BuiltIn.Keys.CaptureYtDlpWorkingDirectory].Value)?.TrimEnd('\\')}{Environment.NewLine}" + + scriptContent = $"TYPE nul > {lockFile}{Environment.NewLine}" + + $"{Path.GetPathRoot(captureOverrides?.CaptureYtDlpWorkingDirectory ?? _settings[BuiltIn.Keys.CaptureYtDlpWorkingDirectory].Value)?.TrimEnd('\\')}{Environment.NewLine}" + $"CD {captureOverrides?.CaptureYtDlpWorkingDirectory ?? _settings[BuiltIn.Keys.CaptureYtDlpWorkingDirectory].Value}{Environment.NewLine}" + $"{captureLine}{Environment.NewLine}" + + $"DEL /q {lockFile}{Environment.NewLine}" + $"{remuxLine}{Environment.NewLine}" + $"PAUSE"; } else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) { scriptContent = $"#!/bin/bash{Environment.NewLine}" + + $"touch {lockFile}{Environment.NewLine}" + $"cd {captureOverrides?.CaptureYtDlpWorkingDirectory ?? _settings[BuiltIn.Keys.CaptureYtDlpWorkingDirectory].Value}{Environment.NewLine}" + $"{captureLine}{Environment.NewLine}" + + $"rm -f {lockFile}{Environment.NewLine}" + $"{remuxLine}{Environment.NewLine}" + $"read -p \"Press enter to exit\""; } @@ -162,6 +175,17 @@ public class StreamCapture(string streamUrl, StreamCaptureMethods captureMethod, _logger.Info("Wrote the script, contents follow this message"); _logger.Info(scriptContent); + + _logger.Info("Updating locktable"); + var table = _settings[BuiltIn.Keys.CaptureLockTable].JsonDeserialize>() ?? + new Dictionary(); + if (!table.TryAdd(streamUrl, lockFile)) + { + _logger.Info($"{streamUrl} has a lockfile already pointing to {table[streamUrl]}, updating to {lockFile}"); + table[streamUrl] = lockFile; + } + + await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.CaptureLockTable, table); await File.WriteAllTextAsync(scriptPath, scriptContent, ct); if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) diff --git a/KfChatDotNetBot/Settings/BuiltIn.cs b/KfChatDotNetBot/Settings/BuiltIn.cs index 43594f3..d385307 100644 --- a/KfChatDotNetBot/Settings/BuiltIn.cs +++ b/KfChatDotNetBot/Settings/BuiltIn.cs @@ -424,6 +424,8 @@ public static class BuiltIn public static string KasinoPlanesRandomRiggeryEnabled = "Kasino.Planes.RandomRiggeryEnabled"; [BuiltInSetting("Array of forum IDs to guarantee riggery in Planes", SettingValueType.Array, "[]")] public static string KasinoPlanesTargetedRiggeryVictims = "Kasino.Planes.TargetedRiggeryVictims"; + [BuiltInSetting("Table of locks to look for before checking if someone is live / initiating a capture", SettingValueType.Complex, "{}")] + public static string CaptureLockTable = "Capture.LockTable"; } }