Mimir/scripts/restart-obs-muninn-source.ps1
2026-06-19 22:35:58 +01:00

149 lines
4.4 KiB
PowerShell

param(
[string]$ObsExe = "C:\Program Files\obs-studio\bin\64bit\obs64.exe",
[string]$ObsConfigRoot = "$env:APPDATA\obs-studio",
[int[]]$ExpectedUdpPorts = @(5204, 17874),
[int]$StartupWaitSeconds = 30,
[switch]$NoStart
)
$ErrorActionPreference = "Stop"
function Stop-Obs {
$processes = Get-Process obs64,obs-browser-page,obs-ffmpeg-mux -ErrorAction SilentlyContinue
if (-not $processes) {
return
}
& "$env:SystemRoot\System32\taskkill.exe" /IM obs64.exe /IM obs-browser-page.exe /IM obs-ffmpeg-mux.exe /T /F | Write-Host
$deadline = (Get-Date).AddSeconds(15)
do {
Start-Sleep -Milliseconds 500
$remaining = Get-Process obs64,obs-browser-page,obs-ffmpeg-mux -ErrorAction SilentlyContinue
if (-not $remaining) {
return
}
} while ((Get-Date) -lt $deadline)
$remaining | ForEach-Object {
try {
Stop-Process -Id $_.Id -Force -ErrorAction Stop
} catch {
Write-Host "Failed to stop lingering $($_.ProcessName) PID $($_.Id): $($_.Exception.Message)"
}
}
Start-Sleep -Seconds 1
}
function Remove-ObsCrashSentinel {
param([string]$ConfigRoot)
$sentinel = Join-Path $ConfigRoot ".sentinel"
if (-not (Test-Path -LiteralPath $sentinel)) {
return
}
$resolvedSentinel = Resolve-Path -LiteralPath $sentinel
$resolvedConfigRoot = Resolve-Path -LiteralPath $ConfigRoot
$expected = Join-Path $resolvedConfigRoot.Path ".sentinel"
if ($resolvedSentinel.Path -ne $expected) {
throw "Refusing to remove unexpected OBS sentinel path '$($resolvedSentinel.Path)'."
}
Remove-Item -LiteralPath $resolvedSentinel.Path -Recurse -Force
Write-Host "Removed OBS crash sentinel: $($resolvedSentinel.Path)"
}
function Get-UdpListenerLines {
param([int[]]$Ports)
$patterns = $Ports | ForEach-Object { ":$_" }
netstat -ano -p udp | Where-Object {
$line = $_
$patterns | Where-Object { $line -match [regex]::Escape($_) }
}
}
function Get-ObservedUdpPorts {
param([int[]]$Ports)
$listeners = @(Get-UdpListenerLines -Ports $Ports)
$observed = @{}
foreach ($port in $Ports) {
$pattern = ":\s*$port\s+"
if ($listeners | Where-Object { $_ -match $pattern }) {
$observed[$port] = $true
}
}
[pscustomobject]@{
Lines = $listeners
Ports = $observed
}
}
function Get-LatestObsLog {
param([string]$ConfigRoot)
$logRoot = Join-Path $ConfigRoot "logs"
if (-not (Test-Path -LiteralPath $logRoot)) {
return $null
}
Get-ChildItem -LiteralPath $logRoot -Filter "*.txt" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
}
if (-not (Test-Path -LiteralPath $ObsExe)) {
throw "OBS executable was not found at '$ObsExe'."
}
if (-not (Test-Path -LiteralPath $ObsConfigRoot)) {
throw "OBS config root was not found at '$ObsConfigRoot'."
}
Stop-Obs
Remove-ObsCrashSentinel -ConfigRoot $ObsConfigRoot
if ($NoStart) {
Write-Host "OBS stopped and crash sentinel cleared."
return
}
$workingDirectory = Split-Path -Parent $ObsExe
Start-Process -FilePath $ObsExe -WorkingDirectory $workingDirectory
Write-Host "Started OBS: $ObsExe"
$deadline = (Get-Date).AddSeconds($StartupWaitSeconds)
$listeners = @()
do {
Start-Sleep -Seconds 1
$obs = Get-Process obs64 -ErrorAction SilentlyContinue
if (-not $obs) {
throw "OBS did not remain running after startup."
}
$observed = Get-ObservedUdpPorts -Ports $ExpectedUdpPorts
$listeners = @($observed.Lines)
$missingPorts = @($ExpectedUdpPorts | Where-Object { -not $observed.Ports.ContainsKey($_) })
if ($missingPorts.Count -eq 0) {
break
}
} while ((Get-Date) -lt $deadline)
if ($missingPorts.Count -ne 0) {
$latestLog = Get-LatestObsLog -ConfigRoot $ObsConfigRoot
if ($latestLog) {
Write-Host "Newest OBS log: $($latestLog.FullName) ($($latestLog.Length) bytes)"
if ($latestLog.Length -gt 0) {
Get-Content -LiteralPath $latestLog.FullName -Tail 40 | Write-Host
}
}
throw "OBS is running, but expected UDP listener ports are missing: $($missingPorts -join ', ')."
}
Write-Host "OBS process:"
$obs | Select-Object Id, ProcessName, StartTime, Responding | Format-Table -AutoSize
Write-Host "Observed Muninn UDP listeners:"
$listeners | ForEach-Object { Write-Host $_ }