111 lines
No EOL
4.7 KiB
C#
111 lines
No EOL
4.7 KiB
C#
using Toastie.Utilities;
|
|
using EllieHub.Features.AppConfig.Services.Abstractions;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Versioning;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace EllieHub.Features.AppConfig.Services;
|
|
|
|
/// <summary>
|
|
/// Service that checks, downloads, installs, and updates ffmpeg on Linux.
|
|
/// </summary>
|
|
/// <remarks>Source: https://johnvansickle.com/ffmpeg</remarks>
|
|
[SupportedOSPlatform("linux")]
|
|
public sealed partial class FfmpegLinuxResolver : FfmpegResolver
|
|
{
|
|
private readonly Regex _ffmpegLatestVersionRegex = FfmpegLatestVersionRegexGenerator();
|
|
private readonly string _tempDirectory = Path.GetTempPath();
|
|
private bool _isUpdating;
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
|
|
/// <inheritdoc/>
|
|
public override string FileName { get; } = "ffmpeg";
|
|
|
|
/// <summary>
|
|
/// Creates a service that checks, downloads, installs, and updates ffmpeg on Linux.
|
|
/// </summary>
|
|
/// <param name="httpClientFactory">The HTTP client factory.</param>
|
|
public FfmpegLinuxResolver(IHttpClientFactory httpClientFactory)
|
|
=> _httpClientFactory = httpClientFactory;
|
|
|
|
/// <inheritdoc/>
|
|
public override ValueTask<bool?> CanUpdateAsync(CancellationToken cToken = default)
|
|
{
|
|
return (RuntimeInformation.OSArchitecture is Architecture.X64 or Architecture.Arm64)
|
|
? base.CanUpdateAsync(cToken)
|
|
: ValueTask.FromResult<bool?>(false);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override async ValueTask<string> GetLatestVersionAsync(CancellationToken cToken = default)
|
|
{
|
|
var http = _httpClientFactory.CreateClient();
|
|
var pageContent = await http.GetStringAsync("https://johnvansickle.com/ffmpeg", cToken);
|
|
var match = _ffmpegLatestVersionRegex.Match(pageContent);
|
|
|
|
return (match.Success)
|
|
? match.Groups[1].Value
|
|
: throw new InvalidOperationException("Regex did not match the web page content.");
|
|
}
|
|
|
|
/// <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)
|
|
return (currentVersion, null);
|
|
|
|
ToastieUtilities.TryDeleteFile(Path.Join(installationUri, FileName));
|
|
ToastieUtilities.TryDeleteFile(Path.Join(installationUri, "ffprobe"));
|
|
}
|
|
|
|
// Install
|
|
Directory.CreateDirectory(installationUri);
|
|
|
|
var architecture = (RuntimeInformation.OSArchitecture is Architecture.X64) ? "amd" : "arm";
|
|
var tarFileName = $"ffmpeg-release-{architecture}64-static.tar.xz";
|
|
var http = _httpClientFactory.CreateClient();
|
|
await using var downloadStream = await http.GetStreamAsync($"https://johnvansickle.com/ffmpeg/releases/{tarFileName}", cToken);
|
|
|
|
// Save tar file to the temporary directory.
|
|
var tarFilePath = Path.Join(_tempDirectory, tarFileName);
|
|
var tarExtractDir = Path.Join(_tempDirectory, $"ffmpeg-{newVersion}-{architecture}64-static");
|
|
await using (var fileStream = new FileStream(tarFilePath, FileMode.Create))
|
|
await downloadStream.CopyToAsync(fileStream, cToken);
|
|
|
|
// Extract the tar file.
|
|
using var extractProcess = ToastieUtilities.StartProcess("tar", ["xf", tarFilePath, $"--directory=\"{_tempDirectory}\""]);
|
|
await extractProcess.WaitForExitAsync(cToken);
|
|
|
|
// Move ffmpeg to the dependencies directory.
|
|
ToastieUtilities.TryMoveFile(Path.Join(tarExtractDir, FileName), Path.Join(installationUri, FileName), true);
|
|
ToastieUtilities.TryMoveFile(Path.Join(tarExtractDir, "ffprobe"), Path.Join(installationUri, "ffprobe"), true);
|
|
|
|
// Mark the files as executable.
|
|
using var chmod = ToastieUtilities.StartProcess("chmod", ["+x", Path.Join(installationUri, FileName), Path.Join(installationUri, "ffprobe")]);
|
|
await chmod.WaitForExitAsync(cToken);
|
|
|
|
// Cleanup
|
|
ToastieUtilities.TryDeleteFile(tarFilePath);
|
|
ToastieUtilities.TryDeleteDirectory(tarExtractDir, true);
|
|
|
|
// Update environment variable
|
|
ToastieUtilities.AddPathToPATHEnvar(installationUri);
|
|
|
|
_isUpdating = false;
|
|
return (currentVersion, newVersion);
|
|
}
|
|
|
|
[GeneratedRegex(@"release:\s?([\d\.]+)", RegexOptions.Compiled)]
|
|
private static partial Regex FfmpegLatestVersionRegexGenerator();
|
|
} |