113 lines
No EOL
4.7 KiB
C#
113 lines
No EOL
4.7 KiB
C#
using Toastie.Utilities;
|
|
using EllieHub.Features.AppConfig.Services.Abstractions;
|
|
using System.IO.Compression;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace EllieHub.Features.AppConfig.Services;
|
|
|
|
/// <summary>
|
|
/// Service that checks, downloads, installs, and updates ffmpeg on Windows.
|
|
/// </summary>
|
|
/// <remarks>Source: https://github.com/GyanD/codexffmpeg/releases/latest</remarks>
|
|
[SupportedOSPlatform("windows")]
|
|
public sealed class FfmpegWindowsResolver : FfmpegResolver
|
|
{
|
|
private readonly string _tempDirectory = Path.GetTempPath();
|
|
private bool _isUpdating;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
|
|
/// <inheritdoc />
|
|
public override string FileName { get; } = "ffmpeg.exe";
|
|
|
|
/// <summary>
|
|
/// Creates a service that checks, downloads, installs, and updates ffmpeg on Windows.
|
|
/// </summary>
|
|
/// <param name="httpClientFactory">The HTTP client factory.</param>
|
|
public FfmpegWindowsResolver(IHttpClientFactory httpClientFactory)
|
|
=> _httpClientFactory = httpClientFactory;
|
|
|
|
/// <inheritdoc/>
|
|
public override ValueTask<bool?> CanUpdateAsync(CancellationToken cToken = default)
|
|
{
|
|
// I could not find any ARM build of ffmpeg for Windows.
|
|
return RuntimeInformation.OSArchitecture is Architecture.X64
|
|
? base.CanUpdateAsync(cToken)
|
|
: ValueTask.FromResult<bool?>(false);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override async ValueTask<string> GetLatestVersionAsync(CancellationToken cToken = default)
|
|
{
|
|
var http = _httpClientFactory.CreateClient(AppConstants.ToastielabClient);
|
|
|
|
var response = await http.GetAsync("https://github.com/GyanD/codexffmpeg/releases/latest", cToken);
|
|
|
|
var lastSlashIndex = response.Headers.Location?.OriginalString.LastIndexOf('/')
|
|
?? throw new InvalidOperationException("Failed to get the latest yt-dlp version.");
|
|
|
|
return response.Headers.Location.OriginalString[(lastSlashIndex + 1)..];
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override async ValueTask<(string? OldVersion, string? NewVersion)> InstallOrUpdateAsync(string installationUri, CancellationToken cToken = default)
|
|
{
|
|
if (_isUpdating)
|
|
return (null, null);
|
|
|
|
_isUpdating = true;
|
|
var currentVersion = await GetCurrentVersionAsync(cToken);
|
|
var newVersion = await GetLatestVersionAsync(cToken);
|
|
|
|
// Update
|
|
if (currentVersion is not null)
|
|
{
|
|
// If the versions are the same, exit.
|
|
if (currentVersion == newVersion)
|
|
{
|
|
_isUpdating = false;
|
|
return (currentVersion, null);
|
|
}
|
|
|
|
ToastieUtilities.TryDeleteFile(Path.Join(installationUri, FileName));
|
|
ToastieUtilities.TryDeleteFile(Path.Join(installationUri, "ffprobe.exe"));
|
|
//ToastieUtilities.TryDeleteFile(Path.Join(dependenciesUri, "ffplay.exe"));
|
|
}
|
|
|
|
// Install
|
|
Directory.CreateDirectory(installationUri);
|
|
|
|
var zipFileName = $"ffmpeg-{newVersion}-full_build.zip";
|
|
var http = _httpClientFactory.CreateClient();
|
|
await using var downloadStream = await http.GetStreamAsync($"https://github.com/GyanD/codexffmpeg/releases/download/{newVersion}/{zipFileName}", cToken);
|
|
|
|
// Save zip file to the temporary directory.
|
|
var zipFilePath = Path.Join(_tempDirectory, zipFileName);
|
|
var zipExtractDir = Path.Join(_tempDirectory, zipFileName[..^4]);
|
|
await using (var fileStream = new FileStream(zipFilePath, FileMode.Create))
|
|
await downloadStream.CopyToAsync(fileStream, cToken);
|
|
|
|
// Schedule installation to the thread-pool because ffmpeg is pretty
|
|
// large and doing I/O with it can potentially block the UI thread.
|
|
await Task.Run(() =>
|
|
{
|
|
// Extract the zip file.
|
|
ZipFile.ExtractToDirectory(zipFilePath, _tempDirectory);
|
|
|
|
// Move ffmpeg to the dependencies directory.
|
|
ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", FileName), Path.Join(installationUri, FileName), true);
|
|
ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", "ffprobe.exe"), Path.Join(installationUri, "ffprobe.exe"), true);
|
|
//ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", "ffplay.exe"), Path.Join(dependenciesUri, "ffplay.exe"));
|
|
|
|
// Cleanup
|
|
ToastieUtilities.TryDeleteFile(zipFilePath);
|
|
ToastieUtilities.TryDeleteDirectory(zipExtractDir);
|
|
}, cToken);
|
|
|
|
// Update environment variable
|
|
ToastieUtilities.AddPathToPATHEnvar(installationUri);
|
|
|
|
_isUpdating = false;
|
|
return (currentVersion, newVersion);
|
|
}
|
|
} |