From f7fb63c50fcd2fdef74927b3bfd5ca96d680ba19 Mon Sep 17 00:00:00 2001 From: Heavy Bob <ferrettclay@gmail.com> Date: Wed, 9 Apr 2025 23:53:07 +1000 Subject: [PATCH 1/4] Fixed /register-kill not using filtering. /register-kill didn't use the same filtering as /test resulting in users failing to get kills because url malformed. /test was also hanging app for response so fixed this. --- AutoTrackR2/App.xaml | 50 ++++++++ AutoTrackR2/ConfigPage.xaml | 85 ++++++++++++-- AutoTrackR2/ConfigPage.xaml.cs | 203 +++++++++++++++++++++++++-------- AutoTrackR2/MainWindow.xaml.cs | 8 ++ AutoTrackR2/WebHandler.cs | 7 +- AutoTrackR2/config.ini | 1 + 6 files changed, 301 insertions(+), 53 deletions(-) diff --git a/AutoTrackR2/App.xaml b/AutoTrackR2/App.xaml index b2f9f06..0717447 100644 --- a/AutoTrackR2/App.xaml +++ b/AutoTrackR2/App.xaml @@ -571,5 +571,55 @@ </Setter> </Style> + <!-- Standard Slider Style --> + <Style x:Key="SliderStyle" TargetType="Slider"> + <Setter Property="Height" Value="40"/> + <Setter Property="Width" Value="160"/> + <Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> + <Setter Property="Background" Value="{DynamicResource BackgroundDarkBrush}"/> + <Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}"/> + <Setter Property="BorderThickness" Value="2"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="Slider"> + <Grid Width="100" Height="30" HorizontalAlignment="Left"> + <!-- Track Background --> + <Border Background="{DynamicResource BackgroundDarkBrush}" + BorderBrush="{DynamicResource AccentBrush}" + BorderThickness="2" + CornerRadius="15" + Margin="0,0,-5,-4"/> + + <!-- Track --> + <Track x:Name="PART_Track"> + <Track.Thumb> + <Thumb x:Name="PART_Thumb" + Width="22" + Height="22" + Margin="6,4,1,0"> + <Thumb.Template> + <ControlTemplate TargetType="Thumb"> + <Ellipse Fill="{DynamicResource AccentBrush}"/> + </ControlTemplate> + </Thumb.Template> + </Thumb> + </Track.Thumb> + <Track.DecreaseRepeatButton> + <RepeatButton Background="Transparent" + BorderBrush="Transparent" + IsHitTestVisible="False"/> + </Track.DecreaseRepeatButton> + <Track.IncreaseRepeatButton> + <RepeatButton Background="Transparent" + BorderBrush="Transparent" + IsHitTestVisible="False"/> + </Track.IncreaseRepeatButton> + </Track> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + </Application.Resources> </Application> diff --git a/AutoTrackR2/ConfigPage.xaml b/AutoTrackR2/ConfigPage.xaml index 70567a9..b164f23 100644 --- a/AutoTrackR2/ConfigPage.xaml +++ b/AutoTrackR2/ConfigPage.xaml @@ -139,11 +139,15 @@ <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> </Grid.RowDefinitions> + <!-- Left Column Controls --> + <!-- Visor Wipe Toggle --> <StackPanel Grid.Column="0" Grid.Row="0" - Orientation="Horizontal" + Orientation="Horizontal" Margin="0,0,5,5"> <TextBlock Text="ⓘ" ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2." @@ -167,11 +171,11 @@ </StackPanel> <!-- Video Record Toggle --> - <StackPanel Grid.Column="1" Grid.Row="0" + <StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" - Margin="5,0,0,5"> + Margin="0,0,5,5"> <TextBlock Text="ⓘ" - ToolTip="Automatically clip your last kill. Check the README for more info." + ToolTip="Record video on player kill. Requires AHKv2." Foreground="{DynamicResource TextBrush}" FontSize="20" Margin="0,4,3,0"/> @@ -192,7 +196,7 @@ </StackPanel> <!-- Offline Mode Toggle --> - <StackPanel Grid.Column="0" Grid.Row="1" + <StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal" Margin="0,0,5,5"> <TextBlock Text="ⓘ" @@ -216,11 +220,78 @@ ValueChanged="OfflineModeSlider_ValueChanged"/> </StackPanel> + <!-- Right Column Controls --> + + <!-- Streamlink Toggle --> + <StackPanel Grid.Column="1" Grid.Row="0" + Orientation="Horizontal" + Margin="5,0,0,5"> + <TextBlock Text="ⓘ" + ToolTip="Use Streamlink for video recording." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Streamlink:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="StreamlinkSlider" + Minimum="0" + Maximum="1" + TickFrequency="1" + IsSnapToTickEnabled="True" + Value="0" + Style="{StaticResource ToggleSliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="StreamlinkSlider_ValueChanged"/> + </StackPanel> + + <!-- Streamlink Duration --> + <StackPanel Grid.Column="1" Grid.Row="1" + Orientation="Horizontal" + Margin="5,0,0,5"> + <TextBlock Text="ⓘ" + ToolTip="Duration of Streamlink recordings in seconds." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Duration:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="StreamlinkDurationSlider" + Minimum="10" + Maximum="300" + TickFrequency="10" + IsSnapToTickEnabled="True" + Value="30" + Style="{StaticResource SliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="StreamlinkDurationSlider_ValueChanged"/> + <TextBlock Name="StreamlinkDurationText" + Text="30s" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="5,7,0,0"/> + </StackPanel> + + <!-- Test Streamlink Button --> + <Button Grid.Column="1" Grid.Row="2" + Content="Test Streamlink" + Width="120" + Height="30" + FontFamily="{StaticResource Orbitron}" + Margin="5,0,0,5" + Style="{StaticResource ButtonStyle}" + Click="TestStreamlinkButton_Click"/> + <!-- Theme Slider --> - <StackPanel Grid.Column="0" Grid.Row="2" + <StackPanel Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="2" Orientation="Horizontal" - Margin="0,0,0,5"> + Margin="0,10,0,5"> <TextBlock Text="Theme:" Foreground="{DynamicResource TextBrush}" FontSize="16" diff --git a/AutoTrackR2/ConfigPage.xaml.cs b/AutoTrackR2/ConfigPage.xaml.cs index 1136f55..569041d 100644 --- a/AutoTrackR2/ConfigPage.xaml.cs +++ b/AutoTrackR2/ConfigPage.xaml.cs @@ -11,6 +11,7 @@ using System.Windows.Media; using System.Windows.Media.Effects; using System.Windows.Threading; using Microsoft.Win32; +using System.Threading.Tasks; namespace AutoTrackR2; @@ -37,6 +38,9 @@ public partial class ConfigPage : UserControl OfflineModeSlider.Value = ConfigManager.OfflineMode; ThemeSlider.Value = ConfigManager.Theme; + // Initialize Streamlink slider style + StreamlinkSlider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + ApplyToggleModeStyle(OfflineModeSlider.Value, VisorWipeSlider.Value, VideoRecordSlider.Value); const string themeJsonPath = "themes.json"; @@ -213,51 +217,6 @@ public partial class ConfigPage : UserControl } } - private void VisorWipeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - Slider slider = (Slider)sender; - - // Build the dynamic file path for the current user - if (string.IsNullOrEmpty(ConfigManager.AHKScriptFolder)) - { - MessageBox.Show("AHK script folder path is not configured.", "Configuration Error", MessageBoxButton.OK, MessageBoxImage.Warning); - return; - } - string filePath = Path.Combine( - ConfigManager.AHKScriptFolder, - "visorwipe.ahk" - ); - - // Get the current value of the slider (0 or 1) - ConfigManager.VisorWipe = (int)slider.Value; - - if (ConfigManager.VisorWipe == 1) - { - // Check if the file exists - if (File.Exists(filePath)) - { - // Apply the enabled style if the file exists - slider.Style = (Style)Application.Current.FindResource("ToggleSliderStyle"); - } - else - { - // File does not exist; revert the toggle to 0 - ConfigManager.VisorWipe = 0; - slider.Value = 0; // Revert the slider value - slider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); - - // Optionally, display a message to the user - MessageBox.Show($"Visor wipe script not found. Please ensure the file exists at:\n{filePath}", - "File Missing", MessageBoxButton.OK, MessageBoxImage.Warning); - } - } - else - { - // Apply the disabled style - slider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); - } - } - private void VideoRecordSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { Slider slider = (Slider)sender; @@ -315,6 +274,51 @@ public partial class ConfigPage : UserControl } } + private void VisorWipeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + Slider slider = (Slider)sender; + + // Build the dynamic file path for the current user + if (string.IsNullOrEmpty(ConfigManager.AHKScriptFolder)) + { + MessageBox.Show("AHK script folder path is not configured.", "Configuration Error", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + string filePath = Path.Combine( + ConfigManager.AHKScriptFolder, + "visorwipe.ahk" + ); + + // Get the current value of the slider (0 or 1) + ConfigManager.VisorWipe = (int)slider.Value; + + if (ConfigManager.VisorWipe == 1) + { + // Check if the file exists + if (File.Exists(filePath)) + { + // Apply the enabled style if the file exists + slider.Style = (Style)Application.Current.FindResource("ToggleSliderStyle"); + } + else + { + // File does not exist; revert the toggle to 0 + ConfigManager.VisorWipe = 0; + slider.Value = 0; // Revert the slider value + slider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + + // Optionally, display a message to the user + MessageBox.Show($"Visor wipe script not found. Please ensure the file exists at:\n{filePath}", + "File Missing", MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + else + { + // Apply the disabled style + slider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + } + } + private void SaveButton_Click(object sender, RoutedEventArgs e) { ConfigManager.ApiKey = ApiKey.Password; @@ -422,6 +426,115 @@ public partial class ConfigPage : UserControl MessageBox.Show($"API Test Failure. {ex.Message}"); } } + + private void StreamlinkSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + Slider slider = (Slider)sender; + ConfigManager.StreamlinkEnabled = (int)slider.Value; + + // Apply the appropriate style based on the value + if (slider.Value == 0) + { + slider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + } + else + { + slider.Style = (Style)Application.Current.FindResource("ToggleSliderStyle"); + } + } + + private void StreamlinkDurationSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (StreamlinkDurationText != null) + { + int duration = (int)e.NewValue; + StreamlinkDurationText.Text = $"{duration}s"; + ConfigManager.StreamlinkDuration = duration; + } + } + + private async void TestStreamlinkButton_Click(object sender, RoutedEventArgs e) + { + try + { + // Get the duration from the slider + int duration = (int)StreamlinkDurationSlider.Value; + + // Check if streamlink is installed + var versionInfo = new ProcessStartInfo + { + FileName = "streamlink", + Arguments = "--version", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + + using (var process = Process.Start(versionInfo)) + { + if (process == null) + { + MessageBox.Show("Streamlink is not installed. Please install Streamlink to use this feature.", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + string output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + if (process.ExitCode != 0) + { + MessageBox.Show("Streamlink is not installed or not working correctly. Please install Streamlink to use this feature.", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + } + + // Test recording functionality + var testUrl = "https://www.twitch.tv/starcitizen"; // Example URL for testing + var outputPath = Path.Combine( + ConfigManager.VideoPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), + "streamlink_test.mp4" + ); + + var recordInfo = new ProcessStartInfo + { + FileName = "streamlink", + Arguments = $"{testUrl} best -o {outputPath}", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + + using (var process = Process.Start(recordInfo)) + { + if (process == null) + { + MessageBox.Show("Failed to start test recording.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + // Wait for a short duration (5 seconds) to test recording + await Task.Delay(5000); + + try + { + if (!process.HasExited) + { + process.Kill(); + } + } + catch { } + } + + MessageBox.Show($"Streamlink test recording completed successfully.\nA 5-second test recording was saved to:\n{outputPath}", + "Success", MessageBoxButton.OK, MessageBoxImage.Information); + } + catch (Exception ex) + { + MessageBox.Show($"Error testing Streamlink: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } } public class Theme diff --git a/AutoTrackR2/MainWindow.xaml.cs b/AutoTrackR2/MainWindow.xaml.cs index 15f0722..2e95f60 100644 --- a/AutoTrackR2/MainWindow.xaml.cs +++ b/AutoTrackR2/MainWindow.xaml.cs @@ -196,6 +196,8 @@ namespace AutoTrackR2 public static int VideoRecord { get; set; } public static int OfflineMode { get; set; } public static int Theme { get; set; } + public static int StreamlinkEnabled { get; set; } + public static int StreamlinkDuration { get; set; } = 30; static ConfigManager() { @@ -247,6 +249,10 @@ namespace AutoTrackR2 OfflineMode = int.Parse(line.Substring("OfflineMode=".Length).Trim()); else if (line.StartsWith("Theme=")) Theme = int.Parse(line.Substring("Theme=".Length).Trim()); + else if (line.StartsWith("StreamlinkEnabled=")) + StreamlinkEnabled = int.Parse(line.Substring("StreamlinkEnabled=".Length).Trim()); + else if (line.StartsWith("StreamlinkDuration=")) + StreamlinkDuration = int.Parse(line.Substring("StreamlinkDuration=".Length).Trim()); } } } @@ -278,6 +284,8 @@ namespace AutoTrackR2 writer.WriteLine($"VideoRecord={VideoRecord}"); writer.WriteLine($"OfflineMode={OfflineMode}"); writer.WriteLine($"Theme={Theme}"); + writer.WriteLine($"StreamlinkEnabled={StreamlinkEnabled}"); + writer.WriteLine($"StreamlinkDuration={StreamlinkDuration}"); } } } diff --git a/AutoTrackR2/WebHandler.cs b/AutoTrackR2/WebHandler.cs index e53e809..fd2b646 100644 --- a/AutoTrackR2/WebHandler.cs +++ b/AutoTrackR2/WebHandler.cs @@ -155,7 +155,12 @@ public static class WebHandler Console.WriteLine($"Time (UTC): {DateTimeOffset.UtcNow}"); Console.WriteLine("=== End Debug Info ===\n"); - var response = await httpClient.PostAsync(ConfigManager.ApiUrl + "register-kill", new StringContent(jsonData, Encoding.UTF8, "application/json")); + // Ensure proper URL formatting + string baseUrl = Regex.Replace(ConfigManager.ApiUrl ?? "", @"(https?://[^/]+)/?.*", "$1"); + string endpoint = "register-kill"; + string fullUrl = $"{baseUrl}/{endpoint}"; + + var response = await httpClient.PostAsync(fullUrl, new StringContent(jsonData, Encoding.UTF8, "application/json")); if (response.StatusCode != HttpStatusCode.OK) { Console.WriteLine("Failed to submit kill data:"); diff --git a/AutoTrackR2/config.ini b/AutoTrackR2/config.ini index 0f01f82..b661bb4 100644 --- a/AutoTrackR2/config.ini +++ b/AutoTrackR2/config.ini @@ -6,3 +6,4 @@ VisorWipe=0 VideoRecord=0 OfflineMode=0 Theme=0 +StreamlinkEnabled=0 From d7057efe1561dd016c5ddb0c9db7557a17200f2c Mon Sep 17 00:00:00 2001 From: Heavy Bob <ferrettclay@gmail.com> Date: Thu, 10 Apr 2025 01:55:35 +1000 Subject: [PATCH 2/4] Streamlink - Almost done. --- AutoTrackR2/App.xaml.cs | 2 + AutoTrackR2/ConfigPage.xaml | 407 ++++++++++++++------------- AutoTrackR2/ConfigPage.xaml.cs | 124 ++++---- AutoTrackR2/StreamlinkHandler.cs | 88 ++++++ AutoTrackR2/TrackREventDispatcher.cs | 7 + AutoTrackR2/WebHandler.cs | 21 +- 6 files changed, 385 insertions(+), 264 deletions(-) create mode 100644 AutoTrackR2/StreamlinkHandler.cs diff --git a/AutoTrackR2/App.xaml.cs b/AutoTrackR2/App.xaml.cs index e84d792..e9a45e7 100644 --- a/AutoTrackR2/App.xaml.cs +++ b/AutoTrackR2/App.xaml.cs @@ -19,6 +19,7 @@ namespace AutoTrackR2 "AutoTrackR2", "crash.log" ); + private StreamlinkHandler? _streamlinkHandler; protected override void OnStartup(StartupEventArgs e) { @@ -43,6 +44,7 @@ namespace AutoTrackR2 } base.OnStartup(e); + _streamlinkHandler = new StreamlinkHandler(); } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) diff --git a/AutoTrackR2/ConfigPage.xaml b/AutoTrackR2/ConfigPage.xaml index b164f23..2ecc235 100644 --- a/AutoTrackR2/ConfigPage.xaml +++ b/AutoTrackR2/ConfigPage.xaml @@ -2,14 +2,13 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="410" - Width="626"> + Width="700"> <Grid Background="{DynamicResource BackgroundLightBrush}"> <!-- Main Layout Grid --> - <Grid Margin="0,0,5,7"> + <Grid> <Grid.RowDefinitions> - <!-- One row for the content, the other for buttons --> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> @@ -22,9 +21,9 @@ </Grid.ColumnDefinitions> <!-- Section for Config Fields --> - <StackPanel Grid.Column="0" - VerticalAlignment="Center" - Height="389"> + <StackPanel Grid.Row="0" + VerticalAlignment="Top" + Margin="20,10,20,0"> <!-- Log File --> <StackPanel Margin="0,5,0,10" Orientation="Horizontal"> @@ -38,11 +37,11 @@ FontSize="16" Margin="0,5,0,5" FontFamily="{StaticResource Roboto}"/> - <StackPanel Orientation="Horizontal" - Margin="30,0,0,0"> + <StackPanel Orientation="Horizontal"> <TextBox Name="LogFilePath" - Width="330" + Width="260" Height="30" + Margin="10,0,0,0" Style="{StaticResource RoundedTextBox}"/> <Button Content="Browse" Width="75" @@ -51,6 +50,13 @@ Margin="5,0" Style="{StaticResource ButtonStyle}" Click="LogFileBrowseButton_Click"/> + <Button Content="Open" + Width="75" + Height="30" + FontFamily="{StaticResource Orbitron}" + Margin="5,0" + Style="{StaticResource ButtonStyle}" + Click="LogFileOpenButton_Click"/> </StackPanel> </StackPanel> @@ -66,11 +72,11 @@ Foreground="{DynamicResource TextBrush}" FontSize="16" Margin="0,5,0,5"/> - <StackPanel Orientation="Horizontal" - Margin="30,0,0,0"> + <StackPanel Orientation="Horizontal"> <TextBox Name="ApiUrl" - Width="330" + Width="260" Height="30" + Margin="10,0,0,0" Style="{StaticResource RoundedTextBox}"/> <Button Content="Test" Width="75" @@ -95,9 +101,9 @@ FontSize="16" Margin="0,5,0,5"/> <PasswordBox Name="ApiKey" - Width="330" + Width="260" Height="30" - Margin="33,0,0,0" + Margin="10,0,0,0" Style="{StaticResource RoundedPasswordBox}"/> </StackPanel> @@ -115,7 +121,7 @@ Margin="0,5,0,5"/> <StackPanel Orientation="Horizontal"> <TextBox Name="VideoPath" - Width="330" + Width="260" Height="30" Margin="10,0,0,0" Style="{StaticResource RoundedTextBox}"/> @@ -126,201 +132,214 @@ Margin="5,0" Style="{StaticResource ButtonStyle}" Click="VideoPathBrowseButton_Click"/> + <Button Content="Open" + Width="75" + Height="30" + FontFamily="{StaticResource Orbitron}" + Margin="5,0" + Style="{StaticResource ButtonStyle}" + Click="VideoPathOpenButton_Click"/> </StackPanel> </StackPanel> <!-- Toggle Settings Grid --> <Grid Margin="0,0,0,10"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*"/> - <ColumnDefinition Width="*"/> - </Grid.ColumnDefinitions> - <Grid.RowDefinitions> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - <RowDefinition Height="Auto"/> - </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> - <!-- Left Column Controls --> - - <!-- Visor Wipe Toggle --> - <StackPanel Grid.Column="0" Grid.Row="0" - Orientation="Horizontal" - Margin="0,0,5,5"> - <TextBlock Text="ⓘ" - ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2." - Foreground="{DynamicResource TextBrush}" - FontSize="20" - Margin="0,4,3,0"/> - <TextBlock Text="Visor Wipe:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,0,0"/> - <Slider Name="VisorWipeSlider" - Minimum="0" - Maximum="1" - TickFrequency="1" - IsSnapToTickEnabled="True" - Value="0" - Style="{StaticResource ToggleSliderStyle}" - Margin="10,-4,0,0" - Width="100" - ValueChanged="VisorWipeSlider_ValueChanged"/> - </StackPanel> + <!-- Left Column Controls --> - <!-- Video Record Toggle --> - <StackPanel Grid.Column="0" Grid.Row="1" - Orientation="Horizontal" - Margin="0,0,5,5"> - <TextBlock Text="ⓘ" - ToolTip="Record video on player kill. Requires AHKv2." - Foreground="{DynamicResource TextBrush}" - FontSize="20" - Margin="0,4,3,0"/> - <TextBlock Text="Video Record:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,0,0"/> - <Slider Name="VideoRecordSlider" - Minimum="0" - Maximum="1" - TickFrequency="1" - IsSnapToTickEnabled="True" - Value="0" - Style="{StaticResource ToggleSliderStyle}" - Margin="10,-4,0,0" - Width="100" - ValueChanged="VideoRecordSlider_ValueChanged"/> - </StackPanel> + <!-- Visor Wipe Toggle --> + <StackPanel Grid.Column="0" + Grid.Row="0" + Orientation="Horizontal" + Margin="0,0,5,5"> + <TextBlock Text="ⓘ" + ToolTip="Perform a Visor Wipe animation on player kill. Requires AHKv2." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Visor Wipe:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="VisorWipeSlider" + Minimum="0" + Maximum="1" + TickFrequency="1" + IsSnapToTickEnabled="True" + Value="0" + Style="{StaticResource ToggleSliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="VisorWipeSlider_ValueChanged"/> + </StackPanel> - <!-- Offline Mode Toggle --> - <StackPanel Grid.Column="0" Grid.Row="2" - Orientation="Horizontal" - Margin="0,0,5,5"> - <TextBlock Text="ⓘ" - ToolTip="With Offline Mode enabled, kills will not be submitted to the configured API." - Foreground="{DynamicResource TextBrush}" - FontSize="20" - Margin="0,4,3,0"/> - <TextBlock Text="Offline Mode:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,0,0"/> - <Slider Name="OfflineModeSlider" - Minimum="0" - Maximum="1" - TickFrequency="1" - IsSnapToTickEnabled="True" - Value="0" - Style="{StaticResource ToggleSliderStyle}" - Margin="10,-4,0,0" - Width="100" - ValueChanged="OfflineModeSlider_ValueChanged"/> - </StackPanel> + <!-- Video Record Toggle --> + <StackPanel Grid.Column="0" + Grid.Row="1" + Orientation="Horizontal" + Margin="0,0,5,5"> + <TextBlock Text="ⓘ" + ToolTip="Record video on player kill. Requires AHKv2." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Video Record:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="VideoRecordSlider" + Minimum="0" + Maximum="1" + TickFrequency="1" + IsSnapToTickEnabled="True" + Value="0" + Style="{StaticResource ToggleSliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="VideoRecordSlider_ValueChanged"/> + </StackPanel> - <!-- Right Column Controls --> - - <!-- Streamlink Toggle --> - <StackPanel Grid.Column="1" Grid.Row="0" - Orientation="Horizontal" - Margin="5,0,0,5"> - <TextBlock Text="ⓘ" - ToolTip="Use Streamlink for video recording." - Foreground="{DynamicResource TextBrush}" - FontSize="20" - Margin="0,4,3,0"/> - <TextBlock Text="Streamlink:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,0,0"/> - <Slider Name="StreamlinkSlider" - Minimum="0" - Maximum="1" - TickFrequency="1" - IsSnapToTickEnabled="True" - Value="0" - Style="{StaticResource ToggleSliderStyle}" - Margin="10,-4,0,0" - Width="100" - ValueChanged="StreamlinkSlider_ValueChanged"/> - </StackPanel> + <!-- Offline Mode Toggle --> + <StackPanel Grid.Column="0" + Grid.Row="2" + Orientation="Horizontal" + Margin="0,0,5,5"> + <TextBlock Text="ⓘ" + ToolTip="With Offline Mode enabled, kills will not be submitted to the configured API." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Offline Mode:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="OfflineModeSlider" + Minimum="0" + Maximum="1" + TickFrequency="1" + IsSnapToTickEnabled="True" + Value="0" + Style="{StaticResource ToggleSliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="OfflineModeSlider_ValueChanged"/> + </StackPanel> - <!-- Streamlink Duration --> - <StackPanel Grid.Column="1" Grid.Row="1" - Orientation="Horizontal" - Margin="5,0,0,5"> - <TextBlock Text="ⓘ" - ToolTip="Duration of Streamlink recordings in seconds." - Foreground="{DynamicResource TextBrush}" - FontSize="20" - Margin="0,4,3,0"/> - <TextBlock Text="Duration:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,0,0"/> - <Slider Name="StreamlinkDurationSlider" - Minimum="10" - Maximum="300" - TickFrequency="10" - IsSnapToTickEnabled="True" - Value="30" - Style="{StaticResource SliderStyle}" - Margin="10,-4,0,0" - Width="100" - ValueChanged="StreamlinkDurationSlider_ValueChanged"/> - <TextBlock Name="StreamlinkDurationText" - Text="30s" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="5,7,0,0"/> - </StackPanel> + <!-- Right Column Controls --> - <!-- Test Streamlink Button --> - <Button Grid.Column="1" Grid.Row="2" - Content="Test Streamlink" - Width="120" - Height="30" - FontFamily="{StaticResource Orbitron}" - Margin="5,0,0,5" - Style="{StaticResource ButtonStyle}" - Click="TestStreamlinkButton_Click"/> + <!-- Streamlink Toggle --> + <StackPanel Grid.Column="1" + Grid.Row="0" + Orientation="Horizontal" + Margin="5,0,0,5"> + <TextBlock Text="ⓘ" + ToolTip="Use Streamlink for video recording." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Streamlink:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="StreamlinkSlider" + Minimum="0" + Maximum="1" + TickFrequency="1" + IsSnapToTickEnabled="True" + Value="0" + Style="{StaticResource ToggleSliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="StreamlinkSlider_ValueChanged"/> + </StackPanel> - <!-- Theme Slider --> - <StackPanel Grid.Column="0" Grid.Row="4" - Grid.ColumnSpan="2" - Orientation="Horizontal" - Margin="0,10,0,5"> - <TextBlock Text="Theme:" - Foreground="{DynamicResource TextBrush}" - FontSize="16" - Margin="0,7,10,0"/> - <Slider x:Name="ThemeSlider" - Minimum="0" - Value="0" - TickFrequency="1" - IsSnapToTickEnabled="True" - ValueChanged="ThemeSlider_ValueChanged" - Width="447" - Style="{StaticResource ThreePositionSlider}"/> - </StackPanel> + <!-- Streamlink Duration --> + <StackPanel Grid.Column="1" + Grid.Row="1" + Orientation="Horizontal" + Margin="5,0,0,5"> + <TextBlock Text="ⓘ" + ToolTip="Duration of Streamlink recordings in seconds." + Foreground="{DynamicResource TextBrush}" + FontSize="20" + Margin="0,4,3,0"/> + <TextBlock Text="Duration:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,0,0"/> + <Slider Name="StreamlinkDurationSlider" + Minimum="10" + Maximum="600" + TickFrequency="30" + IsSnapToTickEnabled="True" + Value="30" + Style="{StaticResource SliderStyle}" + Margin="10,-4,0,0" + Width="100" + ValueChanged="StreamlinkDurationSlider_ValueChanged"/> + <TextBlock Name="StreamlinkDurationText" + Text="30s" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="5,7,0,0"/> + </StackPanel> + + <!-- Test Streamlink Button --> + <Button Grid.Column="1" + Grid.Row="2" + Content="Test Streamlink" + Width="120" + Height="30" + FontFamily="{StaticResource Orbitron}" + Margin="5,0,0,5" + Style="{StaticResource ButtonStyle}" + Click="TestStreamlinkButton_Click"/> + + <!-- Theme Slider --> + <StackPanel Grid.Column="0" + Grid.Row="3" + Grid.ColumnSpan="2" + Orientation="Horizontal" + Margin="0,0,0,5"> + <TextBlock Text="Theme:" + Foreground="{DynamicResource TextBrush}" + FontSize="16" + Margin="0,7,10,0"/> + <Slider x:Name="ThemeSlider" + Minimum="0" + Value="0" + TickFrequency="1" + IsSnapToTickEnabled="True" + ValueChanged="ThemeSlider_ValueChanged" + Width="350" + Style="{StaticResource ThreePositionSlider}"/> + </StackPanel> </Grid> </StackPanel> <!-- Save Button --> - <StackPanel Grid.Column="2" - HorizontalAlignment="Right" - VerticalAlignment="Bottom" - Margin="0,0,0,10"> - <Button x:Name="SaveButton" - Content="Save" - Width="100" - Height="40" - Style="{StaticResource ButtonStyle}" - FontFamily="{StaticResource Orbitron}" - Click="SaveButton_Click"/> - </StackPanel> + <Button x:Name="SaveButton" + Grid.Row="1" + Content="Save" + Width="100" + Height="40" + Style="{StaticResource ButtonStyle}" + FontFamily="{StaticResource Orbitron}" + HorizontalAlignment="Right" + VerticalAlignment="Bottom" + Margin="0,0,80,20" + Click="SaveButton_Click"/> </Grid> </Grid> </UserControl> diff --git a/AutoTrackR2/ConfigPage.xaml.cs b/AutoTrackR2/ConfigPage.xaml.cs index 569041d..284f835 100644 --- a/AutoTrackR2/ConfigPage.xaml.cs +++ b/AutoTrackR2/ConfigPage.xaml.cs @@ -37,9 +37,18 @@ public partial class ConfigPage : UserControl VideoRecordSlider.Value = ConfigManager.VideoRecord; OfflineModeSlider.Value = ConfigManager.OfflineMode; ThemeSlider.Value = ConfigManager.Theme; + StreamlinkSlider.Value = ConfigManager.StreamlinkEnabled; + StreamlinkDurationSlider.Value = ConfigManager.StreamlinkDuration; // Initialize Streamlink slider style - StreamlinkSlider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + if (StreamlinkSlider.Value == 0) + { + StreamlinkSlider.Style = (Style)Application.Current.FindResource("FalseToggleStyle"); + } + else + { + StreamlinkSlider.Style = (Style)Application.Current.FindResource("ToggleSliderStyle"); + } ApplyToggleModeStyle(OfflineModeSlider.Value, VisorWipeSlider.Value, VideoRecordSlider.Value); @@ -430,6 +439,19 @@ public partial class ConfigPage : UserControl private void StreamlinkSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { Slider slider = (Slider)sender; + + // Only allow enabling if streamlink is installed + if (slider.Value == 1) + { + if (!StreamlinkHandler.IsStreamlinkInstalled()) + { + MessageBox.Show("Streamlink is not installed. Please install Streamlink to use this feature.", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + slider.Value = 0; + return; + } + } + ConfigManager.StreamlinkEnabled = (int)slider.Value; // Apply the appropriate style based on the value @@ -459,82 +481,46 @@ public partial class ConfigPage : UserControl { // Get the duration from the slider int duration = (int)StreamlinkDurationSlider.Value; - - // Check if streamlink is installed - var versionInfo = new ProcessStartInfo - { - FileName = "streamlink", - Arguments = "--version", - UseShellExecute = false, - RedirectStandardOutput = true, - CreateNoWindow = true - }; - using (var process = Process.Start(versionInfo)) - { - if (process == null) - { - MessageBox.Show("Streamlink is not installed. Please install Streamlink to use this feature.", - "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } + // Calculate the total duration (30 seconds before + configured duration) + int totalDuration = 30 + duration; - string output = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - - if (process.ExitCode != 0) - { - MessageBox.Show("Streamlink is not installed or not working correctly. Please install Streamlink to use this feature.", - "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - } - - // Test recording functionality - var testUrl = "https://www.twitch.tv/starcitizen"; // Example URL for testing - var outputPath = Path.Combine( - ConfigManager.VideoPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), - "streamlink_test.mp4" - ); - - var recordInfo = new ProcessStartInfo - { - FileName = "streamlink", - Arguments = $"{testUrl} best -o {outputPath}", - UseShellExecute = false, - RedirectStandardOutput = true, - CreateNoWindow = true - }; - - using (var process = Process.Start(recordInfo)) - { - if (process == null) - { - MessageBox.Show("Failed to start test recording.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - - // Wait for a short duration (5 seconds) to test recording - await Task.Delay(5000); - - try - { - if (!process.HasExited) - { - process.Kill(); - } - } - catch { } - } - - MessageBox.Show($"Streamlink test recording completed successfully.\nA 5-second test recording was saved to:\n{outputPath}", - "Success", MessageBoxButton.OK, MessageBoxImage.Information); + // Test recording functionality, we're using spacecutlet cause need a good tester. + TrackREventDispatcher.OnStreamlinkRecordEvent("spacecutlet"); } catch (Exception ex) { MessageBox.Show($"Error testing Streamlink: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } + + private void LogFileOpenButton_Click(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrEmpty(LogFilePath.Text)) + { + string directory = Path.GetDirectoryName(LogFilePath.Text) ?? string.Empty; + if (Directory.Exists(directory)) + { + Process.Start("explorer.exe", directory); + } + else + { + MessageBox.Show("Directory does not exist.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void VideoPathOpenButton_Click(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrEmpty(VideoPath.Text) && Directory.Exists(VideoPath.Text)) + { + Process.Start("explorer.exe", VideoPath.Text); + } + else + { + MessageBox.Show("Directory does not exist.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } } public class Theme diff --git a/AutoTrackR2/StreamlinkHandler.cs b/AutoTrackR2/StreamlinkHandler.cs new file mode 100644 index 0000000..f384d61 --- /dev/null +++ b/AutoTrackR2/StreamlinkHandler.cs @@ -0,0 +1,88 @@ +using System.Diagnostics; +using System.IO; + +namespace AutoTrackR2; + +public class StreamlinkHandler +{ + public StreamlinkHandler() + { + TrackREventDispatcher.StreamlinkRecordEvent += HandleStreamlinkRecord; + } + + public static bool IsStreamlinkInstalled() + { + try + { + var checkInfo = new ProcessStartInfo + { + FileName = "where", + Arguments = "streamlink", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + + using (var process = Process.Start(checkInfo)) + { + if (process == null) + { + return false; + } + + string output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + return process.ExitCode == 0 && !string.IsNullOrEmpty(output); + } + } + catch + { + return false; + } + } + + private async void HandleStreamlinkRecord(string streamerHandle) + { + if (ConfigManager.StreamlinkEnabled != 1) + { + return; + } + + try + { + var outputPath = Path.Combine( + ConfigManager.VideoPath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), + $"kill_{DateTime.Now:yyyyMMdd_HHmmss}.mp4" + ); + + // Calculate the duration for recording (30 seconds before + configured duration after) + var totalDuration = 30 + ConfigManager.StreamlinkDuration; + + var recordInfo = new ProcessStartInfo + { + FileName = "streamlink", + Arguments = $"https://www.twitch.tv/{streamerHandle} best --hls-live-edge 30 --hls-duration {totalDuration} -o {outputPath}", + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + + using (var process = Process.Start(recordInfo)) + { + if (process == null) + { + return; + } + + // Wait for the recording to complete + await process.WaitForExitAsync(); + } + } + catch (Exception ex) + { + // Log the error but don't crash the application + Debug.WriteLine($"Error recording streamlink clip: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/AutoTrackR2/TrackREventDispatcher.cs b/AutoTrackR2/TrackREventDispatcher.cs index d5dc3ae..36823ac 100644 --- a/AutoTrackR2/TrackREventDispatcher.cs +++ b/AutoTrackR2/TrackREventDispatcher.cs @@ -54,4 +54,11 @@ public static class TrackREventDispatcher { JumpDriveStateChangedEvent?.Invoke(data); } + + public static event Action<string>? StreamlinkRecordEvent; + + public static void OnStreamlinkRecordEvent(string streamerHandle) + { + StreamlinkRecordEvent?.Invoke(streamerHandle); + } } \ No newline at end of file diff --git a/AutoTrackR2/WebHandler.cs b/AutoTrackR2/WebHandler.cs index fd2b646..c18d440 100644 --- a/AutoTrackR2/WebHandler.cs +++ b/AutoTrackR2/WebHandler.cs @@ -172,7 +172,26 @@ public static class WebHandler else if (response.StatusCode == HttpStatusCode.OK) { Console.WriteLine("Successfully submitted kill data"); - Console.WriteLine($"Response: {await response.Content.ReadAsStringAsync()}"); + var responseContent = await response.Content.ReadAsStringAsync(); + Console.WriteLine($"Response: {responseContent}"); + + // Check if the response contains a streamer field + try + { + var responseData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(responseContent); + if (responseData != null && responseData.TryGetValue("streamer", out JsonElement streamerElement)) + { + string streamerHandle = streamerElement.GetString() ?? string.Empty; + if (!string.IsNullOrEmpty(streamerHandle)) + { + TrackREventDispatcher.OnStreamlinkRecordEvent(streamerHandle); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing streamer from response: {ex.Message}"); + } } } } \ No newline at end of file From 5994743c73616afbb9219cfc9d3b8b81dc5ee843 Mon Sep 17 00:00:00 2001 From: Heavy Bob <ferrettclay@gmail.com> Date: Thu, 10 Apr 2025 02:46:35 +1000 Subject: [PATCH 3/4] Added checks for streamlink response for web handler I've also put in a check to make sure if we are going to accept a response from the api that it it's valid. --- AutoTrackR2/WebHandler.cs | 54 ++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/AutoTrackR2/WebHandler.cs b/AutoTrackR2/WebHandler.cs index c18d440..b05a1a8 100644 --- a/AutoTrackR2/WebHandler.cs +++ b/AutoTrackR2/WebHandler.cs @@ -175,23 +175,47 @@ public static class WebHandler var responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine($"Response: {responseContent}"); - // Check if the response contains a streamer field - try + // Only process streamer data if streamlink is enabled + if (ConfigManager.StreamlinkEnabled == 1) { - var responseData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(responseContent); - if (responseData != null && responseData.TryGetValue("streamer", out JsonElement streamerElement)) - { - string streamerHandle = streamerElement.GetString() ?? string.Empty; - if (!string.IsNullOrEmpty(streamerHandle)) - { - TrackREventDispatcher.OnStreamlinkRecordEvent(streamerHandle); - } - } - } - catch (Exception ex) - { - Console.WriteLine($"Error parsing streamer from response: {ex.Message}"); + ProcessStreamerResponse(responseContent); } } } + + private static void ProcessStreamerResponse(string responseContent) + { + try + { + var responseData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(responseContent); + if (responseData != null && responseData.TryGetValue("streamer", out JsonElement streamerElement)) + { + string streamerHandle = streamerElement.GetString() ?? string.Empty; + if (!string.IsNullOrEmpty(streamerHandle)) + { + // Sanitize the streamer handle before using it, this is to prevent any malicious instructions. + string sanitizedHandle = SanitizeStreamerHandle(streamerHandle); + if (!string.IsNullOrEmpty(sanitizedHandle)) + { + TrackREventDispatcher.OnStreamlinkRecordEvent(sanitizedHandle); + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing streamer from response: {ex.Message}"); + } + } + + private static string SanitizeStreamerHandle(string handle) + { + // Twitch usernames 4-25 characters, letters, numbers and underscores. + if (Regex.IsMatch(handle, @"^[a-zA-Z0-9_]{4,25}$")) + { + return handle.ToLower(); // Api won't return anything other than lowercase but just in case. + } + + return string.Empty; // Reject invalid handles + } } \ No newline at end of file From b448cfec3ca891d3ae415f84e811ad587da67729 Mon Sep 17 00:00:00 2001 From: Heavy Bob <ferrettclay@gmail.com> Date: Thu, 10 Apr 2025 03:16:42 +1000 Subject: [PATCH 4/4] Point streamlink test to /test Streamlink tests now throw at the /test api and expect a streamer variable, if nothing then it will succeed. Think the streamer link stuff is mostly done, just need to do the changes on the api. --- AutoTrackR2/ConfigPage.xaml.cs | 55 +++++++++++++++++++++++++++++----- AutoTrackR2/WebHandler.cs | 2 +- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/AutoTrackR2/ConfigPage.xaml.cs b/AutoTrackR2/ConfigPage.xaml.cs index 284f835..e4c1772 100644 --- a/AutoTrackR2/ConfigPage.xaml.cs +++ b/AutoTrackR2/ConfigPage.xaml.cs @@ -477,20 +477,61 @@ public partial class ConfigPage : UserControl private async void TestStreamlinkButton_Click(object sender, RoutedEventArgs e) { + Debug.WriteLine("TestStreamlinkButton_Click: Starting streamlink test"); + + if (ConfigManager.StreamlinkEnabled != 1) + { + Debug.WriteLine("TestStreamlinkButton_Click: Streamlink is not enabled"); + MessageBox.Show("Streamlink is not enabled. Please enable Streamlink to test.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + try { - // Get the duration from the slider - int duration = (int)StreamlinkDurationSlider.Value; + Debug.WriteLine("TestStreamlinkButton_Click: Configuring HTTP client"); + // Configure HttpClient with TLS 1.2 + var handler = new HttpClientHandler + { + SslProtocols = System.Security.Authentication.SslProtocols.Tls12 + }; - // Calculate the total duration (30 seconds before + configured duration) - int totalDuration = 30 + duration; + using (var client = new HttpClient(handler)) + { + // Set headers + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ConfigManager.ApiKey); + client.DefaultRequestHeaders.UserAgent.ParseAdd("AutoTrackR2"); - // Test recording functionality, we're using spacecutlet cause need a good tester. - TrackREventDispatcher.OnStreamlinkRecordEvent("spacecutlet"); + // Create JSON body with version + var jsonBody = new { version = "2.10" }; + var content = new StringContent(JsonSerializer.Serialize(jsonBody), Encoding.UTF8, "application/json"); + + // Send POST to test endpoint + string baseUrl = Regex.Replace(ConfigManager.ApiUrl ?? "", @"(https?://[^/]+)/?.*", "$1"); + string endpoint = "test"; + string fullUrl = $"{baseUrl}/{endpoint}"; + Debug.WriteLine($"TestStreamlinkButton_Click: Sending request to {fullUrl}"); + + var response = await client.PostAsync(fullUrl, content); + Debug.WriteLine($"TestStreamlinkButton_Click: Response status code: {response.StatusCode}"); + + if (response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + Debug.WriteLine($"TestStreamlinkButton_Click: Response content: {responseContent}"); + WebHandler.ProcessStreamerResponse(responseContent); + MessageBox.Show("Streamlink Test Success."); + } + else + { + Debug.WriteLine($"TestStreamlinkButton_Click: Error response: {response.StatusCode} - {response.ReasonPhrase}"); + MessageBox.Show($"Error: {response.StatusCode} - {response.ReasonPhrase}"); + } + } } catch (Exception ex) { - MessageBox.Show($"Error testing Streamlink: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + Debug.WriteLine($"TestStreamlinkButton_Click: Exception: {ex.Message}"); + MessageBox.Show($"Streamlink Test Failure. {ex.Message}"); } } diff --git a/AutoTrackR2/WebHandler.cs b/AutoTrackR2/WebHandler.cs index b05a1a8..7015282 100644 --- a/AutoTrackR2/WebHandler.cs +++ b/AutoTrackR2/WebHandler.cs @@ -183,7 +183,7 @@ public static class WebHandler } } - private static void ProcessStreamerResponse(string responseContent) + public static void ProcessStreamerResponse(string responseContent) { try {