.netpowershellpowershell-7.0

youtube-dl process hangs with RedirectStandardOutput


Trying to write a basic Powershell script that will pull videos from URLs, and cut them with FFmpeg if desired.

One thing I need to do up front is pull the download options from the site to see if I want to use -f best or -f bestvideo+bestaudio. So I need to run youtube-dl -F [url] and return the output, but I can't seem to do so without hanging:

param(
  [string]$url = "https://www.youtube.com/watch?v=njX2bu-_Vw4"
)

$YTDLinfo = New-Object System.Diagnostics.ProcessStartInfo
$YTDLinfo.FileName = "youtube-dl"
$YTDLinfo.RedirectStandardError = $false
$YTDLinfo.RedirectStandardOutput = $true
$YTDLinfo.UseShellExecute = $false
$YTDLinfo.Arguments = "-F $url"
$YTDL = New-Object System.Diagnostics.Process
$YTDL.StartInfo = $YTDLinfo
$YTDL.Start() | Out-Null
$YTDL.WaitForExit()

$YTDLStdOut = $YTDL.StandardOutput.ReadToEnd().split([Environment]::NewLine)

Write-Host $YTDLStdOut

If I change $YTDLinfo.RedirectStandardOutput to $false it works and returns the output directly into the console, but I need the output in a variable. Also worth mentioning that if I run the above but without passing the process any arguments ($YTDLinfo.Arguments = "") it also works, even with $YTDLinfo.RedirectStandardOutput set to $true, and just returns some youtube-dl jargon about how it needs a url.

A bit confusing to explain, but it only hangs when both redirecting the standard output and providing it with -F [url]. Any ideas?


Solution

  • Your immediate problem is that you're waiting for the process to exit without ensuring that you've fully consumed its redirected stdout output first.

    That is, your .WaitForExit() call may wait indefinitely, namely if the process creates more than one output buffer's worth of stdout data, which blocks the process until more data is read by the caller.

    Therefore, call $YTDL.StandardOuput.ReadToEnd() before $YTDL.WaitForExit().


    However, it's worth taking a step back:

    In order to execute console applications (such as youtube-dl) from PowerShell, synchronously, with their stdout and stderr streams connected to the equivalent PowerShell output streams, invoke them directly, as you would in any shell:

    # Directly captures youtube-dl's stdout output as an *array of lines*.
    $YTDLStdOut = youtube-dl -F $url
    

    Use redirection 2> in order to capture stderr output in a file.

    Alternatively, use 2>&1 to capture stdout and stderr merged in a variable, and differentiate which output line came from what stream later: -is [string] implies stdout - see this answer.

    See this answer for a comprehensive overview of capturing stdout and stderr via direct invocation of console programs.

    Specifically, the following techniques should generally be avoided for invoking console applications: