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;
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
FFMpegVersions version = FFMpegVersions.Latest,
public static async Task> DownloadBinaries(
FFMpegBinaries binaries = FFMpegBinaries.FFMpeg | FFMpegBinaries.FFProbe,
FFOptions? options = null,
SupportedPlatforms? platformOverride = null)
{
using var httpClient = new HttpClient();
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();
var relevantOptions = options ?? GlobalFFOptions.Current;
if (string.IsNullOrEmpty(relevantOptions.BinaryFolder))
{
throw new FFMpegDownloaderException("Binary folder not specified");
}
var binaryFlags = binaries.GetFlags();
foreach (var binaryFlag in binaryFlags)
{
if (binariesDictionary.TryGetValue(binaryFlag.ToString().ToLowerInvariant(), out var binaryUrl))
{
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;
}
}