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; - } -}