/// <inheritdoc /> public async Task DownloadAndProcessMediaStreamsAsync(IReadOnlyList <MediaStreamInfo> mediaStreamInfos, string filePath, string format, IProgress <double> progress = null, CancellationToken cancellationToken = default(CancellationToken)) { mediaStreamInfos.GuardNotNull(nameof(mediaStreamInfos)); filePath.GuardNotNull(nameof(filePath)); format.GuardNotNull(nameof(format)); // Determine if transcoding is required for at least one of the streams var transcode = mediaStreamInfos.Any(s => IsTranscodingRequired(s.Container, format)); // Set up progress-related stuff var progressMixer = progress != null ? new ProgressMixer(progress) : null; var downloadProgressPortion = transcode ? 0.15 : 0.99; var ffmpegProgressPortion = 1 - downloadProgressPortion; var totalContentLength = mediaStreamInfos.Sum(s => s.Size); // Keep track of the downloaded streams var streamFilePaths = new List <string>(); try { // Download all streams foreach (var streamInfo in mediaStreamInfos) { // Generate file path var streamIndex = streamFilePaths.Count + 1; var streamFilePath = $"{filePath}.stream-{streamIndex}.tmp"; // Add file path to list streamFilePaths.Add(streamFilePath); // Set up download progress handler var streamDownloadProgress = progressMixer?.Split(downloadProgressPortion * streamInfo.Size / totalContentLength); // Download stream await _youtubeClient.DownloadMediaStreamAsync(streamInfo, streamFilePath, streamDownloadProgress, cancellationToken); } // Set up process progress handler var ffmpegProgress = progressMixer?.Split(ffmpegProgressPortion); // Process streams (mux/transcode/etc) await _ffmpeg.ProcessAsync(streamFilePaths, filePath, format, transcode, ffmpegProgress, cancellationToken); // Report completion in case there are rounding issues in progress reporting progress?.Report(1); } finally { // Delete all stream files foreach (var streamFilePath in streamFilePaths) { FileEx.TryDelete(streamFilePath); } } }