Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,49 @@ internal static class FfprobePlatformDefaults
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
{
return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz";
}

return "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz";
return GetDownloadUrl(OSPlatform.Linux, RuntimeInformation.OSArchitecture);
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "https://evermeet.cx/ffmpeg/ffmpeg-6.0.zip";
return GetDownloadUrl(OSPlatform.OSX, RuntimeInformation.OSArchitecture);
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return GetDownloadUrl(OSPlatform.Windows, RuntimeInformation.OSArchitecture);
}

return null;
}

/// <summary>
/// Resolves the archive URL for an explicit platform, so the mapping for every platform can
/// be asserted from a test host running on any one of them.
/// </summary>
/// <remarks>
/// Two constraints apply to anything returned here. The archive has to contain ffprobe:
/// the Linux and Windows builds ship a bundle carrying both binaries, but evermeet
/// publishes one binary per archive, so macOS needs the ffprobe archive and not the ffmpeg
/// one. The URL also has to end in a suffix <see cref="FfprobeArchiveExtractor"/>
/// recognises, which rules out endpoints that redirect to the current release without
/// naming a file.
/// </remarks>
internal static string? GetDownloadUrl(OSPlatform platform, Architecture architecture)
{
if (platform == OSPlatform.Linux)
{
return architecture == Architecture.Arm64
? "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz"
: "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz";
}

if (platform == OSPlatform.OSX)
{
return "https://evermeet.cx/ffmpeg/ffprobe-6.0.zip";
}

if (platform == OSPlatform.Windows)
{
return "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Listenarr - Audiobook Management System
* Copyright (C) 2024-2026 Listenarr Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Runtime.InteropServices;

// Aliased because `Listenarr.Tests.Features.Architecture` is an enclosing namespace here, and a
// namespace member wins over the type of the same name.
using Arch = System.Runtime.InteropServices.Architecture;

namespace Listenarr.Tests.Features.Infrastructure.Ffmpeg.Installation
{
[Trait("Name", "FfprobePlatformDefaultsTests")]
[Trait("Category", "FfmpegService")]
public class FfprobePlatformDefaultsTests
{
// Suffixes FfprobeArchiveExtractor dispatches on. A URL outside this set downloads fine and
// then extracts nothing, which is the failure mode that hid the macOS bug: the install log
// shows a successful download and no binary ever appears.
private static readonly string[] ExtractableSuffixes = [".zip", ".tar.xz", ".tar.gz", ".tgz"];

public static TheoryData<OSPlatform, Arch> AllPlatforms() => new()
{
{ OSPlatform.Linux, Arch.X64 },
{ OSPlatform.Linux, Arch.Arm64 },
{ OSPlatform.OSX, Arch.X64 },
{ OSPlatform.Windows, Arch.X64 },
};

[Theory]
[MemberData(nameof(AllPlatforms))]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_ReturnsAnArchiveTheExtractorCanOpen(
OSPlatform platform,
Arch architecture)
{
var url = FfprobePlatformDefaults.GetDownloadUrl(platform, architecture);

Assert.NotNull(url);
Assert.Contains(
ExtractableSuffixes,
suffix => url!.EndsWith(suffix, StringComparison.OrdinalIgnoreCase));
}

[Fact]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_ForMacOs_PointsAtTheFfprobeArchive()
{
// evermeet ships a separate archive per binary, so the ffmpeg archive contains no
// ffprobe at all and the install can never succeed on macOS.
var url = FfprobePlatformDefaults.GetDownloadUrl(OSPlatform.OSX, Arch.X64);

Assert.NotNull(url);
var fileName = url!.Split('/')[^1];
Assert.Contains("ffprobe", fileName, StringComparison.OrdinalIgnoreCase);
}

[Theory]
[MemberData(nameof(AllPlatforms))]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_IsServedOverHttps(OSPlatform platform, Arch architecture)
{
var url = FfprobePlatformDefaults.GetDownloadUrl(platform, architecture);

Assert.NotNull(url);
Assert.StartsWith("https://", url, StringComparison.OrdinalIgnoreCase);
}

[Fact]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_ForLinux_DistinguishesArm64FromX64()
{
var x64 = FfprobePlatformDefaults.GetDownloadUrl(OSPlatform.Linux, Arch.X64);
var arm64 = FfprobePlatformDefaults.GetDownloadUrl(OSPlatform.Linux, Arch.Arm64);

Assert.NotEqual(x64, arm64);
Assert.Contains("arm64", arm64!, StringComparison.OrdinalIgnoreCase);
}

[Fact]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_ForAnUnsupportedPlatform_ReturnsNull()
{
Assert.Null(FfprobePlatformDefaults.GetDownloadUrl(OSPlatform.FreeBSD, Arch.X64));
}

[Fact]
[Trait("Method", "GetDownloadUrl")]
public void GetDownloadUrl_WithoutArguments_MatchesTheHostPlatform()
{
var expected = FfprobePlatformDefaults.GetDownloadUrl(
OperatingSystem.IsWindows() ? OSPlatform.Windows
: OperatingSystem.IsMacOS() ? OSPlatform.OSX
: OSPlatform.Linux,
RuntimeInformation.OSArchitecture);

Assert.Equal(expected, FfprobePlatformDefaults.GetDownloadUrl());
}
}
}