Files
skills/youtube-download/scripts/yt-download.ps1
T

110 lines
4.0 KiB
PowerShell

<#
.SYNOPSIS
Download YouTube video(s) at highest quality as merged MP4.
.DESCRIPTION
Wraps yt-dlp with the settings needed for modern YouTube:
- JS runtime (bun preferred; deno auto-downloaded to .\bin if missing)
- yt-dlp (auto-downloaded to .\bin if missing)
- ffmpeg (must be on PATH, or set -FfmpegDir)
- cookies.txt next to this script is used automatically if present
.EXAMPLE
.\yt-download.ps1 "https://www.youtube.com/watch?v=xxxx"
.EXAMPLE
.\yt-download.ps1 URL1 URL2 URL3 -OutDir "D:\Videos"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromRemainingArguments = $true)]
[string[]] $Urls,
[string] $OutDir = (Join-Path $env:USERPROFILE 'Downloads'),
# Override format selection if you want (e.g. H.264 only, or <=1080p)
[string] $Format = 'bestvideo+bestaudio/best',
# Path to ffmpeg's bin dir. Auto-detected from PATH if omitted.
[string] $FfmpegDir = '',
# Path to a Netscape cookies.txt. Defaults to cookies.txt beside this script.
[string] $Cookies = ''
)
$ErrorActionPreference = 'Stop'
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$BinDir = Join-Path $ScriptDir 'bin'
New-Item -ItemType Directory -Force $BinDir | Out-Null
function Get-Tool($name, $url, $outFile) {
$local = Join-Path $BinDir $outFile
if (Test-Path $local) { return $local }
$onPath = (Get-Command $name -ErrorAction SilentlyContinue)
if ($onPath) { return $onPath.Source }
Write-Host "[setup] downloading $name ..." -ForegroundColor Cyan
Invoke-WebRequest -Uri $url -OutFile $local
return $local
}
# --- yt-dlp ---
$ytdlp = Get-Tool 'yt-dlp' 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe' 'yt-dlp.exe'
# --- JS runtime (bun preferred; deno fallback; required by YouTube) ---
$jsRuntime = ''
$bun = Get-Command 'bun' -ErrorAction SilentlyContinue
if ($bun) {
$jsRuntime = "bun:$($bun.Source)"
} else {
$deno = Join-Path $BinDir 'deno.exe'
if (-not (Test-Path $deno)) {
$onPath = Get-Command 'deno' -ErrorAction SilentlyContinue
if ($onPath) {
$deno = $onPath.Source
} else {
Write-Host "[setup] downloading deno ..." -ForegroundColor Cyan
$zip = Join-Path $BinDir 'deno.zip'
Invoke-WebRequest -Uri 'https://github.com/denoland/deno/releases/latest/download/deno-x86_64-pc-windows-msvc.zip' -OutFile $zip
Expand-Archive $zip -DestinationPath $BinDir -Force
Remove-Item $zip -Force
}
}
$jsRuntime = "deno:$deno"
}
# --- ffmpeg ---
if (-not $FfmpegDir) {
$ff = Get-Command 'ffmpeg' -ErrorAction SilentlyContinue
if ($ff) {
$FfmpegDir = Split-Path -Parent $ff.Source
} else {
Write-Warning "ffmpeg not found on PATH. Install it (winget install Gyan.FFmpeg) or pass -FfmpegDir. Merging video+audio will fail without it."
}
}
# --- cookies ---
if (-not $Cookies) {
$defaultCookies = Join-Path $ScriptDir 'cookies.txt'
if (Test-Path $defaultCookies) { $Cookies = $defaultCookies }
}
New-Item -ItemType Directory -Force $OutDir | Out-Null
$common = @(
'--no-js-runtimes',
'--js-runtimes', $jsRuntime,
'-f', $Format,
'--merge-output-format', 'mp4',
'-o', (Join-Path $OutDir '%(title)s [%(height)sp].%(ext)s'),
'--no-playlist'
)
if ($FfmpegDir) { $common += @('--ffmpeg-location', $FfmpegDir) }
if ($Cookies) { $common += @('--cookies', $Cookies); Write-Host "[info] using cookies: $Cookies" -ForegroundColor DarkGray }
else { Write-Host "[info] no cookies.txt -- public videos only. See SKILL.md if you hit 'Sign in to confirm you're not a bot'." -ForegroundColor DarkGray }
$fail = 0
foreach ($u in $Urls) {
Write-Host "`n==== downloading: $u ====" -ForegroundColor Green
& $ytdlp @common $u
if ($LASTEXITCODE -ne 0) { $fail++; Write-Warning "failed: $u" }
}
if ($fail) { Write-Warning "$fail download(s) failed."; exit 1 }
Write-Host "`nAll done. Saved to: $OutDir" -ForegroundColor Green