<# .SYNOPSIS Headless AE spike / debug tool for the preview build stage. .DESCRIPTION Runs vfxr_build_preview.jsx exactly the way the RenderWorker does, but standalone, so the risky part (AfterFX.com -noui -r under a service account) can be proven before wiring the whole pipeline together. It does NOT render - it builds the preview comp, queues the MOV + MP4 output modules and saves a temp AEP, then prints the build result. IMPORTANT: close After Effects first. `AfterFX.com -r` hands the script to an already-running instance, which would open the template in your session and then quit it. .EXAMPLE .\test-preview-build.ps1 -ExrDir "V:\_EXPORTS\UNG_S1\111\UNG_111_001_030\v002" ` -ShotCode "UNG_111_001_030" -Version "v002" .EXAMPLE # then render what it built, exactly as the worker would: & "C:\Program Files\Adobe\Adobe After Effects 2026\Support Files\aerender.exe" -project "$env:ProgramData\VFXReviewWorker\scratch\spike_preview.aep" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$ExrDir, [Parameter(Mandatory = $true)][string]$ShotCode, [string]$Version = "v001", [string]$TemplateAep = "X:/shared_projects_2026/UNGO_VFX/production/working_files/UNG_VFXOVERLAY_SLATE_TEMPLATE.aep", [string]$TemplateComp = "UNG_EXPORT_TEMPLATE", [string]$OverlayComp = "UNG_VFX_OVERLAY", [string]$LutComp = "_SHOW LUT", [string]$MovTemplate = "4444 Tri", [string]$Mp4Template = "REVIEW_PREVIEW", [string]$AfterFx = "C:\Program Files\Adobe\Adobe After Effects 2026\Support Files\AfterFX.com", [int]$FrameStart = 1001, [double]$Fps = 24, [int]$TimeoutSeconds = 300 ) $ErrorActionPreference = "Stop" $running = Get-Process AfterFX -ErrorAction SilentlyContinue if ($running) { Write-Warning "After Effects is running (PID $($running.Id -join ', '))." Write-Warning "AfterFX.com -r would target that instance and quit it. Close AE first." return } $scratch = Join-Path $env:ProgramData "VFXReviewWorker\scratch" if (-not (Test-Path $scratch)) { New-Item -ItemType Directory -Force -Path $scratch | Out-Null } $scriptPath = Join-Path $PSScriptRoot "vfxr_build_preview.jsx" if (-not (Test-Path $scriptPath)) { throw "Build script not found: $scriptPath" } if (-not (Test-Path $AfterFx)) { throw "AfterFX.com not found: $AfterFx" } if (-not (Test-Path $ExrDir)) { throw "EXR folder not found: $ExrDir" } $contextPath = Join-Path $scratch "spike_context.json" $resultPath = Join-Path $scratch "spike_result.json" $wrapperPath = Join-Path $scratch "spike_run.jsx" $tempAep = Join-Path $scratch "spike_preview.aep" $base = "${ShotCode}_cmp_TT_${Version}" Remove-Item $resultPath -ErrorAction SilentlyContinue $context = [ordered]@{ jobId = "spike" exportId = "spike" shotCode = $ShotCode versionString = $Version outputDirLocal = $ExrDir templateAep = $TemplateAep templateComp = $TemplateComp overlayComp = $OverlayComp lutComp = $LutComp movTemplate = $MovTemplate mp4Template = $Mp4Template movOutput = (Join-Path $ExrDir "$base.mov") mp4Output = (Join-Path $ExrDir "$base.mp4") tempAep = $tempAep resultPath = $resultPath frameStart = $FrameStart fps = $Fps slate = [ordered]@{ versionName = $base date = (Get-Date -Format "yyyy/MM/dd") description = "Spike test" notes = "" shotCode = $ShotCode episode = "" scene = "" } } $context | ConvertTo-Json -Depth 6 | Out-File -FilePath $contextPath -Encoding utf8 $ctxJs = ($contextPath -replace '\\', '/') | ConvertTo-Json $scriptJs = ($scriptPath -replace '\\', '/') | ConvertTo-Json $wrapper = @( "// generated by test-preview-build.ps1", ("var VFXR_CONTEXT_PATH = {0};" -f $ctxJs), ('$.evalFile({0});' -f $scriptJs) ) -join "`r`n" $wrapper | Out-File -FilePath $wrapperPath -Encoding utf8 Write-Host "Launching headless AE build..." -ForegroundColor Cyan $proc = Start-Process -FilePath $AfterFx -ArgumentList @("-noui", "-r", $wrapperPath) -PassThru -NoNewWindow if (-not $proc.WaitForExit($TimeoutSeconds * 1000)) { Write-Warning "Timed out after $TimeoutSeconds s - killing AE." try { $proc.Kill($true) } catch {} } if (-not (Test-Path $resultPath)) { Write-Host "FAILED: no result file produced." -ForegroundColor Red Write-Host "AE could not run the script headlessly under this account. See RenderWorker/README.md fallbacks." -ForegroundColor Red return } $result = Get-Content $resultPath -Raw | ConvertFrom-Json if ($result.ok) { Write-Host "BUILD OK" -ForegroundColor Green Write-Host " temp AEP : $($result.tempAep)" Write-Host " preview comp: $($result.previewComp)" Write-Host (" frames : {0} ({1} x {2})" -f $result.frameCount, $result.width, $result.height) Write-Host (" queued : {0} output(s)" -f @($result.queued).Count) foreach ($q in @($result.queued)) { Write-Host (" - {0}: {1} -> {2}" -f $q.label, $q.template, $q.output) } Write-Host "" Write-Host "Render it with:" -ForegroundColor Cyan Write-Host (' "{0}\aerender.exe" -project "{1}"' -f (Split-Path $AfterFx), $result.tempAep) } else { Write-Host "BUILD FAILED: $($result.error)" -ForegroundColor Red } if ($result.warnings -and $result.warnings.Count -gt 0) { Write-Host "Warnings:" -ForegroundColor Yellow $result.warnings | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } }