using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using KfChatDotNetBot.Models.DbModels; using KfChatDotNetBot.Settings; using NLog; namespace KfChatDotNetBot.Services; public class Owncast(ChatBot kfChatBot) : IDisposable { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private Task? _liveStatusCheckTask; private CancellationTokenSource _liveStatusCheckTaskCts = new(); public void StartLiveStatusCheck() { _liveStatusCheckTaskCts = new CancellationTokenSource(); _liveStatusCheckTask = Task.Run(LiveStatusCheckTask, _liveStatusCheckTaskCts.Token); } private async Task LiveStatusCheckTask() { var interval = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.OwncastCheckInterval)).ToType(); using var timer = new PeriodicTimer(TimeSpan.FromSeconds(interval)); while (await timer.WaitForNextTickAsync(_liveStatusCheckTaskCts.Token)) { var ct = _liveStatusCheckTaskCts.Token; _logger.Debug("Going to check if anyone is live on Owncast now"); var persistedLive = (await SettingsProvider.GetValueAsync(BuiltIn.Keys.OwncastPersistedCurrentlyLive)) .ToBoolean(); OwncastStatusModel status; try { status = await GetLiveStatus(ct); } catch (Exception e) { _logger.Error("Caught an error while trying to get the currently live streams from Owncast"); _logger.Error(e); continue; } if (status.Online == persistedLive) continue; await SettingsProvider.SetValueAsBooleanAsync(BuiltIn.Keys.OwncastPersistedCurrentlyLive, status.Online); if (!status.Online) continue; await kfChatBot.SendChatMessageAsync("https://bossmanjack.tv restream is live!", true); if (!(await SettingsProvider.GetValueAsync(BuiltIn.Keys.CaptureEnabled)).ToBoolean()) continue; _ = new StreamCapture("https://bossmanjack.tv/hls/stream.m3u8", StreamCaptureMethods.Streamlink, new CaptureOverridesModel { CaptureStreamlinkOutputFormat = "bossmanjacktv-{time:%Y-%m-%d_%Hh%Mm%Ss}.ts" }, ct).CaptureAsync(); } } public static async Task GetLiveStatus(CancellationToken ct = default) { var logger = LogManager.GetCurrentClassLogger(); var proxy = await SettingsProvider.GetValueAsync(BuiltIn.Keys.Proxy); var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All, }; if (proxy.Value != null) { handler.Proxy = new WebProxy(proxy.Value); handler.UseProxy = true; logger.Debug($"Set proxy for the Owncast API request to {proxy.Value}"); } using var client = new HttpClient(handler); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await client.GetAsync("https://bossmanjack.tv/api/status", ct); var content = await response.Content.ReadFromJsonAsync(cancellationToken: ct); logger.Debug("Owncast endpoint returned the following JSON"); logger.Debug(content.GetRawText); return content.Deserialize() ?? throw new InvalidOperationException(); } public void Dispose() { _liveStatusCheckTaskCts.Cancel(); _liveStatusCheckTask?.Dispose(); GC.SuppressFinalize(this); } } public class OwncastStatusModel { [JsonPropertyName("online")] public required bool Online { get; set; } }