From 9db4ba75b45a2a55da2feabf973a459865d1acb7 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Tue, 11 Apr 2023 21:55:30 -0600 Subject: [PATCH 01/47] features to auto download ffmpeg binaries added --- FFMpegCore.Test/DownloaderTests.cs | 27 +++++++++++ FFMpegCore/Helpers/FFMpegDownloader.cs | 64 ++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 FFMpegCore.Test/DownloaderTests.cs create mode 100644 FFMpegCore/Helpers/FFMpegDownloader.cs diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs new file mode 100644 index 0000000..301078a --- /dev/null +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -0,0 +1,27 @@ +using System.Runtime.InteropServices; +using FFMpegCore.Helpers; + +namespace FFMpegCore.Test; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +public class DownloaderTests +{ + [TestClass] + public class FFMpegDownloaderTest + { + [TestMethod] + public void GetLatestVersionTest() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var files = FFMpegDownloader.GetLatestVersion(); + Assert.IsTrue(files.Count == 3); + } + else + { + Assert.Inconclusive("This test is only for Windows"); + } + + } + } +} diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs new file mode 100644 index 0000000..7767683 --- /dev/null +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -0,0 +1,64 @@ +using System.ComponentModel; +using System.Net; +using System.IO; +using System.IO.Compression; + + +namespace FFMpegCore.Helpers; +using System.Runtime.InteropServices; + +/// +/// Downloads the latest FFMpeg binaries from GitHub. Only supported for windows at the moment. +/// +public class FFMpegDownloader // this class is built to be easily modified to support other platforms +{ + /// + /// List of URLs to download FFMpeg from. + /// + private static Dictionary FFMpegDownloadUrls = new() + { + { "windows", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip" } + }; + + public static List GetLatestVersion() + { + var os = GetOSPlatform(); + var zipStream = DownloadFFMpeg(new Uri(FFMpegDownloadUrls[os])); + return ExtractAndSave(zipStream); + } + + private static MemoryStream DownloadFFMpeg(Uri address) + { + var client = new WebClient(); + var zipStream = new MemoryStream(client.DownloadData(address)); + zipStream.Position = 0; + + return zipStream; + } + + private static List ExtractAndSave(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") + { + entry.ExtractToFile(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name), true); + files.Add(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name)); + } + } + + return files; + } + + private static string GetOSPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return "windows"; + } + + throw new PlatformNotSupportedException("Auto download is only supported on Windows."); + } +} From a4bb69a8a8b94cab0ba8a61e16263fddaf1789c5 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Tue, 11 Apr 2023 22:42:51 -0600 Subject: [PATCH 02/47] changes made to support multiple version downloads for windows --- FFMpegCore.Test/DownloaderTests.cs | 70 +++++++++++++---- FFMpegCore/Helpers/FFMpegDownloader.cs | 105 ++++++++++++++++++++----- 2 files changed, 140 insertions(+), 35 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 301078a..2d968ad 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,27 +1,65 @@ using System.Runtime.InteropServices; using FFMpegCore.Helpers; - -namespace FFMpegCore.Test; using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace FFMpegCore.Test; + +[TestClass] public class DownloaderTests { - [TestClass] - public class FFMpegDownloaderTest + [TestMethod] + public void GetLatestSuiteTest() { - [TestMethod] - public void GetLatestVersionTest() + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var files = FFMpegDownloader.GetLatestVersion(); - Assert.IsTrue(files.Count == 3); - } - else - { - Assert.Inconclusive("This test is only for Windows"); - } - + var fileNames = FFMpegDownloader.AutoDownloadFFMpegSuite(); + Assert.IsTrue(fileNames.Count == 3); + } + else + { + Assert.Inconclusive("This test is only for Windows"); + } + } + + [TestMethod] + public void GetLatestFFMpegTest() + { + 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"); } } } diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index 7767683..d6149e8 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -8,26 +8,95 @@ namespace FFMpegCore.Helpers; using System.Runtime.InteropServices; /// -/// Downloads the latest FFMpeg binaries from GitHub. Only supported for windows at the moment. +/// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment. /// public class FFMpegDownloader // this class is built to be easily modified to support other platforms { - /// - /// List of URLs to download FFMpeg from. - /// - private static Dictionary FFMpegDownloadUrls = new() + private static Dictionary Windows64FFMpegDownloadUrls = new() { - { "windows", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip" } + { 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"}, }; - - public static List GetLatestVersion() + + private static Dictionary Windows32FFMpegDownloadUrls = new() { - var os = GetOSPlatform(); - var zipStream = DownloadFFMpeg(new Uri(FFMpegDownloadUrls[os])); + { 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 + { + V4_4_1, + V4_2_1, + V4_2, + V4_1, + V4_0, + V3_4, + V3_3, + V3_2 + } + + public static List AutoDownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.V4_4_1) + { + var files = AutoDownloadFFMpeg(version); + files.AddRange(AutoDownloadFFProbe(version)); + files.AddRange(AutoDownloadFFPlay(version)); + + return files; + } + + 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 DownloadFFMpeg(Uri address) + private static MemoryStream DownloadZip(Uri address) { var client = new WebClient(); var zipStream = new MemoryStream(client.DownloadData(address)); @@ -51,14 +120,12 @@ public class FFMpegDownloader // this class is built to be easily modified to su return files; } - - private static string GetOSPlatform() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "windows"; - } - throw new PlatformNotSupportedException("Auto download is only supported on Windows."); + private static void HasValidUri(Uri uri) + { + if (uri.ToString() == "https://example.com/" || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + throw new PlatformNotSupportedException("The requested version of FFMpeg component is not available for your OS."); + } } } From 657ee5ff5d371487e950c75e141f220a568927ab Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Fri, 5 May 2023 01:21:53 -0600 Subject: [PATCH 03/47] Comments added --- FFMpegCore/Helpers/FFMpegDownloader.cs | 52 ++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index d6149e8..9d87001 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -1,6 +1,4 @@ -using System.ComponentModel; -using System.Net; -using System.IO; +using System.Net; using System.IO.Compression; @@ -10,7 +8,7 @@ using System.Runtime.InteropServices; /// /// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment. /// -public class FFMpegDownloader // this class is built to be easily modified to support other platforms +public class FFMpegDownloader { private static Dictionary Windows64FFMpegDownloadUrls = new() { @@ -26,7 +24,7 @@ public class FFMpegDownloader // this class is built to be easily modified to su private static Dictionary Windows32FFMpegDownloadUrls = new() { - { FFMpegVersions.V4_4_1, "https://example.com/" }, + { FFMpegVersions.V4_4_1, "" }, { 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"}, @@ -36,6 +34,9 @@ public class FFMpegDownloader // this class is built to be easily modified to su { FFMpegVersions.V3_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.2/ffmpeg-3.2-win-32.zip"}, }; + /// + /// Supported FFMpeg versions + /// public enum FFMpegVersions { V4_4_1, @@ -48,6 +49,11 @@ public class FFMpegDownloader // this class is built to be easily modified to su V3_2 } + /// + /// Downloads the latest FFMpeg suite binaries to bin directory. + /// + /// + /// public static List AutoDownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.V4_4_1) { var files = AutoDownloadFFMpeg(version); @@ -57,6 +63,11 @@ public class FFMpegDownloader // this class is built to be easily modified to su return files; } + /// + /// Downloads the latest FFMpeg binaries to bin directory. + /// + /// + /// public static List AutoDownloadFFMpeg(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess @@ -70,6 +81,11 @@ public class FFMpegDownloader // this class is built to be easily modified to su return ExtractAndSave(zipStream); } + /// + /// Downloads the latest FFProbe binaries to bin directory. + /// + /// + /// public static List AutoDownloadFFProbe(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess @@ -83,6 +99,11 @@ public class FFMpegDownloader // this class is built to be easily modified to su return ExtractAndSave(zipStream); } + /// + /// Downloads the latest FFPlay binaries to bin directory. + /// + /// + /// public static List AutoDownloadFFPlay(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess @@ -96,6 +117,11 @@ public class FFMpegDownloader // this class is built to be easily modified to su return ExtractAndSave(zipStream); } + /// + /// Downloads the zip file from the given url. + /// + /// + /// private static MemoryStream DownloadZip(Uri address) { var client = new WebClient(); @@ -105,13 +131,18 @@ public class FFMpegDownloader // this class is built to be easily modified to su return zipStream; } + /// + /// Extracts the zip file and saves the binaries to bin directory. + /// + /// + /// private static List ExtractAndSave(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.exe" or "ffmpeg" or "ffprobe.exe" or "ffplay.exe" or "ffplay") // only extract the binaries { entry.ExtractToFile(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name), true); files.Add(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name)); @@ -121,11 +152,16 @@ public class FFMpegDownloader // this class is built to be easily modified to su return files; } + /// + /// Checks if the given uri is valid. + /// + /// + /// private static void HasValidUri(Uri uri) { - if (uri.ToString() == "https://example.com/" || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (uri.ToString() == "" || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - throw new PlatformNotSupportedException("The requested version of FFMpeg component is not available for your OS."); + throw new PlatformNotSupportedException("The requested version of FFMpeg component is not available for your OS/System."); } } } From d68ce8e953f9e914e899ace85b32058b62bfe734 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Fri, 5 May 2023 01:30:52 -0600 Subject: [PATCH 04/47] Methods renamed to remove auto prefix --- FFMpegCore.Test/DownloaderTests.cs | 8 ++++---- FFMpegCore/Helpers/FFMpegDownloader.cs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 2d968ad..d0f1f55 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -12,7 +12,7 @@ public class DownloaderTests { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var fileNames = FFMpegDownloader.AutoDownloadFFMpegSuite(); + var fileNames = FFMpegDownloader.DownloadFFMpegSuite(); Assert.IsTrue(fileNames.Count == 3); } else @@ -26,7 +26,7 @@ public class DownloaderTests { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var fileNames = FFMpegDownloader.AutoDownloadFFMpeg(); + var fileNames = FFMpegDownloader.DownloadFFMpeg(); Assert.IsTrue(fileNames.Count == 1); } else @@ -40,7 +40,7 @@ public class DownloaderTests { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var fileNames = FFMpegDownloader.AutoDownloadFFProbe(); + var fileNames = FFMpegDownloader.DownloadFFProbe(); Assert.IsTrue(fileNames.Count == 1); } else @@ -54,7 +54,7 @@ public class DownloaderTests { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var fileNames = FFMpegDownloader.AutoDownloadFFPlay(); + var fileNames = FFMpegDownloader.DownloadFFPlay(); Assert.IsTrue(fileNames.Count == 1); } else diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index 9d87001..89b3d4c 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -53,12 +53,12 @@ public class FFMpegDownloader /// Downloads the latest FFMpeg suite binaries to bin directory. /// /// - /// - public static List AutoDownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.V4_4_1) + /// Names of the binary that was saved to bin directory + public static List DownloadFFMpegSuite(FFMpegVersions version = FFMpegVersions.V4_4_1) { - var files = AutoDownloadFFMpeg(version); - files.AddRange(AutoDownloadFFProbe(version)); - files.AddRange(AutoDownloadFFPlay(version)); + var files = DownloadFFMpeg(version); + files.AddRange(DownloadFFProbe(version)); + files.AddRange(DownloadFFPlay(version)); return files; } @@ -68,7 +68,7 @@ public class FFMpegDownloader /// /// /// - public static List AutoDownloadFFMpeg(FFMpegVersions version = FFMpegVersions.V4_4_1) + public static List DownloadFFMpeg(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess ? new Uri(Windows64FFMpegDownloadUrls[version]) @@ -86,7 +86,7 @@ public class FFMpegDownloader /// /// /// - public static List AutoDownloadFFProbe(FFMpegVersions version = FFMpegVersions.V4_4_1) + public static List DownloadFFProbe(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess ? new Uri(Windows64FFMpegDownloadUrls[version].Replace("ffmpeg", "ffprobe")) @@ -104,7 +104,7 @@ public class FFMpegDownloader /// /// /// - public static List AutoDownloadFFPlay(FFMpegVersions version = FFMpegVersions.V4_4_1) + public static List DownloadFFPlay(FFMpegVersions version = FFMpegVersions.V4_4_1) { var url = Environment.Is64BitProcess ? new Uri(Windows64FFMpegDownloadUrls[version].Replace("ffmpeg", "ffplay")) From debb868b38ef11deee4335bc7d37a2f75bc9e333 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Fri, 5 May 2023 01:34:21 -0600 Subject: [PATCH 05/47] README.md updated to include ffmpegdownloader usage --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 365d0be..74876a7 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,19 @@ If you want to use `System.Drawing.Bitmap`s as `IVideoFrame`s, a `BitmapVideoFra # Binaries -## Installation +## Runtime Auto Installation +You can install a version of ffmpeg suite at runtime using `FFMpegDownloader.DownloadFFMpegSuite();` + +Or you can download only the desired binary using `FFMpegDownloader.DownloadFFMpeg()`, `FFMpegDownloader.DownloadFFProbe()`, `FFMpegDownloader.DownloadFFPlay()`. + +| OS | Support | +|---------|:-------------------------------------------------:| +| Windows | x64 Fully Supported, x86 Up to version V4.2.1 | +| Mac OSX | NOT YET | +| Linux | NOT YET | + + +## Manual Installation If you prefer to manually download them, visit [ffbinaries](https://ffbinaries.com/downloads) or [zeranoe Windows builds](https://ffmpeg.zeranoe.com/builds/). ### Windows (using choco) From a647f440296dc5d668d10ca6b08bbda033b32998 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Fri, 5 May 2023 01:47:08 -0600 Subject: [PATCH 06/47] refactoring and reformatting --- FFMpegCore/Helpers/FFMpegDownloader.cs | 128 +++++++++++++++++-------- 1 file changed, 88 insertions(+), 40 deletions(-) diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index 89b3d4c..fef53b3 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -1,39 +1,83 @@ using System.Net; using System.IO.Compression; - +using System.Runtime.InteropServices; namespace FFMpegCore.Helpers; -using System.Runtime.InteropServices; /// /// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment. /// public class FFMpegDownloader { - private static Dictionary Windows64FFMpegDownloadUrls = new() + private static readonly Dictionary Windows64FFMpegDownloadUrls = new() { - { 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"}, + { + 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() + + private static readonly Dictionary Windows32FFMpegDownloadUrls = new() { { FFMpegVersions.V4_4_1, "" }, - { 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"}, + { + 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" + }, }; - + /// /// Supported FFMpeg versions /// @@ -59,7 +103,7 @@ public class FFMpegDownloader var files = DownloadFFMpeg(version); files.AddRange(DownloadFFProbe(version)); files.AddRange(DownloadFFPlay(version)); - + return files; } @@ -73,14 +117,10 @@ public class FFMpegDownloader var url = Environment.Is64BitProcess ? new Uri(Windows64FFMpegDownloadUrls[version]) : new Uri(Windows32FFMpegDownloadUrls[version]); - - HasValidUri(url); - Stream zipStream = DownloadZip(url); - - return ExtractAndSave(zipStream); + return DownloadAndSave(url); } - + /// /// Downloads the latest FFProbe binaries to bin directory. /// @@ -91,14 +131,10 @@ public class FFMpegDownloader 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); + return DownloadAndSave(url); } - + /// /// Downloads the latest FFPlay binaries to bin directory. /// @@ -109,12 +145,22 @@ public class FFMpegDownloader var url = Environment.Is64BitProcess ? new Uri(Windows64FFMpegDownloadUrls[version].Replace("ffmpeg", "ffplay")) : new Uri(Windows32FFMpegDownloadUrls[version].Replace("ffmpeg", "ffplay")); - + + return DownloadAndSave(url); + } + + /// + /// Downloads the zip file from the given url and saves the binaries to bin directory. + /// + /// + /// + private static List DownloadAndSave(Uri url) + { HasValidUri(url); Stream zipStream = DownloadZip(url); - return ExtractAndSave(zipStream); + return ExtractZip(zipStream); } /// @@ -130,19 +176,20 @@ public class FFMpegDownloader return zipStream; } - + /// /// Extracts the zip file and saves the binaries to bin directory. /// /// /// - private static List ExtractAndSave(Stream zipStream) + private static List ExtractZip(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" or "ffplay.exe" or "ffplay") // only extract the binaries + if (entry.Name is "ffmpeg.exe" or "ffmpeg" or "ffprobe.exe" or "ffplay.exe" + or "ffplay") // only extract the binaries { entry.ExtractToFile(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name), true); files.Add(Path.Combine(GlobalFFOptions.Current.BinaryFolder, entry.Name)); @@ -161,7 +208,8 @@ public class FFMpegDownloader { if (uri.ToString() == "" || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - throw new PlatformNotSupportedException("The requested version of FFMpeg component is not available for your OS/System."); + throw new PlatformNotSupportedException( + "The requested version of FFMpeg component is not available for your OS/System."); } } } From 463fb9bf6b9153e945d433f8ca2349ead0cf4e2f Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Fri, 5 May 2023 01:54:50 -0600 Subject: [PATCH 07/47] Reformatted to fix lint error --- FFMpegCore.Test/DownloaderTests.cs | 6 +-- FFMpegCore/Helpers/FFMpegDownloader.cs | 56 +++++++++++++------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index d0f1f55..52c1780 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -20,7 +20,7 @@ public class DownloaderTests Assert.Inconclusive("This test is only for Windows"); } } - + [TestMethod] public void GetLatestFFMpegTest() { @@ -34,7 +34,7 @@ public class DownloaderTests Assert.Inconclusive("This test is only for Windows"); } } - + [TestMethod] public void GetLatestFFProbeTest() { @@ -48,7 +48,7 @@ public class DownloaderTests Assert.Inconclusive("This test is only for Windows"); } } - + [TestMethod] public void GetLatestFFPlayTest() { diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index fef53b3..6705540 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -1,14 +1,29 @@ -using System.Net; -using System.IO.Compression; +using System.IO.Compression; +using System.Net; using System.Runtime.InteropServices; namespace FFMpegCore.Helpers; /// -/// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment. +/// Downloads the latest FFMpeg suite binaries from GitHub. Only supported for windows at the moment. /// public class FFMpegDownloader { + /// + /// Supported FFMpeg versions + /// + public enum FFMpegVersions + { + V4_4_1, + V4_2_1, + V4_2, + V4_1, + V4_0, + V3_4, + V3_3, + V3_2 + } + private static readonly Dictionary Windows64FFMpegDownloadUrls = new() { { @@ -42,7 +57,7 @@ public class FFMpegDownloader { FFMpegVersions.V3_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.2/ffmpeg-3.2-win-64.zip" - }, + } }; private static readonly Dictionary Windows32FFMpegDownloadUrls = new() @@ -75,26 +90,11 @@ public class FFMpegDownloader { FFMpegVersions.V3_2, "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v3.2/ffmpeg-3.2-win-32.zip" - }, + } }; /// - /// Supported FFMpeg versions - /// - public enum FFMpegVersions - { - V4_4_1, - V4_2_1, - V4_2, - V4_1, - V4_0, - V3_4, - V3_3, - V3_2 - } - - /// - /// Downloads the latest FFMpeg suite binaries to bin directory. + /// Downloads the latest FFMpeg suite binaries to bin directory. /// /// /// Names of the binary that was saved to bin directory @@ -108,7 +108,7 @@ public class FFMpegDownloader } /// - /// Downloads the latest FFMpeg binaries to bin directory. + /// Downloads the latest FFMpeg binaries to bin directory. /// /// /// @@ -122,7 +122,7 @@ public class FFMpegDownloader } /// - /// Downloads the latest FFProbe binaries to bin directory. + /// Downloads the latest FFProbe binaries to bin directory. /// /// /// @@ -136,7 +136,7 @@ public class FFMpegDownloader } /// - /// Downloads the latest FFPlay binaries to bin directory. + /// Downloads the latest FFPlay binaries to bin directory. /// /// /// @@ -150,7 +150,7 @@ public class FFMpegDownloader } /// - /// Downloads the zip file from the given url and saves the binaries to bin directory. + /// Downloads the zip file from the given url and saves the binaries to bin directory. /// /// /// @@ -164,7 +164,7 @@ public class FFMpegDownloader } /// - /// Downloads the zip file from the given url. + /// Downloads the zip file from the given url. /// /// /// @@ -178,7 +178,7 @@ public class FFMpegDownloader } /// - /// Extracts the zip file and saves the binaries to bin directory. + /// Extracts the zip file and saves the binaries to bin directory. /// /// /// @@ -200,7 +200,7 @@ public class FFMpegDownloader } /// - /// Checks if the given uri is valid. + /// Checks if the given uri is valid. /// /// /// From da7d1fafed127d1f4c89f39d191e2a87f7e756b5 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 01:25:51 -0600 Subject: [PATCH 08/47] support added for linux, macos win32, win64, lnx32, lnx64, lnx-armhf, lnx-armel, lnx-arm64, osx64 --- FFMpegCore.Test/DownloaderTests.cs | 61 +----- FFMpegCore/Helpers/FFMpegDownloader.cs | 285 +++++++++++++++++-------- 2 files changed, 209 insertions(+), 137 deletions(-) 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 class FFMpegDownloader // this class is built to be easily modified to su 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 @@ public class FFMpegDownloader // this class is built to be easily modified to su 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; } = ""; } From df8d97ebbbb7f3617ce4bc9850ec408449ead186 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 13:19:02 -0600 Subject: [PATCH 09/47] allow os and architecture overrider --- FFMpegCore/Helpers/FFMpegDownloader.cs | 102 +++++++++++++++++-------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index c07a30e..f465f1b 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -14,19 +14,8 @@ public class FFMpegDownloader [Flags] public enum FFMpegBinaries : ushort { - /// - /// FFMpeg binary - /// FFMpeg, - - /// - /// FFProbe binary - /// FFProbe, - - /// - /// FFPlay binary - /// FFPlay } @@ -43,17 +32,32 @@ public class FFMpegDownloader V3_2 } + public enum PlatformOverride : short + { + Windows64, + Windows32, + Linux64, + Linux32, + LinuxArmhf, + LinuxArmel, + LinuxArm64, + Osx64 + } + /// /// 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 + /// used to explicitly state the binaries you want to download (ffmpeg, ffprobe, ffplay) + /// used to explicitly state the os and architecture 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) + public static async Task> DownloadFFMpegSuite( + FFMpegVersions version = FFMpegVersions.Latest, + FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe, + PlatformOverride? platformOverride = null) { var versionInfo = await GetVersionInfo(version); - var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo() ?? + var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? throw new FFMpegDownloaderException("Failed to get compatible download info"); var successList = new List(); @@ -81,7 +85,7 @@ public class FFMpegDownloader return successList; } - + /// /// Download file from uri /// @@ -101,7 +105,7 @@ public class FFMpegDownloader /// /// steam of the zip file /// - private static List ExtractZipAndSave(Stream zipStream) + private static IEnumerable ExtractZipAndSave(Stream zipStream) { using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); List files = new(); @@ -121,33 +125,64 @@ public class FFMpegDownloader private class DownloadInfo { - [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; } + [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; } - [JsonPropertyName("ffprobe")] public string? FFProbe { get; } + [JsonPropertyName("ffprobe")] public string? FFProbe { get; set; } - [JsonPropertyName("ffplay")] public string? FFPlay { get; } + [JsonPropertyName("ffplay")] public string? FFPlay { get; set; } } private class BinaryInfo { - [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; } + [JsonPropertyName("windows-64")] + public DownloadInfo? Windows64 { get; set; } - [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; } + [JsonPropertyName("windows-32")] + public DownloadInfo? Windows32 { get; set; } - [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } + [JsonPropertyName("linux-32")] + public DownloadInfo? Linux32 { get; set; } - [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; } + [JsonPropertyName("linux-64")] + public DownloadInfo? Linux64 { get; set; } - [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; } + [JsonPropertyName("linux-armhf")] + public DownloadInfo? LinuxArmhf { get; set; } - [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } + [JsonPropertyName("linux-armel")] + public DownloadInfo? LinuxArmel { get; set; } - [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; } + [JsonPropertyName("linux-arm64")] + public DownloadInfo? LinuxArm64 { get; set; } - [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; } + [JsonPropertyName("osx-64")] + public DownloadInfo? Osx64 { get; set; } - public DownloadInfo? GetCompatibleDownloadInfo() + /// + /// Automatically get the compatible download info for current os and architecture + /// + /// + /// + /// + /// + public DownloadInfo? GetCompatibleDownloadInfo(PlatformOverride? platformOverride = null) { + if (platformOverride is not null) + { + return platformOverride switch + { + PlatformOverride.Windows64 => Windows64, + PlatformOverride.Windows32 => Windows32, + PlatformOverride.Linux64 => Linux64, + PlatformOverride.Linux32 => Linux32, + PlatformOverride.LinuxArmhf => LinuxArmhf, + PlatformOverride.LinuxArmel => LinuxArmel, + PlatformOverride.LinuxArm64 => LinuxArm64, + PlatformOverride.Osx64 => Osx64, + _ => throw new ArgumentOutOfRangeException(nameof(platformOverride), platformOverride, null) + }; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32; @@ -157,10 +192,11 @@ public class FFMpegDownloader { return RuntimeInformation.OSArchitecture switch { + Architecture.X86 => Linux32, Architecture.X64 => Linux64, Architecture.Arm => LinuxArmhf, Architecture.Arm64 => LinuxArm64, - _ => throw new PlatformNotSupportedException("Unsupported Linux architecture") + _ => LinuxArmel }; } @@ -169,7 +205,7 @@ public class FFMpegDownloader return Osx64; } - throw new PlatformNotSupportedException("Unsupported OS"); + throw new PlatformNotSupportedException("Unsupported OS or Architecture"); } } @@ -182,7 +218,7 @@ public class FFMpegDownloader [JsonPropertyName("bin")] public BinaryInfo? BinaryInfo { get; set; } } - private static readonly Dictionary FFBinariesAPIs = new() + 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" }, @@ -203,7 +239,7 @@ public class FFMpegDownloader /// private static async Task GetVersionInfo(FFMpegVersions version) { - if (!FFBinariesAPIs.TryGetValue(version, out var versionUri)) + if (!_FFBinariesAPIs.TryGetValue(version, out var versionUri)) { throw new FFMpegDownloaderException($"Invalid version selected: {version}", "contact dev"); } From 937db76e0067c143866af2629642d63c23433332 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 13:22:13 -0600 Subject: [PATCH 10/47] format fix for lint --- FFMpegCore/Helpers/FFMpegDownloader.cs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore/Helpers/FFMpegDownloader.cs index f465f1b..99e04c7 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore/Helpers/FFMpegDownloader.cs @@ -85,7 +85,7 @@ public class FFMpegDownloader return successList; } - + /// /// Download file from uri /// @@ -134,29 +134,21 @@ public class FFMpegDownloader private class BinaryInfo { - [JsonPropertyName("windows-64")] - public DownloadInfo? Windows64 { get; set; } + [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; } - [JsonPropertyName("windows-32")] - public DownloadInfo? Windows32 { get; set; } + [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; } - [JsonPropertyName("linux-32")] - public DownloadInfo? Linux32 { get; set; } + [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } - [JsonPropertyName("linux-64")] - public DownloadInfo? Linux64 { get; set; } + [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; } - [JsonPropertyName("linux-armhf")] - public DownloadInfo? LinuxArmhf { get; set; } + [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; } - [JsonPropertyName("linux-armel")] - public DownloadInfo? LinuxArmel { get; set; } + [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } - [JsonPropertyName("linux-arm64")] - public DownloadInfo? LinuxArm64 { get; set; } + [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; } - [JsonPropertyName("osx-64")] - public DownloadInfo? Osx64 { get; set; } + [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; } /// /// Automatically get the compatible download info for current os and architecture From dfc486db1d8b0a796dcda1319676e1fb98062753 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 13:42:22 -0600 Subject: [PATCH 11/47] ffmpeg downloader moved to seperate package --- .../FFMpegCore.Downloader.csproj | 23 ++ FFMpegCore.Downloader/FFMpegDownloader.cs | 272 ++++++++++++++++++ FFMpegCore.Test/FFMpegCore.Test.csproj | 1 + FFMpegCore.sln | 10 +- 4 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 FFMpegCore.Downloader/FFMpegCore.Downloader.csproj create mode 100644 FFMpegCore.Downloader/FFMpegDownloader.cs diff --git a/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj b/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj new file mode 100644 index 0000000..720cbf1 --- /dev/null +++ b/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.1 + enable + + + + true + FFMpeg downloader extension for FFMpegCore + 5.0.0 + ../nupkg + + + ffmpeg ffprobe convert video audio mediafile resize analyze download + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Kerry Cao + + + + + + + diff --git a/FFMpegCore.Downloader/FFMpegDownloader.cs b/FFMpegCore.Downloader/FFMpegDownloader.cs new file mode 100644 index 0000000..36f27c5 --- /dev/null +++ b/FFMpegCore.Downloader/FFMpegDownloader.cs @@ -0,0 +1,272 @@ +using System.IO.Compression; +using System.Net; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace FFMpegCore.Downloader; + +/// +/// Downloads the latest FFMpeg suite binaries from ffbinaries.com. +/// +public class FFMpegDownloader +{ + [Flags] + public enum FFMpegBinaries : ushort + { + FFMpeg, + FFProbe, + FFPlay + } + + public enum FFMpegVersions : ushort + { + Latest, + V4_4_1, + V4_2_1, + V4_2, + V4_1, + V4_0, + V3_4, + V3_3, + V3_2 + } + + public enum PlatformOverride : short + { + Windows64, + Windows32, + Linux64, + Linux32, + LinuxArmhf, + LinuxArmel, + LinuxArm64, + Osx64 + } + + /// + /// 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 binaries you want to download (ffmpeg, ffprobe, ffplay) + /// used to explicitly state the os and architecture 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, + PlatformOverride? platformOverride = null) + { + var versionInfo = await GetVersionInfo(version); + var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? + 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; + } + + /// + /// Download file from uri + /// + /// uri of the file + /// + private static MemoryStream DownloadFileAsSteam(Uri address) + { + var client = new WebClient(); + var fileStream = new MemoryStream(client.DownloadData(address)); + fileStream.Position = 0; + + return fileStream; + } + + /// + /// Extracts the binaries from the zip stream and saves them to the current binary folder + /// + /// steam of the zip file + /// + private static IEnumerable 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" 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)); + } + } + + return files; + } + + #region FFbinaries api + + private class DownloadInfo + { + [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; } + + [JsonPropertyName("ffprobe")] public string? FFProbe { get; set; } + + [JsonPropertyName("ffplay")] public string? FFPlay { get; set; } + } + + private class BinaryInfo + { + [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; } + + [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; } + + [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } + + [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; } + + [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; } + + [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } + + [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; } + + [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; } + + /// + /// Automatically get the compatible download info for current os and architecture + /// + /// + /// + /// + /// + public DownloadInfo? GetCompatibleDownloadInfo(PlatformOverride? platformOverride = null) + { + if (platformOverride is not null) + { + return platformOverride switch + { + PlatformOverride.Windows64 => Windows64, + PlatformOverride.Windows32 => Windows32, + PlatformOverride.Linux64 => Linux64, + PlatformOverride.Linux32 => Linux32, + PlatformOverride.LinuxArmhf => LinuxArmhf, + PlatformOverride.LinuxArmel => LinuxArmel, + PlatformOverride.LinuxArm64 => LinuxArm64, + PlatformOverride.Osx64 => Osx64, + _ => throw new ArgumentOutOfRangeException(nameof(platformOverride), platformOverride, null) + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => Linux32, + Architecture.X64 => Linux64, + Architecture.Arm => LinuxArmhf, + Architecture.Arm64 => LinuxArm64, + _ => LinuxArmel + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return Osx64; + } + + throw new PlatformNotSupportedException("Unsupported OS or Architecture"); + } + } + + 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; } = ""; +} diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index b78af1b..d54d269 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -24,6 +24,7 @@ + diff --git a/FFMpegCore.sln b/FFMpegCore.sln index 7ab0929..f7f42d2 100644 --- a/FFMpegCore.sln +++ b/FFMpegCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31005.135 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore", "FFMpegCore\FFMpegCore.csproj", "{19DE2EC2-9955-4712-8096-C22EF6713E4F}" EndProject @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.Syste EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.SkiaSharp", "FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj", "{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Downloader", "FFMpegCore.Downloader\FFMpegCore.Downloader.csproj", "{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.Build.0 = Release|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d978d7d9b4d42862a18a74d9358220c13fc7ba49 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 13:42:22 -0600 Subject: [PATCH 12/47] ffmpeg downloader moved to separate package --- .../FFMpegCore.Downloader.csproj | 23 +++++++++++++++++++ .../FFMpegDownloader.cs | 2 +- FFMpegCore.Test/DownloaderTests.cs | 2 +- FFMpegCore.Test/FFMpegCore.Test.csproj | 1 + FFMpegCore.sln | 10 ++++++-- 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 FFMpegCore.Downloader/FFMpegCore.Downloader.csproj rename {FFMpegCore/Helpers => FFMpegCore.Downloader}/FFMpegDownloader.cs (99%) diff --git a/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj b/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj new file mode 100644 index 0000000..720cbf1 --- /dev/null +++ b/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.1 + enable + + + + true + FFMpeg downloader extension for FFMpegCore + 5.0.0 + ../nupkg + + + ffmpeg ffprobe convert video audio mediafile resize analyze download + Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Kerry Cao + + + + + + + diff --git a/FFMpegCore/Helpers/FFMpegDownloader.cs b/FFMpegCore.Downloader/FFMpegDownloader.cs similarity index 99% rename from FFMpegCore/Helpers/FFMpegDownloader.cs rename to FFMpegCore.Downloader/FFMpegDownloader.cs index 99e04c7..36f27c5 100644 --- a/FFMpegCore/Helpers/FFMpegDownloader.cs +++ b/FFMpegCore.Downloader/FFMpegDownloader.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Text.Json.Serialization; -namespace FFMpegCore.Helpers; +namespace FFMpegCore.Downloader; /// /// Downloads the latest FFMpeg suite binaries from ffbinaries.com. diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index c677d5d..e394dc0 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,4 +1,4 @@ -using FFMpegCore.Helpers; +using FFMpegCore.Downloader; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index b78af1b..d54d269 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -24,6 +24,7 @@ + diff --git a/FFMpegCore.sln b/FFMpegCore.sln index 7ab0929..f7f42d2 100644 --- a/FFMpegCore.sln +++ b/FFMpegCore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31005.135 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore", "FFMpegCore\FFMpegCore.csproj", "{19DE2EC2-9955-4712-8096-C22EF6713E4F}" EndProject @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.Syste EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.SkiaSharp", "FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj", "{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Downloader", "FFMpegCore.Downloader\FFMpegCore.Downloader.csproj", "{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}.Release|Any CPU.Build.0 = Release|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ae5ae973fae456ef4071c3da91c7fbf61a919b32 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Wed, 6 Sep 2023 14:07:08 -0600 Subject: [PATCH 13/47] FFMpegCore.Downloader renamed to FFMpegCore.Extensions.Downloader --- .../FFMpegCore.Extensions.Downloader.csproj | 0 .../FFMpegDownloader.cs | 2 +- FFMpegCore.Test/DownloaderTests.cs | 2 +- FFMpegCore.Test/FFMpegCore.Test.csproj | 2 +- FFMpegCore.sln | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename FFMpegCore.Downloader/FFMpegCore.Downloader.csproj => FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj (100%) rename {FFMpegCore.Downloader => FFMpegCore.Extensions.Downloader}/FFMpegDownloader.cs (99%) diff --git a/FFMpegCore.Downloader/FFMpegCore.Downloader.csproj b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj similarity index 100% rename from FFMpegCore.Downloader/FFMpegCore.Downloader.csproj rename to FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj diff --git a/FFMpegCore.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs similarity index 99% rename from FFMpegCore.Downloader/FFMpegDownloader.cs rename to FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 36f27c5..097c9f6 100644 --- a/FFMpegCore.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Text.Json.Serialization; -namespace FFMpegCore.Downloader; +namespace FFMpegCore.Extensions.Downloader; /// /// Downloads the latest FFMpeg suite binaries from ffbinaries.com. diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index e394dc0..3d65a3d 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,4 +1,4 @@ -using FFMpegCore.Downloader; +using FFMpegCore.Extensions.Downloader; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; diff --git a/FFMpegCore.Test/FFMpegCore.Test.csproj b/FFMpegCore.Test/FFMpegCore.Test.csproj index d54d269..220f05c 100644 --- a/FFMpegCore.Test/FFMpegCore.Test.csproj +++ b/FFMpegCore.Test/FFMpegCore.Test.csproj @@ -24,7 +24,7 @@ - + diff --git a/FFMpegCore.sln b/FFMpegCore.sln index f7f42d2..b99a44e 100644 --- a/FFMpegCore.sln +++ b/FFMpegCore.sln @@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.Syste EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Extensions.SkiaSharp", "FFMpegCore.Extensions.SkiaSharp\FFMpegCore.Extensions.SkiaSharp.csproj", "{5A76F9B7-3681-4551-A9B6-8D3AC5DA1090}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Downloader", "FFMpegCore.Downloader\FFMpegCore.Downloader.csproj", "{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Extensions.Downloader", "FFMpegCore.Extensions.Downloader\FFMpegCore.Extensions.Downloader.csproj", "{5FA30158-CAB0-44FD-AD98-C31F5E3D5A56}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 5bedbdb1076e0a954d4f3a895491bdde85c1cb85 Mon Sep 17 00:00:00 2001 From: K <28016308+yuqian5@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:45:14 -0600 Subject: [PATCH 14/47] Update README.md --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index b2df37a..716d85f 100644 --- a/README.md +++ b/README.md @@ -165,14 +165,7 @@ If you want to use `System.Drawing.Bitmap`s as `IVideoFrame`s, a `BitmapVideoFra ## Runtime Auto Installation You can install a version of ffmpeg suite at runtime using `FFMpegDownloader.DownloadFFMpegSuite();` -Or you can download only the desired binary using `FFMpegDownloader.DownloadFFMpeg()`, `FFMpegDownloader.DownloadFFProbe()`, `FFMpegDownloader.DownloadFFPlay()`. - -| OS | Support | -|---------|:-------------------------------------------------:| -| Windows | x64 Fully Supported, x86 Up to version V4.2.1 | -| Mac OSX | NOT YET | -| Linux | NOT YET | - +This feature uses the api from [ffbinaries](https://ffbinaries.com/api). ## Manual Installation If you prefer to manually download them, visit [ffbinaries](https://ffbinaries.com/downloads) or [zeranoe Windows builds](https://ffmpeg.zeranoe.com/builds/). From 22fa0023947149dc0fe11b951783e9a5d7f645d8 Mon Sep 17 00:00:00 2001 From: K <28016308+yuqian5@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:50:59 -0600 Subject: [PATCH 15/47] Debug for CI failure --- FFMpegCore.Test/DownloaderTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 3d65a3d..8fcdc4a 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -6,6 +6,7 @@ namespace FFMpegCore.Test; [TestClass] public class DownloaderTests { + [Ignore] [TestMethod] public void GetAllLatestSuiteTest() { @@ -15,6 +16,7 @@ public class DownloaderTests Assert.IsTrue(binaries.Count >= 2); // many platforms have only ffmpeg and ffprobe } + [Ignore] [TestMethod] public void GetSpecificVersionTest() { From 6524b1cd4d90f1d204c359823045f20300d546ab Mon Sep 17 00:00:00 2001 From: K <28016308+yuqian5@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:59:11 -0600 Subject: [PATCH 16/47] Update DownloaderTests.cs --- FFMpegCore.Test/DownloaderTests.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 8fcdc4a..5a89a59 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -6,21 +6,17 @@ namespace FFMpegCore.Test; [TestClass] public class DownloaderTests { - [Ignore] [TestMethod] public void GetAllLatestSuiteTest() { - 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 + var binaries = FFMpegDownloader.DownloadFFMpegSuite(binaries: FFMpegDownloader.FFMpegBinaries.FFMpeg).Result; + Assert.IsTrue(binaries.Count == 1); // many platforms have only ffmpeg and ffprobe } - [Ignore] [TestMethod] public void GetSpecificVersionTest() { - var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegDownloader.FFMpegVersions.V4_0).Result; - Assert.IsTrue(binaries.Count == 2); + var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegDownloader.FFMpegVersions.V4_0, binaries: FFMpegDownloader.FFMpegBinaries.FFMpeg).Result; + Assert.IsTrue(binaries.Count == 1); } } From 09ae9447c854a82c51152aafc8ae00c42de4a907 Mon Sep 17 00:00:00 2001 From: K <28016308+yuqian5@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:59:25 -0700 Subject: [PATCH 17/47] version 5.1 and 6.1 added --- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 097c9f6..139921c 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -22,6 +22,8 @@ public class FFMpegDownloader public enum FFMpegVersions : ushort { Latest, + V6_1, + V5_1, V4_4_1, V4_2_1, V4_2, @@ -213,6 +215,8 @@ public class FFMpegDownloader private static readonly Dictionary _FFBinariesAPIs = new() { { FFMpegVersions.Latest, "https://ffbinaries.com/api/v1/version/latest" }, + { FFMpegVersions.V6_1, "https://ffbinaries.com/api/v1/version/6.1" }, + { FFMpegVersions.V5_1, "https://ffbinaries.com/api/v1/version/5.1" }, { 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" }, From 7be54963050f0950d7628dc9a4210e6a13e6f299 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:22:47 -0500 Subject: [PATCH 18/47] major refactoring --- .../Enums/FFMpegBinaries.cs | 9 + .../Enums/FFMpegVersions.cs | 29 ++ .../Enums/SupportedPlatforms.cs | 13 + .../Exceptions/FFMpegDownloaderException.cs | 18 ++ .../Extensions/EnumExtensions.cs | 21 ++ .../FFMpegDownloader.cs | 260 ++---------------- .../Models/BinaryInfo.cs | 82 ++++++ .../Models/DownloadInfo.cs | 15 + .../Models/VersionInfo.cs | 15 + .../Services/FFbinariesService.cs | 75 +++++ FFMpegCore.Test/DownloaderTests.cs | 7 +- 11 files changed, 300 insertions(+), 244 deletions(-) create mode 100644 FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs create mode 100644 FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs create mode 100644 FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs create mode 100644 FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs create mode 100644 FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs create mode 100644 FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs create mode 100644 FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs create mode 100644 FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs create mode 100644 FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs diff --git a/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs b/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs new file mode 100644 index 0000000..d92116a --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs @@ -0,0 +1,9 @@ +namespace FFMpegCore.Extensions.Downloader.Enums; + +[Flags] +public enum FFMpegBinaries : ushort +{ + FFMpeg, + FFProbe, + FFPlay +} diff --git a/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs new file mode 100644 index 0000000..7a9f476 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs @@ -0,0 +1,29 @@ +using System.ComponentModel; + +namespace FFMpegCore.Extensions.Downloader.Enums; + +public enum FFMpegVersions : ushort +{ + [Description("https://ffbinaries.com/api/v1/version/latest")] + Latest, + [Description("https://ffbinaries.com/api/v1/version/6.1")] + V6_1, + [Description("https://ffbinaries.com/api/v1/version/5.1")] + V5_1, + [Description("https://ffbinaries.com/api/v1/version/4.4.1")] + V4_4_1, + [Description("https://ffbinaries.com/api/v1/version/4.2.1")] + V4_2_1, + [Description("https://ffbinaries.com/api/v1/version/4.2")] + V4_2, + [Description("https://ffbinaries.com/api/v1/version/4.1")] + V4_1, + [Description("https://ffbinaries.com/api/v1/version/4.0")] + V4_0, + [Description("https://ffbinaries.com/api/v1/version/3.4")] + V3_4, + [Description("https://ffbinaries.com/api/v1/version/3.3")] + V3_3, + [Description("https://ffbinaries.com/api/v1/version/3.2")] + V3_2 +} diff --git a/FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs b/FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs new file mode 100644 index 0000000..0378f3e --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Enums/SupportedPlatforms.cs @@ -0,0 +1,13 @@ +namespace FFMpegCore.Extensions.Downloader.Enums; + +public enum SupportedPlatforms : ushort +{ + Windows64, + Windows32, + Linux64, + Linux32, + LinuxArmhf, + LinuxArmel, + LinuxArm64, + Osx64 +} diff --git a/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs b/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs new file mode 100644 index 0000000..c7d2ead --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs @@ -0,0 +1,18 @@ +namespace FFMpegCore.Extensions.Downloader.Exceptions; + +/// +/// Custom exception for FFMpegDownloader +/// +public class FFMpegDownloaderException : Exception +{ + public string Detail { get; set; } = ""; + + public FFMpegDownloaderException(string message) : base(message) + { + } + + public FFMpegDownloaderException(string message, string detail) : base(message) + { + Detail = detail; + } +} diff --git a/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..e2f5616 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; + +namespace FFMpegCore.Extensions.Downloader.Extensions; + +internal static class EnumExtensions +{ + public static string GetDescription(this Enum enumValue) + { + var field = enumValue.GetType().GetField(enumValue.ToString()); + if (field == null) + return enumValue.ToString(); + + var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false); + if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute) + { + return attribute.Description; + } + + return enumValue.ToString(); + } +} diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 139921c..adc9c74 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -1,51 +1,11 @@ -using System.IO.Compression; -using System.Net; -using System.Runtime.InteropServices; -using System.Text.Json; -using System.Text.Json.Serialization; +using FFMpegCore.Extensions.Downloader.Enums; +using FFMpegCore.Extensions.Downloader.Exceptions; +using FFMpegCore.Extensions.Downloader.Services; namespace FFMpegCore.Extensions.Downloader; -/// -/// Downloads the latest FFMpeg suite binaries from ffbinaries.com. -/// public class FFMpegDownloader { - [Flags] - public enum FFMpegBinaries : ushort - { - FFMpeg, - FFProbe, - FFPlay - } - - public enum FFMpegVersions : ushort - { - Latest, - V6_1, - V5_1, - V4_4_1, - V4_2_1, - V4_2, - V4_1, - V4_0, - V3_4, - V3_3, - V3_2 - } - - public enum PlatformOverride : short - { - Windows64, - Windows32, - Linux64, - Linux32, - LinuxArmhf, - LinuxArmel, - LinuxArm64, - Osx64 - } - /// /// Download the latest FFMpeg suite binaries for current platform /// @@ -56,221 +16,39 @@ public class FFMpegDownloader public static async Task> DownloadFFMpegSuite( FFMpegVersions version = FFMpegVersions.Latest, FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe, - PlatformOverride? platformOverride = null) + SupportedPlatforms? platformOverride = null) { - var versionInfo = await GetVersionInfo(version); + // get all available versions + var versionInfo = await FFbinariesService.GetVersionInfo(version); + + // get the download info for the current platform var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? throw new FFMpegDownloaderException("Failed to get compatible download info"); - + var successList = new List(); - - // if ffmpeg is selected + + // download ffmpeg if selected if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null) { - var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg)); - successList.AddRange(ExtractZipAndSave(zipStream)); + var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg)); + successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } - // if ffprobe is selected + // download ffprobe if selected if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null) { - var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFProbe)); - successList.AddRange(ExtractZipAndSave(zipStream)); + var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFProbe)); + successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } - // if ffplay is selected + // download ffplay if selected if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null) { - var zipStream = DownloadFileAsSteam(new Uri(downloadInfo.FFPlay)); - successList.AddRange(ExtractZipAndSave(zipStream)); + var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFPlay)); + successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } return successList; } - - /// - /// Download file from uri - /// - /// uri of the file - /// - private static MemoryStream DownloadFileAsSteam(Uri address) - { - var client = new WebClient(); - var fileStream = new MemoryStream(client.DownloadData(address)); - fileStream.Position = 0; - - return fileStream; - } - - /// - /// Extracts the binaries from the zip stream and saves them to the current binary folder - /// - /// steam of the zip file - /// - private static IEnumerable 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" 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)); - } - } - - return files; - } - - #region FFbinaries api - - private class DownloadInfo - { - [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; } - - [JsonPropertyName("ffprobe")] public string? FFProbe { get; set; } - - [JsonPropertyName("ffplay")] public string? FFPlay { get; set; } - } - - private class BinaryInfo - { - [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; } - - [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; } - - [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } - - [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; } - - [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; } - - [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } - - [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; } - - [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; } - - /// - /// Automatically get the compatible download info for current os and architecture - /// - /// - /// - /// - /// - public DownloadInfo? GetCompatibleDownloadInfo(PlatformOverride? platformOverride = null) - { - if (platformOverride is not null) - { - return platformOverride switch - { - PlatformOverride.Windows64 => Windows64, - PlatformOverride.Windows32 => Windows32, - PlatformOverride.Linux64 => Linux64, - PlatformOverride.Linux32 => Linux32, - PlatformOverride.LinuxArmhf => LinuxArmhf, - PlatformOverride.LinuxArmel => LinuxArmel, - PlatformOverride.LinuxArm64 => LinuxArm64, - PlatformOverride.Osx64 => Osx64, - _ => throw new ArgumentOutOfRangeException(nameof(platformOverride), platformOverride, null) - }; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return RuntimeInformation.OSArchitecture switch - { - Architecture.X86 => Linux32, - Architecture.X64 => Linux64, - Architecture.Arm => LinuxArmhf, - Architecture.Arm64 => LinuxArm64, - _ => LinuxArmel - }; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return Osx64; - } - - throw new PlatformNotSupportedException("Unsupported OS or Architecture"); - } - } - - 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.V6_1, "https://ffbinaries.com/api/v1/version/6.1" }, - { FFMpegVersions.V5_1, "https://ffbinaries.com/api/v1/version/5.1" }, - { 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; } = ""; -} diff --git a/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs new file mode 100644 index 0000000..e855b2d --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs @@ -0,0 +1,82 @@ +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; +using FFMpegCore.Extensions.Downloader.Enums; + +namespace FFMpegCore.Extensions.Downloader.Models; + +internal record BinaryInfo +{ + [JsonPropertyName("windows-64")] + public DownloadInfo? Windows64 { get; set; } + + [JsonPropertyName("windows-32")] + public DownloadInfo? Windows32 { get; set; } + + [JsonPropertyName("linux-32")] + public DownloadInfo? Linux32 { get; set; } + + [JsonPropertyName("linux-64")] + public DownloadInfo? Linux64 { get; set; } + + [JsonPropertyName("linux-armhf")] + public DownloadInfo? LinuxArmhf { get; set; } + + [JsonPropertyName("linux-armel")] + public DownloadInfo? LinuxArmel { get; set; } + + [JsonPropertyName("linux-arm64")] + public DownloadInfo? LinuxArm64 { get; set; } + + [JsonPropertyName("osx-64")] + public DownloadInfo? Osx64 { get; set; } + + /// + /// Automatically get the compatible download info for current os and architecture + /// + /// + /// + /// + /// + public DownloadInfo? GetCompatibleDownloadInfo(SupportedPlatforms? platformOverride = null) + { + if (platformOverride is not null) + { + return platformOverride switch + { + SupportedPlatforms.Windows64 => Windows64, + SupportedPlatforms.Windows32 => Windows32, + SupportedPlatforms.Linux64 => Linux64, + SupportedPlatforms.Linux32 => Linux32, + SupportedPlatforms.LinuxArmhf => LinuxArmhf, + SupportedPlatforms.LinuxArmel => LinuxArmel, + SupportedPlatforms.LinuxArm64 => LinuxArm64, + SupportedPlatforms.Osx64 => Osx64, + _ => throw new ArgumentOutOfRangeException(nameof(platformOverride), platformOverride, null) + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return RuntimeInformation.OSArchitecture == Architecture.X64 ? Windows64 : Windows32; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => Linux32, + Architecture.X64 => Linux64, + Architecture.Arm => LinuxArmhf, + Architecture.Arm64 => LinuxArm64, + _ => LinuxArmel + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return Osx64; + } + + throw new PlatformNotSupportedException("Unsupported OS or Architecture"); + } +} diff --git a/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs b/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs new file mode 100644 index 0000000..2e3e692 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace FFMpegCore.Extensions.Downloader.Models; + +internal record DownloadInfo +{ + [JsonPropertyName("ffmpeg")] + public string? FFMpeg { get; set; } + + [JsonPropertyName("ffprobe")] + public string? FFProbe { get; set; } + + [JsonPropertyName("ffplay")] + public string? FFPlay { get; set; } +} diff --git a/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs b/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs new file mode 100644 index 0000000..490f431 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace FFMpegCore.Extensions.Downloader.Models; + +internal record VersionInfo +{ + [JsonPropertyName("version")] + public string? Version { get; set; } + + [JsonPropertyName("permalink")] + public string? Permalink { get; set; } + + [JsonPropertyName("bin")] + public BinaryInfo? BinaryInfo { get; set; } +} diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs new file mode 100644 index 0000000..155d925 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -0,0 +1,75 @@ +using System.IO.Compression; +using System.Net; +using System.Text.Json; +using FFMpegCore.Extensions.Downloader.Enums; +using FFMpegCore.Extensions.Downloader.Exceptions; +using FFMpegCore.Extensions.Downloader.Extensions; +using FFMpegCore.Extensions.Downloader.Models; + +namespace FFMpegCore.Extensions.Downloader.Services; + +/// +/// Service to interact with ffbinaries.com API +/// +internal class FFbinariesService +{ + /// + /// Get version info from ffbinaries.com + /// + /// use to explicitly state the version of ffmpeg you want + /// + /// + internal static async Task GetVersionInfo(FFMpegVersions version) + { + var versionUri = version.GetDescription(); + + 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); + } + + /// + /// Download file from uri + /// + /// uri of the file + /// + internal static MemoryStream DownloadFileAsSteam(Uri address) + { + var client = new WebClient(); + var fileStream = new MemoryStream(client.DownloadData(address)); + fileStream.Position = 0; + + return fileStream; + } + + /// + /// Extracts the binaries from the zip stream and saves them to the current binary folder + /// + /// steam of the zip file + /// + internal static IEnumerable 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" 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)); + } + } + + return files; + } +} diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 5a89a59..1a86b4c 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,4 +1,5 @@ using FFMpegCore.Extensions.Downloader; +using FFMpegCore.Extensions.Downloader.Enums; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; @@ -9,14 +10,14 @@ public class DownloaderTests [TestMethod] public void GetAllLatestSuiteTest() { - var binaries = FFMpegDownloader.DownloadFFMpegSuite(binaries: FFMpegDownloader.FFMpegBinaries.FFMpeg).Result; - Assert.IsTrue(binaries.Count == 1); // many platforms have only ffmpeg and ffprobe + var binaries = FFMpegDownloader.DownloadFFMpegSuite().Result; + Assert.IsTrue(binaries.Count == 2); // many platforms have only ffmpeg and ffprobe } [TestMethod] public void GetSpecificVersionTest() { - var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegDownloader.FFMpegVersions.V4_0, binaries: FFMpegDownloader.FFMpegBinaries.FFMpeg).Result; + var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V4_0, binaries: FFMpegBinaries.FFMpeg).Result; Assert.IsTrue(binaries.Count == 1); } } From 8e32d68877b833b053c2b69be9c27c405043773d Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:30:22 -0500 Subject: [PATCH 19/47] format cleanup to avoid lint error --- .../Enums/FFMpegVersions.cs | 10 ++++++++ .../Extensions/EnumExtensions.cs | 3 ++- .../FFMpegCore.Extensions.Downloader.csproj | 2 +- .../FFMpegDownloader.cs | 7 +++--- .../Models/BinaryInfo.cs | 24 +++++++------------ .../Models/DownloadInfo.cs | 9 +++---- .../Models/VersionInfo.cs | 9 +++---- .../Services/FFbinariesService.cs | 2 +- 8 files changed, 31 insertions(+), 35 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs index 7a9f476..a2e5f09 100644 --- a/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs +++ b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs @@ -6,24 +6,34 @@ public enum FFMpegVersions : ushort { [Description("https://ffbinaries.com/api/v1/version/latest")] Latest, + [Description("https://ffbinaries.com/api/v1/version/6.1")] V6_1, + [Description("https://ffbinaries.com/api/v1/version/5.1")] V5_1, + [Description("https://ffbinaries.com/api/v1/version/4.4.1")] V4_4_1, + [Description("https://ffbinaries.com/api/v1/version/4.2.1")] V4_2_1, + [Description("https://ffbinaries.com/api/v1/version/4.2")] V4_2, + [Description("https://ffbinaries.com/api/v1/version/4.1")] V4_1, + [Description("https://ffbinaries.com/api/v1/version/4.0")] V4_0, + [Description("https://ffbinaries.com/api/v1/version/3.4")] V3_4, + [Description("https://ffbinaries.com/api/v1/version/3.3")] V3_3, + [Description("https://ffbinaries.com/api/v1/version/3.2")] V3_2 } diff --git a/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs index e2f5616..7930c13 100644 --- a/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs +++ b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs @@ -8,9 +8,10 @@ internal static class EnumExtensions { var field = enumValue.GetType().GetField(enumValue.ToString()); if (field == null) + { return enumValue.ToString(); + } - var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false); if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute) { return attribute.Description; diff --git a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj index 720cbf1..0c6f791 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj +++ b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj @@ -15,7 +15,7 @@ ffmpeg ffprobe convert video audio mediafile resize analyze download Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Kerry Cao - + diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index adc9c74..87c6fd9 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -20,13 +20,13 @@ public class FFMpegDownloader { // get all available versions var versionInfo = await FFbinariesService.GetVersionInfo(version); - + // get the download info for the current platform var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? throw new FFMpegDownloaderException("Failed to get compatible download info"); - + var successList = new List(); - + // download ffmpeg if selected if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null) { @@ -51,4 +51,3 @@ public class FFMpegDownloader return successList; } } - diff --git a/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs index e855b2d..e91fcee 100644 --- a/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs +++ b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs @@ -6,29 +6,21 @@ namespace FFMpegCore.Extensions.Downloader.Models; internal record BinaryInfo { - [JsonPropertyName("windows-64")] - public DownloadInfo? Windows64 { get; set; } + [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; } - [JsonPropertyName("windows-32")] - public DownloadInfo? Windows32 { get; set; } + [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; } - [JsonPropertyName("linux-32")] - public DownloadInfo? Linux32 { get; set; } + [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } - [JsonPropertyName("linux-64")] - public DownloadInfo? Linux64 { get; set; } + [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; } - [JsonPropertyName("linux-armhf")] - public DownloadInfo? LinuxArmhf { get; set; } + [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; } - [JsonPropertyName("linux-armel")] - public DownloadInfo? LinuxArmel { get; set; } + [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } - [JsonPropertyName("linux-arm64")] - public DownloadInfo? LinuxArm64 { get; set; } + [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; } - [JsonPropertyName("osx-64")] - public DownloadInfo? Osx64 { get; set; } + [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; } /// /// Automatically get the compatible download info for current os and architecture diff --git a/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs b/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs index 2e3e692..5b7c0fd 100644 --- a/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs +++ b/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs @@ -4,12 +4,9 @@ namespace FFMpegCore.Extensions.Downloader.Models; internal record DownloadInfo { - [JsonPropertyName("ffmpeg")] - public string? FFMpeg { get; set; } + [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; } - [JsonPropertyName("ffprobe")] - public string? FFProbe { get; set; } + [JsonPropertyName("ffprobe")] public string? FFProbe { get; set; } - [JsonPropertyName("ffplay")] - public string? FFPlay { get; set; } + [JsonPropertyName("ffplay")] public string? FFPlay { get; set; } } diff --git a/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs b/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs index 490f431..ef24f62 100644 --- a/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs +++ b/FFMpegCore.Extensions.Downloader/Models/VersionInfo.cs @@ -4,12 +4,9 @@ namespace FFMpegCore.Extensions.Downloader.Models; internal record VersionInfo { - [JsonPropertyName("version")] - public string? Version { get; set; } + [JsonPropertyName("version")] public string? Version { get; set; } - [JsonPropertyName("permalink")] - public string? Permalink { get; set; } + [JsonPropertyName("permalink")] public string? Permalink { get; set; } - [JsonPropertyName("bin")] - public BinaryInfo? BinaryInfo { get; set; } + [JsonPropertyName("bin")] public BinaryInfo? BinaryInfo { get; set; } } diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs index 155d925..2a6c616 100644 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -37,7 +37,7 @@ internal class FFbinariesService return versionInfo ?? throw new FFMpegDownloaderException($"Failed to deserialize version info from {versionUri}", jsonString); } - + /// /// Download file from uri /// From 91a2ceaceb12f4f74746f7a5459096773f76d4ff Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:34:57 -0500 Subject: [PATCH 20/47] Test update to try and resolve failed tests --- FFMpegCore.Test/DownloaderTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 1a86b4c..18c6497 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -7,17 +7,17 @@ namespace FFMpegCore.Test; [TestClass] public class DownloaderTests { + [TestMethod] + public void GetSpecificVersionTest() + { + var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1).Result; + Assert.IsTrue(binaries.Count == 1); + } + [TestMethod] public void GetAllLatestSuiteTest() { var binaries = FFMpegDownloader.DownloadFFMpegSuite().Result; Assert.IsTrue(binaries.Count == 2); // many platforms have only ffmpeg and ffprobe } - - [TestMethod] - public void GetSpecificVersionTest() - { - var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V4_0, binaries: FFMpegBinaries.FFMpeg).Result; - Assert.IsTrue(binaries.Count == 1); - } } From 33098ad4def1388266dd374c5873a3abcbbab28c Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:37:03 -0500 Subject: [PATCH 21/47] linter fix --- FFMpegCore.Test/DownloaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 18c6497..9f174e6 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -13,7 +13,7 @@ public class DownloaderTests var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1).Result; Assert.IsTrue(binaries.Count == 1); } - + [TestMethod] public void GetAllLatestSuiteTest() { From 88c7f7bf7c86e5e0d1ad58b0a01cc7bbf2751eb9 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:39:35 -0500 Subject: [PATCH 22/47] debug --- FFMpegCore.Test/DownloaderTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 9f174e6..59caca2 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; +[Ignore] [TestClass] public class DownloaderTests { From 9f6249cb5f324291abfd08b47c0e0f05fc210db8 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Mon, 27 Jan 2025 22:43:20 -0500 Subject: [PATCH 23/47] re-enable DownloaderTests.cs --- FFMpegCore.Test/DownloaderTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 59caca2..9f174e6 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -4,7 +4,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; -[Ignore] [TestClass] public class DownloaderTests { From 644c41df90fdb8c63f695de095555fc2294f54f7 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Tue, 28 Jan 2025 21:41:44 -0500 Subject: [PATCH 24/47] GetSpecificVersionTest assertion fix --- FFMpegCore.Test/DownloaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 9f174e6..f2eb563 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -11,7 +11,7 @@ public class DownloaderTests public void GetSpecificVersionTest() { var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1).Result; - Assert.IsTrue(binaries.Count == 1); + Assert.IsTrue(binaries.Count == 2); } [TestMethod] From 1c03587cd887500f65af601497e1930a8a317fca Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Tue, 28 Jan 2025 21:42:12 -0500 Subject: [PATCH 25/47] obsolete webclient replaced with httpclient --- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 6 +++--- .../Services/FFbinariesService.cs | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 87c6fd9..0def62b 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -30,21 +30,21 @@ public class FFMpegDownloader // download ffmpeg if selected if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null) { - var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg)); + await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } // download ffprobe if selected if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null) { - var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFProbe)); + await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFProbe)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } // download ffplay if selected if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null) { - var zipStream = FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFPlay)); + await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFPlay)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs index 2a6c616..dd35513 100644 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -43,13 +43,10 @@ internal class FFbinariesService /// /// uri of the file /// - internal static MemoryStream DownloadFileAsSteam(Uri address) + internal static async Task DownloadFileAsSteam(Uri address) { - var client = new WebClient(); - var fileStream = new MemoryStream(client.DownloadData(address)); - fileStream.Position = 0; - - return fileStream; + var client = new HttpClient(); + return await client.GetStreamAsync(address); } /// From 483c52677272208ba3620a6607333d96d93d74b3 Mon Sep 17 00:00:00 2001 From: Kerry Cao Date: Tue, 28 Jan 2025 21:44:34 -0500 Subject: [PATCH 26/47] linter fix --- FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs index dd35513..1b7edc2 100644 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -1,5 +1,4 @@ using System.IO.Compression; -using System.Net; using System.Text.Json; using FFMpegCore.Extensions.Downloader.Enums; using FFMpegCore.Extensions.Downloader.Exceptions; From f919a05d43497fb6b975571aacd6d5bcf31ed25f Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 10:24:26 +0200 Subject: [PATCH 27/47] Make download tests run async --- FFMpegCore.Test/DownloaderTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index f2eb563..413aa31 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,6 +1,5 @@ using FFMpegCore.Extensions.Downloader; using FFMpegCore.Extensions.Downloader.Enums; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FFMpegCore.Test; @@ -8,16 +7,16 @@ namespace FFMpegCore.Test; public class DownloaderTests { [TestMethod] - public void GetSpecificVersionTest() + public async Task GetSpecificVersionTest() { - var binaries = FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1).Result; - Assert.IsTrue(binaries.Count == 2); + var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1); + Assert.HasCount(2, binaries); } [TestMethod] - public void GetAllLatestSuiteTest() + public async Task GetAllLatestSuiteTest() { - var binaries = FFMpegDownloader.DownloadFFMpegSuite().Result; - Assert.IsTrue(binaries.Count == 2); // many platforms have only ffmpeg and ffprobe + var binaries = await FFMpegDownloader.DownloadFFMpegSuite(); + Assert.HasCount(2, binaries); } } From 2852692250e7960ac295a256161d332cdfd3b7bc Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 10:33:29 +0200 Subject: [PATCH 28/47] Fix typo --- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 6 +++--- .../Services/FFbinariesService.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 0def62b..9cad618 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -30,21 +30,21 @@ public class FFMpegDownloader // download ffmpeg if selected if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null) { - await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFMpeg)); + await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFMpeg)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } // download ffprobe if selected if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null) { - await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFProbe)); + await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFProbe)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } // download ffplay if selected if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null) { - await using var zipStream = await FFbinariesService.DownloadFileAsSteam(new Uri(downloadInfo.FFPlay)); + await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFPlay)); successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); } diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs index 1b7edc2..479d264 100644 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -42,7 +42,7 @@ internal class FFbinariesService /// /// uri of the file /// - internal static async Task DownloadFileAsSteam(Uri address) + internal static async Task DownloadFileAsStream(Uri address) { var client = new HttpClient(); return await client.GetStreamAsync(address); From b1eb8da6a95e45d36ac253db9daadcf64df82920 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 10:34:27 +0200 Subject: [PATCH 29/47] Delete downloaded binaries after assert --- FFMpegCore.Test/DownloaderTests.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 413aa31..165e813 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -10,13 +10,27 @@ public class DownloaderTests public async Task GetSpecificVersionTest() { var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1); - Assert.HasCount(2, binaries); + try + { + Assert.HasCount(2, binaries); + } + finally + { + binaries.ForEach(File.Delete); + } } [TestMethod] public async Task GetAllLatestSuiteTest() { var binaries = await FFMpegDownloader.DownloadFFMpegSuite(); - Assert.HasCount(2, binaries); + try + { + Assert.HasCount(2, binaries); + } + finally + { + binaries.ForEach(File.Delete); + } } } From af47c9ae9446add1f6956f770587e66c03fc7c9c Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 10:34:49 +0200 Subject: [PATCH 30/47] Throw exception if binary folder not specified --- .../Services/FFbinariesService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs index 479d264..9e46db3 100644 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs @@ -55,6 +55,11 @@ internal class FFbinariesService /// internal static IEnumerable ExtractZipAndSave(Stream zipStream) { + if (string.IsNullOrEmpty(GlobalFFOptions.Current.BinaryFolder)) + { + throw new FFMpegDownloaderException("Binary folder not specified"); + } + using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); List files = new(); foreach (var entry in archive.Entries) From 07253b4b496e62ec25582195d2bf6a2124e74338 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 10:35:05 +0200 Subject: [PATCH 31/47] Update nuget package authors --- .../FFMpegCore.Extensions.Downloader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj index 0c6f791..aa4ff31 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj +++ b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj @@ -13,7 +13,7 @@ ffmpeg ffprobe convert video audio mediafile resize analyze download - Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev, Kerry Cao + Kerry Cao, Malte Rosenbjerg From 3be446880ae6335c71ff6b112746bc587145a8e4 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 14:45:11 +0200 Subject: [PATCH 32/47] Only run downloader tests on windows and linux since macos CI runs on arm which builds are not available for --- .../Enums/EnumExtensions.cs | 13 ++++++ FFMpegCore.Test/DownloaderTests.cs | 5 ++- .../Utilities/OsSpecificTestMethod.cs | 42 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs create mode 100644 FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs diff --git a/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs b/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs new file mode 100644 index 0000000..7743820 --- /dev/null +++ b/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs @@ -0,0 +1,13 @@ +namespace FFMpegCore.Extensions.Downloader.Enums; + +public static class EnumExtensions +{ + public static TEnum[] GetFlags(this TEnum input) where TEnum : Enum + { + return Enum.GetValues(input.GetType()) + .Cast() + .Where(input.HasFlag) + .Cast() + .ToArray(); + } +} diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 165e813..7acf30a 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -1,12 +1,13 @@ using FFMpegCore.Extensions.Downloader; using FFMpegCore.Extensions.Downloader.Enums; +using FFMpegCore.Test.Utilities; namespace FFMpegCore.Test; [TestClass] public class DownloaderTests { - [TestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetSpecificVersionTest() { var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1); @@ -20,7 +21,7 @@ public class DownloaderTests } } - [TestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetAllLatestSuiteTest() { var binaries = await FFMpegDownloader.DownloadFFMpegSuite(); diff --git a/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs new file mode 100644 index 0000000..b3563a9 --- /dev/null +++ b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using FFMpegCore.Extensions.Downloader.Enums; + +namespace FFMpegCore.Test.Utilities; + +[Flags] +internal enum OsPlatforms : ushort +{ + Windows, + Linux, + MacOS +} + +internal class OsSpecificTestMethod : TestMethodAttribute +{ + private readonly IEnumerable _supportedOsPlatforms; + + public OsSpecificTestMethod(OsPlatforms supportedOsPlatforms, [CallerFilePath] string callerFilePath = "", + [CallerLineNumber] int callerLineNumber = -1) : base(callerFilePath, callerLineNumber) + { + _supportedOsPlatforms = supportedOsPlatforms.GetFlags() + .Select(flag => OSPlatform.Create(flag.ToString().ToUpperInvariant())) + .ToArray(); + } + + public override async Task ExecuteAsync(ITestMethod testMethod) + { + if (_supportedOsPlatforms.Any(RuntimeInformation.IsOSPlatform)) + { + return await base.ExecuteAsync(testMethod); + } + + var message = $"Test only executed on specific platforms: {string.Join(", ", _supportedOsPlatforms.Select(platform => platform.ToString()))}"; + { + return + [ + new TestResult { Outcome = UnitTestOutcome.Inconclusive, TestFailureException = new AssertInconclusiveException(message) } + ]; + } + } +} From d7fec62d2e5a9cc9ec3f7b11f0395c3058eaa01b Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:01:00 +0200 Subject: [PATCH 33/47] Only run downloader tests on windows and linux since macos CI runs on arm which builds are not available for --- .../Enums/EnumExtensions.cs | 13 ------------- .../Extensions/EnumExtensions.cs | 13 +++++++++++-- FFMpegCore.Test/DownloaderTests.cs | 8 +++++--- FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) delete mode 100644 FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs diff --git a/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs b/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs deleted file mode 100644 index 7743820..0000000 --- a/FFMpegCore.Extensions.Downloader/Enums/EnumExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FFMpegCore.Extensions.Downloader.Enums; - -public static class EnumExtensions -{ - public static TEnum[] GetFlags(this TEnum input) where TEnum : Enum - { - return Enum.GetValues(input.GetType()) - .Cast() - .Where(input.HasFlag) - .Cast() - .ToArray(); - } -} diff --git a/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs index 7930c13..3336f11 100644 --- a/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs +++ b/FFMpegCore.Extensions.Downloader/Extensions/EnumExtensions.cs @@ -2,9 +2,9 @@ namespace FFMpegCore.Extensions.Downloader.Extensions; -internal static class EnumExtensions +public static class EnumExtensions { - public static string GetDescription(this Enum enumValue) + internal static string GetDescription(this Enum enumValue) { var field = enumValue.GetType().GetField(enumValue.ToString()); if (field == null) @@ -19,4 +19,13 @@ internal static class EnumExtensions return enumValue.ToString(); } + + public static TEnum[] GetFlags(this TEnum input) where TEnum : Enum + { + return Enum.GetValues(input.GetType()) + .Cast() + .Where(input.HasFlag) + .Cast() + .ToArray(); + } } diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 7acf30a..a1a92a8 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -10,7 +10,8 @@ public class DownloaderTests [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetSpecificVersionTest() { - var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1); + var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; + var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1, options: options); try { Assert.HasCount(2, binaries); @@ -21,10 +22,11 @@ public class DownloaderTests } } - [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] + [TestMethod] public async Task GetAllLatestSuiteTest() { - var binaries = await FFMpegDownloader.DownloadFFMpegSuite(); + var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; + var binaries = await FFMpegDownloader.DownloadFFMpegSuite(options: options); try { Assert.HasCount(2, binaries); diff --git a/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs index b3563a9..d03eb27 100644 --- a/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs +++ b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using FFMpegCore.Extensions.Downloader.Enums; +using FFMpegCore.Extensions.Downloader.Extensions; namespace FFMpegCore.Test.Utilities; From ad27076c1f8fc6d6d0a92a0b946d1a9fd8cadc4a Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:01:11 +0200 Subject: [PATCH 34/47] Refactor for conciseness --- .../FFMpegDownloader.cs | 81 +++++++++++++------ .../Models/BinaryInfo.cs | 22 ++--- .../Models/DownloadInfo.cs | 12 --- .../Services/FFbinariesService.cs | 76 ----------------- 4 files changed, 68 insertions(+), 123 deletions(-) delete mode 100644 FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs delete mode 100644 FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 9cad618..a05f9c6 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -1,53 +1,86 @@ -using FFMpegCore.Extensions.Downloader.Enums; +using System.IO.Compression; +using System.Text.Json; +using FFMpegCore.Extensions.Downloader.Enums; using FFMpegCore.Extensions.Downloader.Exceptions; -using FFMpegCore.Extensions.Downloader.Services; +using FFMpegCore.Extensions.Downloader.Extensions; +using FFMpegCore.Extensions.Downloader.Models; namespace FFMpegCore.Extensions.Downloader; -public class FFMpegDownloader +public static class FFMpegDownloader { /// /// 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 binaries you want to download (ffmpeg, ffprobe, ffplay) + /// used for specifying binary folder to download binaries into. If not provided, GlobalFFOptions are used /// used to explicitly state the os and architecture 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, + FFOptions? options = null, SupportedPlatforms? platformOverride = null) { - // get all available versions - var versionInfo = await FFbinariesService.GetVersionInfo(version); + using var httpClient = new HttpClient(); - // get the download info for the current platform - var downloadInfo = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? - throw new FFMpegDownloaderException("Failed to get compatible download info"); + var versionInfo = await httpClient.GetVersionInfo(version); + var binariesDictionary = versionInfo.BinaryInfo?.GetCompatibleDownloadInfo(platformOverride) ?? + throw new FFMpegDownloaderException("Failed to get compatible download info"); var successList = new List(); - - // download ffmpeg if selected - if (binaries.HasFlag(FFMpegBinaries.FFMpeg) && downloadInfo.FFMpeg is not null) + var relevantOptions = options ?? GlobalFFOptions.Current; + if (string.IsNullOrEmpty(relevantOptions.BinaryFolder)) { - await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFMpeg)); - successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); + throw new FFMpegDownloaderException("Binary folder not specified"); } - // download ffprobe if selected - if (binaries.HasFlag(FFMpegBinaries.FFProbe) && downloadInfo.FFProbe is not null) + var binaryFlags = binaries.GetFlags(); + foreach (var binaryFlag in binaryFlags) { - await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFProbe)); - successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); - } - - // download ffplay if selected - if (binaries.HasFlag(FFMpegBinaries.FFPlay) && downloadInfo.FFPlay is not null) - { - await using var zipStream = await FFbinariesService.DownloadFileAsStream(new Uri(downloadInfo.FFPlay)); - successList.AddRange(FFbinariesService.ExtractZipAndSave(zipStream)); + if (binariesDictionary.TryGetValue(binaryFlag.ToString().ToLowerInvariant(), out var binaryUrl)) + { + await using var zipStream = await httpClient.GetStreamAsync(new Uri(binaryUrl)); + var extracted = ExtractZipAndSave(zipStream, relevantOptions.BinaryFolder); + successList.AddRange(extracted); + } } return successList; } + + private static async Task GetVersionInfo(this HttpClient client, FFMpegVersions version) + { + var versionUri = version.GetDescription(); + + 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); + } + + private static IEnumerable ExtractZipAndSave(Stream zipStream, string binaryFolder) + { + using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); + List files = new(); + foreach (var entry in archive.Entries) + { + if (entry.Name is "ffmpeg" or "ffmpeg.exe" or "ffprobe.exe" or "ffprobe" or "ffplay.exe" or "ffplay") + { + var filePath = Path.Combine(binaryFolder, entry.Name); + entry.ExtractToFile(filePath, true); + files.Add(filePath); + } + } + + return files; + } } diff --git a/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs index e91fcee..d02cb14 100644 --- a/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs +++ b/FFMpegCore.Extensions.Downloader/Models/BinaryInfo.cs @@ -4,23 +4,23 @@ using FFMpegCore.Extensions.Downloader.Enums; namespace FFMpegCore.Extensions.Downloader.Models; -internal record BinaryInfo +internal class BinaryInfo { - [JsonPropertyName("windows-64")] public DownloadInfo? Windows64 { get; set; } + [JsonPropertyName("windows-64")] public Dictionary? Windows64 { get; set; } - [JsonPropertyName("windows-32")] public DownloadInfo? Windows32 { get; set; } + [JsonPropertyName("windows-32")] public Dictionary? Windows32 { get; set; } - [JsonPropertyName("linux-32")] public DownloadInfo? Linux32 { get; set; } + [JsonPropertyName("linux-32")] public Dictionary? Linux32 { get; set; } - [JsonPropertyName("linux-64")] public DownloadInfo? Linux64 { get; set; } + [JsonPropertyName("linux-64")] public Dictionary? Linux64 { get; set; } - [JsonPropertyName("linux-armhf")] public DownloadInfo? LinuxArmhf { get; set; } + [JsonPropertyName("linux-armhf")] public Dictionary? LinuxArmhf { get; set; } - [JsonPropertyName("linux-armel")] public DownloadInfo? LinuxArmel { get; set; } + [JsonPropertyName("linux-armel")] public Dictionary? LinuxArmel { get; set; } - [JsonPropertyName("linux-arm64")] public DownloadInfo? LinuxArm64 { get; set; } + [JsonPropertyName("linux-arm64")] public Dictionary? LinuxArm64 { get; set; } - [JsonPropertyName("osx-64")] public DownloadInfo? Osx64 { get; set; } + [JsonPropertyName("osx-64")] public Dictionary? Osx64 { get; set; } /// /// Automatically get the compatible download info for current os and architecture @@ -29,7 +29,7 @@ internal record BinaryInfo /// /// /// - public DownloadInfo? GetCompatibleDownloadInfo(SupportedPlatforms? platformOverride = null) + public Dictionary? GetCompatibleDownloadInfo(SupportedPlatforms? platformOverride = null) { if (platformOverride is not null) { @@ -64,7 +64,7 @@ internal record BinaryInfo }; } - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && RuntimeInformation.OSArchitecture == Architecture.X64) { return Osx64; } diff --git a/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs b/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs deleted file mode 100644 index 5b7c0fd..0000000 --- a/FFMpegCore.Extensions.Downloader/Models/DownloadInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace FFMpegCore.Extensions.Downloader.Models; - -internal record DownloadInfo -{ - [JsonPropertyName("ffmpeg")] public string? FFMpeg { get; set; } - - [JsonPropertyName("ffprobe")] public string? FFProbe { get; set; } - - [JsonPropertyName("ffplay")] public string? FFPlay { get; set; } -} diff --git a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs b/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs deleted file mode 100644 index 9e46db3..0000000 --- a/FFMpegCore.Extensions.Downloader/Services/FFbinariesService.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.IO.Compression; -using System.Text.Json; -using FFMpegCore.Extensions.Downloader.Enums; -using FFMpegCore.Extensions.Downloader.Exceptions; -using FFMpegCore.Extensions.Downloader.Extensions; -using FFMpegCore.Extensions.Downloader.Models; - -namespace FFMpegCore.Extensions.Downloader.Services; - -/// -/// Service to interact with ffbinaries.com API -/// -internal class FFbinariesService -{ - /// - /// Get version info from ffbinaries.com - /// - /// use to explicitly state the version of ffmpeg you want - /// - /// - internal static async Task GetVersionInfo(FFMpegVersions version) - { - var versionUri = version.GetDescription(); - - 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); - } - - /// - /// Download file from uri - /// - /// uri of the file - /// - internal static async Task DownloadFileAsStream(Uri address) - { - var client = new HttpClient(); - return await client.GetStreamAsync(address); - } - - /// - /// Extracts the binaries from the zip stream and saves them to the current binary folder - /// - /// steam of the zip file - /// - internal static IEnumerable ExtractZipAndSave(Stream zipStream) - { - if (string.IsNullOrEmpty(GlobalFFOptions.Current.BinaryFolder)) - { - throw new FFMpegDownloaderException("Binary folder not specified"); - } - - using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); - List files = new(); - foreach (var entry in archive.Entries) - { - 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)); - } - } - - return files; - } -} From 8f2d3d7125a810228709579adbf01c8155d9fc11 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:07:10 +0200 Subject: [PATCH 35/47] Remove specification of .NET Standard 2.1 --- .../FFMpegCore.Extensions.Downloader.csproj | 5 ----- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj index aa4ff31..bd18fb3 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj +++ b/FFMpegCore.Extensions.Downloader/FFMpegCore.Extensions.Downloader.csproj @@ -1,10 +1,5 @@  - - netstandard2.1 - enable - - true FFMpeg downloader extension for FFMpegCore diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index a05f9c6..f539b3a 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -41,7 +41,7 @@ public static class FFMpegDownloader { if (binariesDictionary.TryGetValue(binaryFlag.ToString().ToLowerInvariant(), out var binaryUrl)) { - await using var zipStream = await httpClient.GetStreamAsync(new Uri(binaryUrl)); + using var zipStream = await httpClient.GetStreamAsync(new Uri(binaryUrl)); var extracted = ExtractZipAndSave(zipStream, relevantOptions.BinaryFolder); successList.AddRange(extracted); } From 3404d63655f2c5d4fdef2b918a6ea06ec1a04f9f Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:07:45 +0200 Subject: [PATCH 36/47] Rename download function --- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 2 +- FFMpegCore.Test/DownloaderTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index f539b3a..ef2406a 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -17,8 +17,8 @@ public static class FFMpegDownloader /// used for specifying binary folder to download binaries into. If not provided, GlobalFFOptions are used /// used to explicitly state the os and architecture you want to download /// a list of the binaries that have been successfully downloaded - public static async Task> DownloadFFMpegSuite( FFMpegVersions version = FFMpegVersions.Latest, + public static async Task> DownloadBinaries( FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe, FFOptions? options = null, SupportedPlatforms? platformOverride = null) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index a1a92a8..77bb1fd 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -11,7 +11,7 @@ public class DownloaderTests public async Task GetSpecificVersionTest() { var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; - var binaries = await FFMpegDownloader.DownloadFFMpegSuite(FFMpegVersions.V6_1, options: options); + var binaries = await FFMpegDownloader.DownloadBinaries(FFMpegVersions.V6_1, options: options); try { Assert.HasCount(2, binaries); @@ -26,7 +26,7 @@ public class DownloaderTests public async Task GetAllLatestSuiteTest() { var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; - var binaries = await FFMpegDownloader.DownloadFFMpegSuite(options: options); + var binaries = await FFMpegDownloader.DownloadBinaries(options: options); try { Assert.HasCount(2, binaries); From 1a49b4eec3d91559c0e81d4e3af888b00b6197fd Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:08:00 +0200 Subject: [PATCH 37/47] Rename Latest enum member --- FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs | 2 +- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs index a2e5f09..c9f5dd3 100644 --- a/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs +++ b/FFMpegCore.Extensions.Downloader/Enums/FFMpegVersions.cs @@ -5,7 +5,7 @@ namespace FFMpegCore.Extensions.Downloader.Enums; public enum FFMpegVersions : ushort { [Description("https://ffbinaries.com/api/v1/version/latest")] - Latest, + LatestAvailable, [Description("https://ffbinaries.com/api/v1/version/6.1")] V6_1, diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index ef2406a..06c91d5 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -17,8 +17,8 @@ public static class FFMpegDownloader /// used for specifying binary folder to download binaries into. If not provided, GlobalFFOptions are used /// used to explicitly state the os and architecture you want to download /// a list of the binaries that have been successfully downloaded - FFMpegVersions version = FFMpegVersions.Latest, public static async Task> DownloadBinaries( + FFMpegVersions version = FFMpegVersions.LatestAvailable, FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe, FFOptions? options = null, SupportedPlatforms? platformOverride = null) From bfcb1b95448ef2c0578a8be0569d5bdb54931f54 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:08:13 +0200 Subject: [PATCH 38/47] Minor refactor to use yield return --- FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs index 06c91d5..f616c31 100644 --- a/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs +++ b/FFMpegCore.Extensions.Downloader/FFMpegDownloader.cs @@ -70,17 +70,14 @@ public static class FFMpegDownloader private static IEnumerable ExtractZipAndSave(Stream zipStream, string binaryFolder) { using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); - List files = new(); foreach (var entry in archive.Entries) { if (entry.Name is "ffmpeg" or "ffmpeg.exe" or "ffprobe.exe" or "ffprobe" or "ffplay.exe" or "ffplay") { var filePath = Path.Combine(binaryFolder, entry.Name); entry.ExtractToFile(filePath, true); - files.Add(filePath); + yield return filePath; } } - - return files; } } From a71a55741f42aa3f381d92528ceb1fb215e183bc Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:08:35 +0200 Subject: [PATCH 39/47] Use OsSpecificTestMethod on GetAllLatestSuiteTest --- FFMpegCore.Test/DownloaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 77bb1fd..7c045d6 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -22,7 +22,7 @@ public class DownloaderTests } } - [TestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetAllLatestSuiteTest() { var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; From 2a25bff836671a2a823143e6dcfc3acab81fb4ef Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:14:42 +0200 Subject: [PATCH 40/47] Remove WindowsOnlyTestMethod and use OsSpecificTestMethod --- .../Utilities/WindowsOnlyTestMethod.cs | 28 ------------------- FFMpegCore.Test/VideoTest.cs | 20 ++++++------- 2 files changed, 10 insertions(+), 38 deletions(-) delete mode 100644 FFMpegCore.Test/Utilities/WindowsOnlyTestMethod.cs diff --git a/FFMpegCore.Test/Utilities/WindowsOnlyTestMethod.cs b/FFMpegCore.Test/Utilities/WindowsOnlyTestMethod.cs deleted file mode 100644 index 9a87749..0000000 --- a/FFMpegCore.Test/Utilities/WindowsOnlyTestMethod.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace FFMpegCore.Test.Utilities; - -public class WindowsOnlyTestMethod : TestMethodAttribute -{ - public WindowsOnlyTestMethod([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) - : base(callerFilePath, callerLineNumber) - { - } - - public override async Task ExecuteAsync(ITestMethod testMethod) - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var message = "Test not executed on other platforms than Windows"; - { - return - [ - new TestResult { Outcome = UnitTestOutcome.Inconclusive, TestFailureException = new AssertInconclusiveException(message) } - ]; - } - } - - return await base.ExecuteAsync(testMethod); - } -} diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 010ec44..7946552 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -93,7 +93,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] [DataRow(PixelFormat.Format24bppRgb)] [DataRow(PixelFormat.Format32bppArgb)] @@ -125,7 +125,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public void Video_ToMP4_Args_Pipe_DifferentImageSizes_WindowsOnly() { @@ -157,7 +157,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public async Task Video_ToMP4_Args_Pipe_DifferentImageSizes_WindowsOnly_Async() { @@ -189,7 +189,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public void Video_ToMP4_Args_Pipe_DifferentPixelFormats_WindowsOnly() { @@ -222,7 +222,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public async Task Video_ToMP4_Args_Pipe_DifferentPixelFormats_WindowsOnly_Async() { @@ -397,7 +397,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] [DataRow(PixelFormat.Format24bppRgb)] [DataRow(PixelFormat.Format32bppArgb)] @@ -446,7 +446,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] [DataRow(SKColorType.Rgb565)] [DataRow(SKColorType.Bgra8888)] @@ -483,7 +483,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] [DataRow(PixelFormat.Format24bppRgb)] [DataRow(PixelFormat.Format32bppArgb)] @@ -516,7 +516,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public void Video_Snapshot_InMemory_SystemDrawingCommon() { @@ -840,7 +840,7 @@ public class VideoTest } [SupportedOSPlatform("windows")] - [WindowsOnlyTestMethod] + [OsSpecificTestMethod(OsPlatforms.Windows)] [Timeout(BaseTimeoutMilliseconds, CooperativeCancellation = true)] public void Video_TranscodeInMemory_WindowsOnly() { From 54e28cea23a5c9c8999c66cf106ad59858d97174 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:15:02 +0200 Subject: [PATCH 41/47] Use subfolder in temp folder for testing download of binaries --- FFMpegCore.Test/DownloaderTests.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index 7c045d6..bc2208c 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -7,11 +7,20 @@ namespace FFMpegCore.Test; [TestClass] public class DownloaderTests { + private FFOptions _ffOptions; + + [TestInitialize] + public void InitializeTestFolder() + { + var tempDownloadFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDownloadFolder); + _ffOptions = new FFOptions { BinaryFolder = Path.GetTempPath() }; + } + [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetSpecificVersionTest() { - var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; - var binaries = await FFMpegDownloader.DownloadBinaries(FFMpegVersions.V6_1, options: options); + var binaries = await FFMpegDownloader.DownloadBinaries(FFMpegVersions.V6_1, options: _ffOptions); try { Assert.HasCount(2, binaries); @@ -25,8 +34,7 @@ public class DownloaderTests [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetAllLatestSuiteTest() { - var options = new FFOptions { BinaryFolder = Path.GetTempPath() }; - var binaries = await FFMpegDownloader.DownloadBinaries(options: options); + var binaries = await FFMpegDownloader.DownloadBinaries(options: _ffOptions); try { Assert.HasCount(2, binaries); From 8720e19b9118ec8df9fa36ddece7a1469561df2e Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:17:27 +0200 Subject: [PATCH 42/47] Fix usage of temp subfolder --- FFMpegCore.Test/DownloaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index bc2208c..a5af850 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -14,7 +14,7 @@ public class DownloaderTests { var tempDownloadFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempDownloadFolder); - _ffOptions = new FFOptions { BinaryFolder = Path.GetTempPath() }; + _ffOptions = new FFOptions { BinaryFolder = tempDownloadFolder }; } [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] From 77942765369e0ad414e40364a1b69ad67e284ecc Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:19:12 +0200 Subject: [PATCH 43/47] Update FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs b/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs index d92116a..c3be69f 100644 --- a/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs +++ b/FFMpegCore.Extensions.Downloader/Enums/FFMpegBinaries.cs @@ -3,7 +3,7 @@ [Flags] public enum FFMpegBinaries : ushort { - FFMpeg, - FFProbe, - FFPlay + FFMpeg = 1, + FFProbe = 2, + FFPlay = 4 } From 0a0e6c498561fd9b5608f4e4c60169a7c14bb898 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:20:44 +0200 Subject: [PATCH 44/47] Update FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs index d03eb27..df7ebd5 100644 --- a/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs +++ b/FFMpegCore.Test/Utilities/OsSpecificTestMethod.cs @@ -7,9 +7,9 @@ namespace FFMpegCore.Test.Utilities; [Flags] internal enum OsPlatforms : ushort { - Windows, - Linux, - MacOS + Windows = 1, + Linux = 2, + MacOS = 4 } internal class OsSpecificTestMethod : TestMethodAttribute From 81bf155c382ae364dc840d3d83bfe2e1e61964e7 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:20:51 +0200 Subject: [PATCH 45/47] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 693d0e4..dcee337 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ If you want to use `System.Drawing.Bitmap`s as `IVideoFrame`s, a `BitmapVideoFra ## Runtime Auto Installation You can install a version of ffmpeg suite at runtime using `FFMpegDownloader.DownloadFFMpegSuite();` -This feature uses the api from [ffbinaries](https://ffbinaries.com/api). +This feature uses the api from [ffbinaries](https://ffbinaries.com/api). ## Manual Installation From abf2ab5ee76683d41cb1cec5c4129d9d7e925fa6 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:22:17 +0200 Subject: [PATCH 46/47] Ensure sub tempfolder is deleted after use --- FFMpegCore.Test/DownloaderTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/FFMpegCore.Test/DownloaderTests.cs b/FFMpegCore.Test/DownloaderTests.cs index a5af850..d574230 100644 --- a/FFMpegCore.Test/DownloaderTests.cs +++ b/FFMpegCore.Test/DownloaderTests.cs @@ -17,6 +17,12 @@ public class DownloaderTests _ffOptions = new FFOptions { BinaryFolder = tempDownloadFolder }; } + [TestCleanup] + public void DeleteTestFolder() + { + Directory.Delete(_ffOptions.BinaryFolder, true); + } + [OsSpecificTestMethod(OsPlatforms.Windows | OsPlatforms.Linux)] public async Task GetSpecificVersionTest() { From 8b8701ef44ec6c23c08a78c204b7484319e8b9e7 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 17 Oct 2025 15:22:35 +0200 Subject: [PATCH 47/47] Make Detail prop on FFMpegDownloaderException a readonly field --- .../Exceptions/FFMpegDownloaderException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs b/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs index c7d2ead..8355c4c 100644 --- a/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs +++ b/FFMpegCore.Extensions.Downloader/Exceptions/FFMpegDownloaderException.cs @@ -5,7 +5,7 @@ /// public class FFMpegDownloaderException : Exception { - public string Detail { get; set; } = ""; + public readonly string Detail = ""; public FFMpegDownloaderException(string message) : base(message) {