Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions SentryReplay.Tests/PackageManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
using Shouldly;

namespace SentryReplay.Tests;

/// <summary>
/// Tests for PackageManager functionality.
/// </summary>
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();
}
}
78 changes: 63 additions & 15 deletions SentryReplay/PackageManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Runtime.InteropServices;
using Serilog;

namespace SentryReplay;
Expand All @@ -9,35 +10,82 @@ public static class PackageManager
{
private static async Task DownloadFile(string url, string savePath)
{
using var client = new HttpClient();
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
using var fileStream = File.Create(savePath);
await response.Content.CopyToAsync(fileStream);
}
using var client = new HttpClient { Timeout = TimeSpan.FromMinutes(5) };
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();

using var fileStream = File.Create(savePath);
await response.Content.CopyToAsync(fileStream);
}

private static void ExtractZipFile(string zipFilePath, string extractPath)
{
ZipFile.ExtractToDirectory(zipFilePath, extractPath, true);
// 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)
{
if (string.IsNullOrEmpty(entry.Name))
continue;

// Find bin directory in the zip structure
var entryPath = entry.FullName.Replace('/', Path.DirectorySeparatorChar);
var binDirectory = "bin" + Path.DirectorySeparatorChar;
var binIndex = entryPath.IndexOf(binDirectory, StringComparison.OrdinalIgnoreCase);

if (binIndex >= 0)
{
// Extract files from bin directory to root of extractPath
var relativePath = entryPath.Substring(binIndex + binDirectory.Length);
var destPath = Path.Combine(extractPath, relativePath);

Directory.CreateDirectory(Path.GetDirectoryName(destPath)!);
entry.ExtractToFile(destPath, overwrite: true);
}
}
}

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");
try
{
Log.Information("Downloading FFmpeg...");

Log.Debug($"Downloading ffmpeg to {tempPath} from {url}");
await DownloadFile(url, tempPath);
Log.Debug($"Downloading FFmpeg to {tempPath} from {url}");
await DownloadFile(url, tempPath);

Log.Debug($"Extracting ffmpeg to {outputFolder}");
ExtractZipFile(tempPath, outputFolder);
Log.Information("Extracting FFmpeg...");
Log.Debug($"Extracting FFmpeg to {outputFolder}");

if (Directory.Exists(outputFolder))
Directory.Delete(outputFolder, true);
Directory.CreateDirectory(outputFolder);

ExtractZipFile(tempPath, outputFolder);

Log.Information("FFmpeg downloaded and extracted successfully");
}
finally
{
if (File.Exists(tempPath))
File.Delete(tempPath);
}
}

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",
_ => throw new PlatformNotSupportedException($"Unsupported architecture: {architecture}. Only x64 and ARM64 are supported.")
};
}

public static IEnumerable<string> FindFFmpegDirectories(string searchDirectory = ".")
Expand Down