122 lines
6.9 KiB
PowerShell
122 lines
6.9 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$PythonPath,
|
|
[string]$FfmpegPath,
|
|
[string]$NodePath,
|
|
[switch]$RefreshDependencies,
|
|
[switch]$SkipRegistration,
|
|
[switch]$HelperOnly
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
$taskProjectRoot = $PSScriptRoot
|
|
$taskRuntime = Join-Path $taskProjectRoot '.runtime'
|
|
New-Item -ItemType Directory -Path $taskRuntime -Force | Out-Null
|
|
$taskSavedConfig = $null
|
|
if (Test-Path -LiteralPath (Join-Path $taskRuntime 'config.json')) {
|
|
try { $taskSavedConfig = Get-Content -LiteralPath (Join-Path $taskRuntime 'config.json') -Raw | ConvertFrom-Json } catch {}
|
|
}
|
|
|
|
function Install-PrivatePython {
|
|
$taskPythonRoot = Join-Path $taskRuntime 'python'
|
|
$taskPythonInstaller = Join-Path $taskRuntime 'python-setup.exe'
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
Write-Host 'Installing Python from python.org...'
|
|
Invoke-WebRequest -UseBasicParsing -Uri 'https://www.python.org/ftp/python/3.14.8/python-3.14.8-amd64.exe' -OutFile $taskPythonInstaller
|
|
$taskSignature = Get-AuthenticodeSignature -LiteralPath $taskPythonInstaller
|
|
if ($taskSignature.Status -ne 'Valid') { throw 'Python installer signature verification failed.' }
|
|
$taskArguments = @('/quiet','InstallAllUsers=0',"TargetDir=`"$taskPythonRoot`"",'PrependPath=0','Include_launcher=0','Include_test=0','Include_doc=0','Shortcuts=0','AssociateFiles=0')
|
|
$taskInstalled = Start-Process -FilePath $taskPythonInstaller -ArgumentList $taskArguments -WindowStyle Hidden -Wait -PassThru
|
|
if ($taskInstalled.ExitCode -notin @(0,3010)) { throw 'Python installation failed.' }
|
|
return (Join-Path $taskPythonRoot 'python.exe')
|
|
}
|
|
|
|
function Find-Tool([string]$ExplicitPath, [string]$Name) {
|
|
if ($ExplicitPath) {
|
|
if (-not (Test-Path -LiteralPath $ExplicitPath -PathType Leaf)) { throw "File not found: $ExplicitPath" }
|
|
return (Resolve-Path -LiteralPath $ExplicitPath).Path
|
|
}
|
|
$taskCommand = Get-Command $Name -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if ($taskCommand) { return $taskCommand.Source }
|
|
return $null
|
|
}
|
|
|
|
if (-not $PythonPath) {
|
|
$taskExistingPython = Join-Path $env:USERPROFILE '.agent-reach-venv\Scripts\python.exe'
|
|
$taskProjectPython = Join-Path $taskRuntime 'venv\Scripts\python.exe'
|
|
if ($taskSavedConfig.python -and (Test-Path -LiteralPath $taskSavedConfig.python)) { $PythonPath = $taskSavedConfig.python }
|
|
elseif (Test-Path -LiteralPath $taskExistingPython) { $PythonPath = $taskExistingPython }
|
|
elseif (Test-Path -LiteralPath $taskProjectPython) { $PythonPath = $taskProjectPython }
|
|
else {
|
|
$taskSystemPython = Find-Tool '' 'python.exe'
|
|
if (-not $taskSystemPython -or $taskSystemPython -like '*\WindowsApps\*') { $taskSystemPython = Install-PrivatePython }
|
|
& $taskSystemPython -c 'import sys; assert sys.version_info >= (3,10)'
|
|
if ($LASTEXITCODE -ne 0) { $taskSystemPython = Install-PrivatePython }
|
|
& $taskSystemPython -m venv (Join-Path $taskRuntime 'venv')
|
|
if ($LASTEXITCODE -ne 0) { throw 'Python virtual environment creation failed.' }
|
|
$PythonPath = $taskProjectPython
|
|
}
|
|
}
|
|
$PythonPath = Find-Tool $PythonPath 'python.exe'
|
|
& $PythonPath -c 'import sys; assert sys.version_info >= (3,10), "Python 3.10+ required"'
|
|
if ($LASTEXITCODE -ne 0) { throw 'Unsupported Python version.' }
|
|
& $PythonPath -c 'import yt_dlp, yt_dlp_ejs'
|
|
if ($LASTEXITCODE -ne 0 -or $RefreshDependencies) {
|
|
& $PythonPath -m pip install --disable-pip-version-check --upgrade -r (Join-Path $taskProjectRoot 'requirements.txt')
|
|
if ($LASTEXITCODE -ne 0) { throw 'Installing yt-dlp failed.' }
|
|
}
|
|
if (-not $FfmpegPath -and $taskSavedConfig.ffmpeg -and (Test-Path -LiteralPath $taskSavedConfig.ffmpeg)) { $FfmpegPath = $taskSavedConfig.ffmpeg }
|
|
$taskFfmpeg = Find-Tool $FfmpegPath 'ffmpeg.exe'
|
|
$taskFfprobe = if ($taskFfmpeg) { Join-Path (Split-Path -Parent $taskFfmpeg) 'ffprobe.exe' } else { $null }
|
|
if (-not $taskFfprobe -or -not (Test-Path -LiteralPath $taskFfprobe)) { $taskFfprobe = Find-Tool '' 'ffprobe.exe' }
|
|
if (-not $NodePath) {
|
|
if ($taskSavedConfig.node -and (Test-Path -LiteralPath $taskSavedConfig.node)) { $NodePath = $taskSavedConfig.node }
|
|
$taskPortableNode = Join-Path $env:USERPROFILE '.agent-reach\tools\node-v24.21.0-win-x64\node.exe'
|
|
if (-not $NodePath -and (Test-Path -LiteralPath $taskPortableNode)) { $NodePath = $taskPortableNode }
|
|
}
|
|
$taskNode = Find-Tool $NodePath 'node.exe'
|
|
|
|
$taskUtf8 = [System.Text.UTF8Encoding]::new($false)
|
|
$taskConfig = @{ python=$PythonPath; ffmpeg=$taskFfmpeg; ffprobe=$taskFfprobe; node=$taskNode }
|
|
[System.IO.File]::WriteAllText((Join-Path $taskRuntime 'config.json'), ($taskConfig | ConvertTo-Json), $taskUtf8)
|
|
Push-Location -LiteralPath $taskProjectRoot
|
|
try {
|
|
if (-not $taskFfmpeg -or -not $taskFfprobe) {
|
|
& $PythonPath -m host.dependencies ffmpeg
|
|
if ($LASTEXITCODE -ne 0) { throw 'Installing FFmpeg failed.' }
|
|
}
|
|
$taskNodeGood = $false
|
|
if ($taskNode) {
|
|
$taskNodeVersion = & $taskNode --version
|
|
$taskNodeGood = $taskNodeVersion -match '^v(\d+)\.' -and [int]$Matches[1] -ge 22
|
|
}
|
|
if (-not $taskNodeGood) {
|
|
& $PythonPath -m host.dependencies node
|
|
if ($LASTEXITCODE -ne 0) { throw 'Installing Node.js failed.' }
|
|
}
|
|
} finally { Pop-Location }
|
|
$taskLauncherPath = Join-Path $taskRuntime 'launch-host.cmd'
|
|
$taskMainPath = Join-Path $taskProjectRoot 'host\main.py'
|
|
$taskLauncher = "@echo off`r`nchcp 65001 >nul`r`nset PYTHONIOENCODING=utf-8`r`nset PYTHONUTF8=1`r`n`"$PythonPath`" -u `"$taskMainPath`" %*`r`n"
|
|
[System.IO.File]::WriteAllText($taskLauncherPath, $taskLauncher, $taskUtf8)
|
|
$taskExtensionId = 'ndnoeiaecgmjnjneekaeapeecmmjheli'
|
|
$taskHostName = 'com.youtubedl.native'
|
|
$taskNativeManifestPath = Join-Path $taskRuntime 'native-host.json'
|
|
$taskNativeManifest = @{ name=$taskHostName; description='YouTubeDL download and NVIDIA NVENC helper'; path=$taskLauncherPath; type='stdio'; allowed_origins=@("chrome-extension://$taskExtensionId/") }
|
|
[System.IO.File]::WriteAllText($taskNativeManifestPath, ($taskNativeManifest | ConvertTo-Json), $taskUtf8)
|
|
if (-not $SkipRegistration) {
|
|
foreach ($taskBrowser in @('Google\Chrome','Microsoft\Edge','Chromium')) {
|
|
$taskRegistryPath = "HKCU:\Software\$taskBrowser\NativeMessagingHosts\$taskHostName"
|
|
New-Item -Path $taskRegistryPath -Force | Out-Null
|
|
Set-Item -Path $taskRegistryPath -Value $taskNativeManifestPath
|
|
}
|
|
}
|
|
Write-Host $(if ($SkipRegistration) { 'Native helper files prepared.' } else { 'Native helper registered for Edge and Chrome (current user).' })
|
|
Write-Host "Extension ID: $taskExtensionId"
|
|
if ($HelperOnly) {
|
|
Write-Host 'Return to YouTubeDL settings and press Start.'
|
|
} else {
|
|
Write-Host "Extension folder: $(Join-Path $taskProjectRoot 'extension')"
|
|
Write-Host 'Open edge://extensions or chrome://extensions, enable Developer mode, and load the extension folder.'
|
|
Write-Host 'Then refresh the YouTube page. The Download and Settings buttons are next to Subscribe.'
|
|
}
|