From bf34082d24d86b04975f2023260a44f1f1774791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 20:16:47 +0000 Subject: [PATCH 1/6] Initial plan From 48c04e5722ab91c06c2f28078f7b27521fc1e1de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 20:21:22 +0000 Subject: [PATCH 2/6] Implement architecture-aware FFmpeg download for ARM64 support Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com> --- SentryReplay.Tests/PackageManagerTests.cs | 29 +++++++++++++++++++++++ SentryReplay/PackageManager.cs | 15 +++++++++++- TeslaCam/PackageManager.cs | 15 +++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 SentryReplay.Tests/PackageManagerTests.cs diff --git a/SentryReplay.Tests/PackageManagerTests.cs b/SentryReplay.Tests/PackageManagerTests.cs new file mode 100644 index 0000000..230095f --- /dev/null +++ b/SentryReplay.Tests/PackageManagerTests.cs @@ -0,0 +1,29 @@ +using System.Runtime.InteropServices; +using Shouldly; + +namespace SentryReplay.Tests; + +/// +/// Tests for PackageManager functionality. +/// +public class PackageManagerTests +{ + [Fact] + public void GetFFmpegDownloadUrl_ReturnsValidUrl() + { + // We can't directly test the private method, but we can verify the logic indirectly + // by checking that the current architecture is supported + var architecture = RuntimeInformation.ProcessArchitecture; + + // Verify that the architecture is one we support + architecture.ShouldBeOneOf(Architecture.X64, Architecture.Arm64, Architecture.X86, Architecture.Arm); + } + + [Fact] + public void FindFFmpegDirectories_ReturnsEnumerable() + { + // Test that the method returns an enumerable (even if empty) + var directories = PackageManager.FindFFmpegDirectories("."); + directories.ShouldNotBeNull(); + } +} diff --git a/SentryReplay/PackageManager.cs b/SentryReplay/PackageManager.cs index 50bd70a..6e9015c 100644 --- a/SentryReplay/PackageManager.cs +++ b/SentryReplay/PackageManager.cs @@ -1,6 +1,7 @@ using System.IO; using System.IO.Compression; using System.Net.Http; +using System.Runtime.InteropServices; using Serilog; namespace SentryReplay; @@ -26,7 +27,7 @@ private static void ExtractZipFile(string zipFilePath, string extractPath) public static async Task DownloadAndExtractFFmpeg() { var outputFolder = Path.GetFullPath("ffmpeg"); - var url = "https://github.com/GyanD/codexffmpeg/releases/download/7.0/ffmpeg-7.0-full_build-shared.zip"; // TODO: ARM64 builds? + var url = GetFFmpegDownloadUrl(); var tempPath = Path.GetTempFileName(); Log.Information("Getting ffmpeg"); @@ -40,6 +41,18 @@ public static async Task DownloadAndExtractFFmpeg() File.Delete(tempPath); } + private static string GetFFmpegDownloadUrl() + { + var architecture = RuntimeInformation.ProcessArchitecture; + + return architecture switch + { + Architecture.Arm64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-winarm64-gpl-shared.zip", + Architecture.X64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", + _ => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", // Default to x64 + }; + } + public static IEnumerable FindFFmpegDirectories(string searchDirectory = ".") { foreach (var path in Directory.EnumerateFiles(searchDirectory, "ffmpeg.exe", SearchOption.AllDirectories)) diff --git a/TeslaCam/PackageManager.cs b/TeslaCam/PackageManager.cs index 619af3b..f2e9a2c 100644 --- a/TeslaCam/PackageManager.cs +++ b/TeslaCam/PackageManager.cs @@ -1,6 +1,7 @@ using System.IO; using System.IO.Compression; using System.Net.Http; +using System.Runtime.InteropServices; using Serilog; namespace TeslaCam; @@ -46,7 +47,7 @@ private static void ExtractZipFile(string zipFilePath, string extractPath) public static async Task DownloadAndExtractFFmpeg() { var outputFolder = Path.GetFullPath("ffmpeg"); - var url = "https://github.com/GyanD/codexffmpeg/releases/download/7.0/ffmpeg-7.0-full_build-shared.zip"; + var url = GetFFmpegDownloadUrl(); var tempPath = Path.GetTempFileName(); try @@ -74,6 +75,18 @@ public static async Task DownloadAndExtractFFmpeg() } } + private static string GetFFmpegDownloadUrl() + { + var architecture = RuntimeInformation.ProcessArchitecture; + + return architecture switch + { + Architecture.Arm64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-winarm64-gpl-shared.zip", + Architecture.X64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", + _ => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", // Default to x64 + }; + } + public static IEnumerable FindFFmpegDirectories(string searchDirectory = ".") { foreach (var path in Directory.EnumerateFiles(searchDirectory, "ffmpeg.exe", SearchOption.AllDirectories)) From d8b1fead1f25b6d0196fcc3b4d6b0c833d56bf1b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 20:22:48 +0000 Subject: [PATCH 3/6] Update comment to be generic about zip structure Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com> --- TeslaCam/PackageManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaCam/PackageManager.cs b/TeslaCam/PackageManager.cs index f2e9a2c..42e9ecf 100644 --- a/TeslaCam/PackageManager.cs +++ b/TeslaCam/PackageManager.cs @@ -20,7 +20,7 @@ private static async Task DownloadFile(string url, string savePath) private static void ExtractZipFile(string zipFilePath, string extractPath) { - // Extract and flatten nested directories - the zip contains ffmpeg-7.0-full_build-shared/bin/*.dll and ffmpeg.exe + // Extract and flatten nested directories - the zip contains a bin directory with ffmpeg.exe and required DLLs using var archive = ZipFile.OpenRead(zipFilePath); foreach (var entry in archive.Entries) From 9da72af7b0ab118fcc40fc41c2f28381227f963e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Dec 2025 20:36:42 +0000 Subject: [PATCH 4/6] Throw error for unsupported architectures and remove unused TeslaCam folder Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com> --- SentryReplay/PackageManager.cs | 2 +- TeslaCam/Converters.cs | 74 ---- TeslaCam/MainWindow.xaml | 389 ------------------ TeslaCam/MainWindow.xaml.cs | 723 --------------------------------- TeslaCam/PackageManager.cs | 97 ----- 5 files changed, 1 insertion(+), 1284 deletions(-) delete mode 100644 TeslaCam/Converters.cs delete mode 100644 TeslaCam/MainWindow.xaml delete mode 100644 TeslaCam/MainWindow.xaml.cs delete mode 100644 TeslaCam/PackageManager.cs diff --git a/SentryReplay/PackageManager.cs b/SentryReplay/PackageManager.cs index 6e9015c..2601bb4 100644 --- a/SentryReplay/PackageManager.cs +++ b/SentryReplay/PackageManager.cs @@ -49,7 +49,7 @@ private static string GetFFmpegDownloadUrl() { Architecture.Arm64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-winarm64-gpl-shared.zip", Architecture.X64 => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", - _ => "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip", // Default to x64 + _ => throw new PlatformNotSupportedException($"Unsupported architecture: {architecture}. Only x64 and ARM64 are supported.") }; } diff --git a/TeslaCam/Converters.cs b/TeslaCam/Converters.cs deleted file mode 100644 index 09b2801..0000000 --- a/TeslaCam/Converters.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace TeslaCam; - -/// -/// Converts a TimeSpan to a formatted string (mm:ss or hh:mm:ss). -/// -public class TimeSpanToStringConverter : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is TimeSpan ts) - { - return ts.TotalHours >= 1 - ? ts.ToString(@"h\:mm\:ss") - : ts.ToString(@"m\:ss"); - } - return "0:00"; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} - -/// -/// Converts a boolean to Visibility (true = Visible, false = Collapsed). -/// Supports "Inverse" parameter to invert the logic. -/// -public class BoolToVisibilityConverter : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is bool b) - { - var inverse = parameter?.ToString()?.Equals("Inverse", StringComparison.OrdinalIgnoreCase) ?? false; - var result = inverse ? !b : b; - return result ? Visibility.Visible : Visibility.Collapsed; - } - return Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} - -/// -/// Inverts a boolean value. -/// -public class InverseBoolConverter : IValueConverter -{ - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is bool b) - { - return !b; - } - return false; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is bool b) - { - return !b; - } - return false; - } -} diff --git a/TeslaCam/MainWindow.xaml b/TeslaCam/MainWindow.xaml deleted file mode 100644 index badee6f..0000000 --- a/TeslaCam/MainWindow.xaml +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -