Code/Resolver/Extractors/LabExtractor.cs
using System.Threading;

namespace Bimp.Resolver.Extractors;

/// <summary>
/// [probe] Local test media for the merged (YouTube) path: http://127.0.0.1:8443/lab/NAME plays NAME_v.webm and
/// NAME_a.webm from a range server on loopback, merged like a YouTube video.
/// </summary>
public sealed class LabExtractor : IExtractor
{
	public string Key => "lab";

	public bool CanHandle( Uri url ) => url.IsLoopback && url.AbsolutePath.StartsWith( "/lab/" );

	public async Task<MediaInfo> GetInfoAsync( Uri url, bool audioOnly, CancellationToken ct )
	{
		var id = $"{url.Port}/{url.AbsolutePath[5..]}";
		var duration = float.TryParse( url.Query.TrimStart( '?' ), out var d ) ? d : 0;
		await Task.CompletedTask;
		return new MediaInfo { Extractor = Key, Id = id, Title = $"lab {id}", Duration = duration, AudioOnly = audioOnly, Merged = true };
	}

	public async Task<StreamPlan> GetStreamsAsync( string id, StreamRequest request, CancellationToken ct )
	{
		var port = id[..id.IndexOf( '/' )];
		var name = id[(id.IndexOf( '/' ) + 1)..];
		async Task<StreamFile> File( string suffix )
		{
			var url = $"http://127.0.0.1:{port}/{name}_{suffix}.webm";
			using var head = await Http.RequestAsync( url, "HEAD", cancellationToken: ct );
			return new StreamFile { Url = url, Size = head.Content.Headers.ContentLength ?? 0, FormatId = suffix };
		}
		return new StreamPlan { Kind = request.AudioOnly ? StreamKind.Audio : StreamKind.Merge, Video = request.AudioOnly ? null : await File( "v" ), Audio = await File( "a" ) };
	}
}