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
         {