diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs
index 2d968ad..c677d5d 100644
--- a/FFMpegCore.Test/DownloaderTests.cs
+++ b/FFMpegCore.Test/DownloaderTests.cs
@@ -1,5 +1,4 @@
-using System.Runtime.InteropServices;
-using FFMpegCore.Helpers;
+using FFMpegCore.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace FFMpegCore.Test;
@@ -8,58 +7,18 @@ namespace FFMpegCore.Test;
public class DownloaderTests
{
[TestMethod]
- public void GetLatestSuiteTest()
+ public void GetAllLatestSuiteTest()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- var fileNames = FFMpegDownloader.AutoDownloadFFMpegSuite();
- Assert.IsTrue(fileNames.Count == 3);
- }
- else
- {
- Assert.Inconclusive("This test is only for Windows");
- }
+ var binaries = FFMpegDownloader.DownloadFFMpegSuite(binaries: FFMpegDownloader.FFMpegBinaries.FFProbe |
+ FFMpegDownloader.FFMpegBinaries.FFMpeg |
+ FFMpegDownloader.FFMpegBinaries.FFPlay).Result;
+ Assert.IsTrue(binaries.Count >= 2); // many platforms have only ffmpeg and ffprobe
}
-
+
[TestMethod]
- public void GetLatestFFMpegTest()
+ public void GetSpecificVersionTest()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- var fileNames = FFMpegDownloader.AutoDownloadFFMpeg();
- Assert.IsTrue(fileNames.Count == 1);
- }
- else
- {
- Assert.Inconclusive("This test is only for Windows");
- }
- }
-
- [TestMethod]
- public void GetLatestFFProbeTest()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- var fileNames = FFMpegDownloader.AutoDownloadFFProbe();
- Assert.IsTrue(fileNames.Count == 1);
- }
- else
- {
- Assert.Inconclusive("This test is only for Windows");
- }
- }
-
- [TestMethod]
- public void GetLatestFFPlayTest()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- var fileNames = FFMpegDownloader.AutoDownloadFFPlay();
- Assert.IsTrue(fileNames.Count == 1);
- }
- else
- {
- Assert.Inconclusive("This test is only for Windows");
- }
+ var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegDownloader.FFMpegVersions.V4_0).Result;
+ Assert.IsTrue(binaries.Count == 2);
}
}
diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs
index d6149e8..c07a30e 100644
--- a/FFMpegCore/Helpers/FFMpegDownloader.cs
+++ b/FFMpegCore/Helpers/FFMpegDownloader.cs
@@ -1,43 +1,38 @@
-using System.ComponentModel;
+using System.IO.Compression;
using System.Net;
-using System.IO;
-using System.IO.Compression;
-
+using System.Runtime.InteropServices;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace FFMpegCore.Helpers;
-using System.Runtime.InteropServices;
///
-/// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment.
+/// Downloads the latest FFMpeg suite binaries from ffbinaries.com.
///
-public class FFMpegDownloader // this class is built to be easily modified to support other platforms
+public class FFMpegDownloader
{
- private static Dictionary Windows64FFMpegDownloadUrls = new()
+ [Flags]
+ public enum FFMpegBinaries : ushort
{
- { FFMpegVersions.V4_4_1, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-win-64.zip"},
- { FFMpegVersions.V4_2_1, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-win-64.zip"},
- { FFMpegVersions.V4_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.2/ffmpeg-4.2-win-64.zip"},
- { FFMpegVersions.V4_1, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.1/ffmpeg-4.1-win-64.zip"},
- { FFMpegVersions.V4_0, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.0/ffmpeg-4.0.1-win-64.zip"},
- { FFMpegVersions.V3_4, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.4/ffmpeg-3.4-win-64.zip"},
- { FFMpegVersions.V3_3, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.3/ffmpeg-3.3.4-win-64.zip"},
- { FFMpegVersions.V3_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.2/ffmpeg-3.2-win-64.zip"},
- };
-
- private static Dictionary Windows32FFMpegDownloadUrls = new()
- {
- { FFMpegVersions.V4_4_1, "https://example.com/" },
- { FFMpegVersions.V4_2_1, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-win-32.zip"},
- { FFMpegVersions.V4_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.2/ffmpeg-4.2-win-32.zip"},
- { FFMpegVersions.V4_1, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.1/ffmpeg-4.1-win-32.zip"},
- { FFMpegVersions.V4_0, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.0/ffmpeg-4.0.1-win-32.zip"},
- { FFMpegVersions.V3_4, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.4/ffmpeg-3.4-win-32.zip"},
- { FFMpegVersions.V3_3, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.3/ffmpeg-3.3.4-win-32.zip"},
- { FFMpegVersions.V3_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.2/ffmpeg-3.2-win-32.zip"},
- };
-
- public enum FFMpegVersions
+ ///
+ /// FFMpeg binary
+ ///
+ FFMpeg,
+
+ ///
+ /// FFProbe binary
+ ///
+ FFProbe,
+
+ ///
+ /// FFPlay binary
+ ///
+ FFPlay
+ }
+
+ public enum FFMpegVersions : ushort
{
+ Latest,
V4_4_1,
V4_2_1,
V4_2,
@@ -48,70 +43,71 @@ public enum FFMpegVersions
V3_2
}
- public static List AutoDownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.V4_4_1)
+ ///
+ /// Download the latest FFMpeg suite binaries for current platform
+ ///
+ /// used to explicitly state the version of binary you want to download
+ /// used to explicitly state the binary you want to download
+ /// a list of the binaries that have been successfully downloaded
+ public static async Task> DownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.Latest,
+ FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe)
{
- var files = AutoDownloadFFMpeg(version);
- files.AddRange(AutoDownloadFFProbe(version));
- files.AddRange(AutoDownloadFFPlay(version));
-
- return files;
+ var versionInfo = await GetVersionInfo(version);
+ var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo() ??
+ throw new FFMpegDownloaderException("Failed to get compatible download info");
+
+ var successList = new List();
+
+ // if ffmpeg is selected
+ if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null)
+ {
+ var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg));
+ successList.AddRange(ExtractZipAndSave(zipStream));
+ }
+
+ // if ffprobe is selected
+ if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null)
+ {
+ var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFProbe));
+ successList.AddRange(ExtractZipAndSave(zipStream));
+ }
+
+ // if ffplay is selected
+ if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null)
+ {
+ var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFPlay));
+ successList.AddRange(ExtractZipAndSave(zipStream));
+ }
+
+ return successList;
}
- public static List AutoDownloadFFMpeg(FFMpegVersions version = FFMpegVersions.V4_4_1)
- {
- var url = Environment.Is64BitProcess
- ? new Uri(Windows64FFMpegDownloadUrls[version])
- : new Uri(Windows32FFMpegDownloadUrls[version]);
-
- HasValidUri(url);
-
- Stream zipStream = DownloadZip(url);
-
- return ExtractAndSave(zipStream);
- }
-
- public static List AutoDownloadFFProbe(FFMpegVersions version = FFMpegVersions.V4_4_1)
- {
- var url = Environment.Is64BitProcess
- ? new Uri(Windows64FFMpegDownloadUrls[version].Replace("ffmpeg", "ffprobe"))
- : new Uri(Windows32FFMpegDownloadUrls[version].Replace("ffmpeg", "ffprobe"));
-
- HasValidUri(url);
-
- Stream zipStream = DownloadZip(url);
-
- return ExtractAndSave(zipStream);
- }
-
- public static List AutoDownloadFFPlay(FFMpegVersions version = FFMpegVersions.V4_4_1)
- {
- var url = Environment.Is64BitProcess
- ? new Uri(Windows64FFMpegDownloadUrls[version].Replace("ffmpeg", "ffplay"))
- : new Uri(Windows32FFMpegDownloadUrls[version].Replace("ffmpeg", "ffplay"));
-
- HasValidUri(url);
-
- Stream zipStream = DownloadZip(url);
-
- return ExtractAndSave(zipStream);
- }
-
- private static MemoryStream DownloadZip(Uri address)
+ ///
+ /// Download file from uri
+ ///
+ /// uri of the file
+ ///
+ private static MemoryStream DownloadFileAsSteam(Uri address)
{
var client = new WebClient();
- var zipStream = new MemoryStream(client.DownloadData(address));
- zipStream.Position = 0;
+ var fileStream = new MemoryStream(client.DownloadData(address));
+ fileStream.Position = 0;
- return zipStream;
+ return fileStream;
}
-
- private static List ExtractAndSave(Stream zipStream)
+
+ ///
+ /// Extracts the binaries from the zip stream and saves them to the current binary folder
+ ///
+ /// steam of the zip file
+ ///
+ private static List ExtractZipAndSave(Stream zipStream)
{
using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read);
List files = new();
foreach (var entry in archive.Entries)
{
- if (entry.Name is "ffmpeg.exe" or "ffmpeg" or "ffprobe.exe")
+ if (entry.Name is "ffmpeg" or "ffmpeg.exe" or "ffprobe.exe" or "ffprobe" or "ffplay.exe" or "ffplay")
{
entry.ExtractToFile(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name), true);
files.Add(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name));
@@ -121,11 +117,128 @@ private static List ExtractAndSave(Stream zipStream)
return files;
}
- private static void HasValidUri(Uri uri)
+ #region FFbinaries api
+
+ private class DownloadInfo
{
- if (uri.ToString() == "https://example.com/" || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; }
+
+ [JsonPropertyName("ffprobe")] public string? FFProbe { get; }
+
+ [JsonPropertyName("ffplay")] public string? FFPlay { get; }
+ }
+
+ private class BinaryInfo
+ {
+ [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; }
+
+ [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; }
+
+ [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; }
+
+ [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; }
+
+ [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; }
+
+ [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; }
+
+ [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; }
+
+ [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; }
+
+ public DownloadInfo? GetCompatibleDownloadInfo()
{
- throw new PlatformNotSupportedException("The requested version of FFMpeg component is not available for your OS.");
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32;
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return RuntimeInformation.OSArchitecture switch
+ {
+ Architecture.X64 => Linux64,
+ Architecture.Arm => LinuxArmhf,
+ Architecture.Arm64 => LinuxArm64,
+ _ => throw new PlatformNotSupportedException("Unsupported Linux architecture")
+ };
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return Osx64;
+ }
+
+ throw new PlatformNotSupportedException("Unsupported OS");
}
}
+
+ private class VersionInfo
+ {
+ [JsonPropertyName("version")] public string? Version { get; set; }
+
+ [JsonPropertyName("permalink")] public string? Permalink { get; set; }
+
+ [JsonPropertyName("bin")] public BinaryInfo? BinaryInfo { get; set; }
+ }
+
+ private static readonly Dictionary FFBinariesAPIs = new()
+ {
+ { FFMpegVersions.Latest, "https://ffbinaries.com/api/v1/version/latest" },
+ { FFMpegVersions.V4_4_1, "https://ffbinaries.com/api/v1/version/4.4.1" },
+ { FFMpegVersions.V4_2_1, "https://ffbinaries.com/api/v1/version/4.2.1" },
+ { FFMpegVersions.V4_2, "https://ffbinaries.com/api/v1/version/4.2" },
+ { FFMpegVersions.V4_1, "https://ffbinaries.com/api/v1/version/4.1" },
+ { FFMpegVersions.V4_0, "https://ffbinaries.com/api/v1/version/4.0" },
+ { FFMpegVersions.V3_4, "https://ffbinaries.com/api/v1/version/3.4" },
+ { FFMpegVersions.V3_3, "https://ffbinaries.com/api/v1/version/3.3" },
+ { FFMpegVersions.V3_2, "https://ffbinaries.com/api/v1/version/3.2" }
+ };
+
+ ///
+ /// Get version info from ffbinaries.com
+ ///
+ /// use to explicitly state the version of ffmpeg you want
+ ///
+ ///
+ private static async Task GetVersionInfo(FFMpegVersions version)
+ {
+ if (!FFBinariesAPIs.TryGetValue(version, out var versionUri))
+ {
+ throw new FFMpegDownloaderException($"Invalid version selected: {version}", "contact dev");
+ }
+
+ HttpClient client = new();
+ var response = await client.GetAsync(versionUri);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new FFMpegDownloaderException($"Failed to get version info from {versionUri}", "network error");
+ }
+
+ var jsonString = await response.Content.ReadAsStringAsync();
+ var versionInfo = JsonSerializer.Deserialize(jsonString);
+
+ return versionInfo ??
+ throw new FFMpegDownloaderException($"Failed to deserialize version info from {versionUri}", jsonString);
+ }
+
+ #endregion
+}
+
+///
+/// Custom exception for FFMpegDownloader
+///
+public class FFMpegDownloaderException : Exception
+{
+ public FFMpegDownloaderException(string message) : base(message)
+ {
+ }
+
+ public FFMpegDownloaderException(string message, string detail) : base(message)
+ {
+ Detail = detail;
+ }
+
+ public string Detail { get; set; } = "";
}