/// <summary>
        /// Selects best media streams for the specified video, downloads them, and processes into a single file.
        /// </summary>
        public static async Task DownloadAsync(
            this VideoClient videoClient,
            VideoId videoId,
            ConversionRequest request,
            IProgress <double>?progress         = null,
            CancellationToken cancellationToken = default)
        {
            var streamManifest = await videoClient.Streams.GetManifestAsync(videoId);

            var streamInfos = GetBestMediaStreamInfos(streamManifest, request.Format).ToArray();

            await videoClient.DownloadAsync(
                streamInfos,
                request,
                progress,
                cancellationToken
                );
        }
        /// <summary>
        /// Downloads specified media streams and processes them into a single file.
        /// </summary>
        public static async ValueTask DownloadAsync(
            this VideoClient videoClient,
            IReadOnlyList <IStreamInfo> streamInfos,
            ConversionRequest request,
            IProgress <double>?progress         = null,
            CancellationToken cancellationToken = default)
        {
            Platform.EnsureDesktop();

            // Ensure that the provided stream collection is not empty
            if (!streamInfos.Any())
            {
                throw new InvalidOperationException("No streams provided.");
            }

            // If all streams have the same container as the output format, then transcoding is not required
            var isTranscodingRequired = streamInfos.Any(s => IsTranscodingRequired(s.Container, request.Format));

            // Progress setup
            var progressMixer           = progress?.Pipe(p => new ProgressMixer(p));
            var downloadProgressPortion = isTranscodingRequired ? 0.15 : 0.99;
            var totalStreamSize         = streamInfos.Sum(s => s.Size.Bytes);

            // Temp files for streams
            var streamFilePaths = new List <string>(streamInfos.Count);

            try
            {
                // Download streams
                foreach (var streamInfo in streamInfos)
                {
                    var streamIndex    = streamFilePaths.Count + 1;
                    var streamFilePath = $"{request.OutputFilePath}.stream-{streamIndex}.tmp";

                    streamFilePaths.Add(streamFilePath);

                    var streamDownloadProgress = progressMixer?.Split(
                        downloadProgressPortion * streamInfo.Size.Bytes / totalStreamSize
                        );

                    await videoClient.Streams.DownloadAsync(
                        streamInfo,
                        streamFilePath,
                        streamDownloadProgress,
                        cancellationToken
                        ).ConfigureAwait(false);
                }

                // Mux/convert streams
                var conversionProgress = progressMixer?.Split(1 - downloadProgressPortion);

                await new FFmpeg(request.FFmpegCliFilePath).ExecuteAsync(
                    streamFilePaths,
                    request.OutputFilePath,
                    request.Format.Name,
                    request.Preset.ToString().ToLowerInvariant(),
                    isTranscodingRequired,
                    conversionProgress,
                    cancellationToken
                    ).ConfigureAwait(false);

                progress?.Report(1);
            }
            finally
            {
                // Delete temp files
                foreach (var streamFilePath in streamFilePaths)
                {
                    try
                    {
                        File.Delete(streamFilePath);
                    }
                    catch
                    {
                        // Try our best but don't crash
                    }
                }
            }
        }