private static void SetupDownload(YoutubeDL ydl, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return; } ydl.process = new Process { StartInfo = ydl.processStartInfo, EnableRaisingEvents = true }; ydl.stdOutputTokenSource = new CancellationTokenSource(); ydl.stdErrorTokenSource = new CancellationTokenSource(); ydl.process.Exited += (sender, args) => ydl.KillStandardEventHandlers(); // Note that synchronous calls are needed in order to process the output line by line. // Asynchronous output reading results in batches of output lines coming in all at once. // The following two threads convert synchronous output reads into asynchronous events. ThreadPool.QueueUserWorkItem(ydl.StandardOutput, ydl.stdOutputTokenSource.Token); ThreadPool.QueueUserWorkItem(ydl.StandardError, ydl.stdErrorTokenSource.Token); if (ydl.Info.set) { ydl.StandardOutputEvent += (sender, output) => ydl.Info.ParseOutput(sender, output.Trim()); ydl.StandardErrorEvent += (sender, output) => ydl.Info.ParseError(sender, output.Trim()); } ydl.process.Start(); ydl.downloadProcessID = ydl.process.Id; }