222 lines
9.2 KiB
PowerShell
222 lines
9.2 KiB
PowerShell
param(
|
|
# Ruta absoluta o nombre del interprete Python que lanza uvicorn.
|
|
# doctor.ps1 la resuelve porque tras una instalacion fresca el PATH
|
|
# de esta sesion aun no ve el Python nuevo.
|
|
[string]$PythonExe = 'python'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
Set-Location -LiteralPath $root
|
|
# Force UTF-8 so accented messages render correctly even when launched
|
|
# from the .bat via `powershell -File` (PS5.1 uses the OEM codepage by default).
|
|
try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
|
|
|
|
# All console output is force-flushed so the user sees live progress even when
|
|
# the launcher is invoked from a .bat-launched powershell.exe window.
|
|
function Out-Line($msg, $color = $null) {
|
|
if ($color) { Write-Host $msg -ForegroundColor $color }
|
|
else { Write-Host $msg }
|
|
[Console]::Out.Flush()
|
|
}
|
|
|
|
# Robust browser opener -- tries several Windows methods until one works.
|
|
function Open-Browser($url) {
|
|
$opened = $false
|
|
try { Start-Process -FilePath $url; $opened = $true } catch {}
|
|
if (-not $opened) { try { & explorer.exe $url; $opened = $true } catch {} }
|
|
if (-not $opened) { try { & cmd.exe /c start "" $url; $opened = $true } catch {} }
|
|
if ($opened) { Out-Line " [ok] navegador abierto -> $url" DarkGray }
|
|
else { Out-Line " [warn] no se pudo abrir el navegador automaticamente" Yellow }
|
|
return $opened
|
|
}
|
|
|
|
# Fast TCP-connect probe. Replaces Invoke-WebRequest (which on Windows
|
|
# PowerShell 5.1 ignores -TimeoutSec for the connect phase and can hang
|
|
# 3-15 s per failed attempt to a closed port -- making a typical port
|
|
# scan silently take many minutes).
|
|
function Test-PortOpen($port, $timeoutMs = 350) {
|
|
$client = New-Object System.Net.Sockets.TcpClient
|
|
try {
|
|
$iar = $client.BeginConnect('127.0.0.1', $port, $null, $null)
|
|
if (-not $iar.AsyncWaitHandle.WaitOne($timeoutMs)) { return $false }
|
|
$client.EndConnect($iar)
|
|
return $true
|
|
} catch { return $false }
|
|
finally { $client.Close() }
|
|
}
|
|
|
|
function Get-ServerHealth($port, $timeoutMs = 800) {
|
|
try {
|
|
$r = Invoke-WebRequest -Uri "http://127.0.0.1:$port/healthz" -UseBasicParsing -TimeoutSec ([Math]::Max(1, [int]($timeoutMs/1000)))
|
|
if ($r.Content -match 'ok') { return $true }
|
|
} catch {}
|
|
return $false
|
|
}
|
|
|
|
function Is-PidAlive($processId) {
|
|
if (-not $processId) { return $false }
|
|
$p = Get-Process -Id ([int]$processId) -ErrorAction SilentlyContinue
|
|
return $null -ne $p
|
|
}
|
|
|
|
function Show-State($label, $obj) {
|
|
Out-Line (" [{0}] puerto={1} pid={2} t={3:o}" -f $label, $obj.port, $obj.pid, (Get-Date))
|
|
}
|
|
|
|
Out-Line ""
|
|
Out-Line " [yt-scraper] iniciando servidor local..." Cyan
|
|
|
|
if (-not (Test-Path '.run')) { New-Item -ItemType Directory -Path '.run' | Out-Null }
|
|
|
|
# --- (1) Check for an already-running server registered in server.info ---
|
|
# A bound port is ground truth that a server is live. The cmd-wrapper PID
|
|
# we recorded is a weak signal (cmd /c exits as soon as python takes over),
|
|
# so we DON'T require the PID to be alive -- only that the port responds.
|
|
$registeredPort = $null
|
|
$registeredPid = $null
|
|
$liveExisting = $false
|
|
|
|
if (Test-Path '.run\server.info') {
|
|
$firstLine = (Get-Content '.run\server.info' -TotalCount 1)
|
|
$parts = $firstLine -split '\s+'
|
|
if ($parts.Count -ge 2) {
|
|
$registeredPort = $parts[0]
|
|
$registeredPid = $parts[1]
|
|
$portBusy = Test-PortOpen ([int]$registeredPort) 350
|
|
$pidAlive = Is-PidAlive $registeredPid
|
|
if ($portBusy) {
|
|
# Port is bound and answering -- this IS the running server.
|
|
# Even if the recorded cmd-wrapper PID died, python is alive.
|
|
$liveExisting = $true
|
|
} else {
|
|
Out-Line " [info] Habia un registro viejo de otra sesion; limpiandolo..." Yellow
|
|
Remove-Item '.run\server.info' -Force -ErrorAction SilentlyContinue
|
|
if (-not $pidAlive) {
|
|
# also nuke any stray python on that port, just in case
|
|
Get-NetTCPConnection -State Listen -LocalPort ([int]$registeredPort) -ErrorAction SilentlyContinue |
|
|
ForEach-Object { try { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue } catch {} }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($liveExisting) {
|
|
Out-Line " [ok] servidor ya activo en puerto $registeredPort" Green
|
|
Out-Line " URL: http://localhost:$registeredPort"
|
|
Out-Line " log: .run\server.log"
|
|
Out-Line " stop: stop-server.bat"
|
|
Open-Browser "http://127.0.0.1:$registeredPort" | Out-Null
|
|
Out-Line ""
|
|
exit 0
|
|
}
|
|
|
|
# --- (2) Find free ports (fast TcpListener probe, like the original) ---
|
|
$candidates = @()
|
|
foreach ($p in 8000..8100) {
|
|
try {
|
|
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, $p)
|
|
$listener.Start(); $listener.Stop()
|
|
$candidates += $p
|
|
if ($candidates.Count -ge 8) { break }
|
|
} catch {}
|
|
}
|
|
if ($candidates.Count -eq 0) {
|
|
Out-Line " [error] no se encontro ningun puerto libre (8000-8100)" Red
|
|
exit 2
|
|
}
|
|
# --- (3) Launch uvicorn on each candidate until one sticks ---
|
|
$launched = $false
|
|
$port = 0
|
|
$proc = $null
|
|
|
|
foreach ($candidate in $candidates) {
|
|
Remove-Item '.run\server.log' -Force -ErrorAction SilentlyContinue
|
|
Remove-Item '.run\server.err.log' -Force -ErrorAction SilentlyContinue
|
|
Out-Line " [...] Encendiendo el servidor (intento $candidate)..." Gray
|
|
# Spawn directo de python: un wrapper cmd /c con la ruta entre comillas
|
|
# dispara el quote-stripping de cmd cuando la ruta tiene espacios y el
|
|
# comando muere en silencio. Start-Process con array de args no sufre eso,
|
|
# y ademas server.info registra el PID real de python (no el del wrapper).
|
|
try {
|
|
$proc = Start-Process -FilePath $PythonExe `
|
|
-ArgumentList @('-m','uvicorn','yt_scraper.webapp.app:app','--host','127.0.0.1','--port',"$candidate",'--log-level','info') `
|
|
-WorkingDirectory $root -WindowStyle Minimized -PassThru `
|
|
-RedirectStandardOutput "$root\.run\server.log" `
|
|
-RedirectStandardError "$root\.run\server.err.log"
|
|
} catch {
|
|
Out-Line " [warn] Start-Process fallo en $candidate : $($_.Exception.Message)" Yellow
|
|
continue
|
|
}
|
|
Out-Line " El servidor corre en segundo plano. Registro: .run\server.log"
|
|
|
|
# Fast TCP-poll for the bound port. Bound == uvicorn listening, which
|
|
# is all the user needs to know "open this URL in a browser".
|
|
$boundAt = $null
|
|
$bound = $false
|
|
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
|
for ($i = 0; $i -lt 50; $i++) {
|
|
Start-Sleep -Milliseconds 200
|
|
if (Test-PortOpen $candidate 250) { $bound = $true; $boundAt = $sw.Elapsed; break }
|
|
# show a tiny heartbeat every ~5 attempts
|
|
if ($i -in 5,15,30,45) {
|
|
Out-Line (" ... esperando puerto {0} ({1:0.0}s)" -f $candidate, $sw.Elapsed.TotalSeconds) DarkGray
|
|
}
|
|
}
|
|
|
|
if (-not $bound) {
|
|
Out-Line " [warn] Este intento no respondio; probando el siguiente..." Yellow
|
|
# Show the actual reason from the logs so the user can diagnose.
|
|
foreach ($logName in @('.run\server.err.log', '.run\server.log')) {
|
|
if (Test-Path $logName) {
|
|
$tail = Get-Content $logName -Tail 6 -ErrorAction SilentlyContinue
|
|
if ($tail) {
|
|
Out-Line " (ultimas lineas de $logName):" DarkGray
|
|
$tail | ForEach-Object { Out-Line " $_" DarkGray }
|
|
}
|
|
}
|
|
}
|
|
try { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue } catch {}
|
|
try { Get-NetTCPConnection -State Listen -LocalPort $candidate -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue } } catch {}
|
|
continue
|
|
}
|
|
|
|
# Port is bound. Reveal the URL RIGHT NOW so the user sees progress.
|
|
$port = $candidate
|
|
Set-Content -Path '.run\server.info' -Value "$port $($proc.Id) $(Get-Date -Format o)" -Encoding ASCII
|
|
|
|
Out-Line " [ok] Servidor encendido." Green
|
|
Out-Line " URL: http://localhost:$port"
|
|
Out-Line " log: .run\server.log"
|
|
Out-Line " stop: stop-server.bat"
|
|
Out-Line " Confirmando que todo cargo bien..." DarkGray
|
|
|
|
Open-Browser "http://127.0.0.1:$port" | Out-Null
|
|
|
|
# Confirm /healthz (separate from TCP-bound so the URL appears first).
|
|
$healthAt = $null
|
|
for ($i = 0; $i -lt 25; $i++) {
|
|
Start-Sleep -Milliseconds 200
|
|
if (Get-ServerHealth $port 800) { $healthAt = $sw.Elapsed; break }
|
|
}
|
|
if ($healthAt) {
|
|
Out-Line " [ok] /healthz OK (t=$([int]$healthAt.TotalSeconds)s)" Green
|
|
} else {
|
|
Out-Line " [warn] El servidor abrio pero tardo en responder; revisa .run\server.log" Yellow
|
|
}
|
|
Out-Line ""
|
|
$launched = $true
|
|
break
|
|
}
|
|
|
|
if (-not $launched) {
|
|
Out-Line " [error] no se pudo iniciar el servidor en ninguno de los puertos candidatos." Red
|
|
if (Test-Path '.run\server.log') {
|
|
Out-Line " --- ultimo log del servidor ---" Yellow
|
|
Get-Content '.run\server.log' -Tail 25 | ForEach-Object { Out-Line " $_" }
|
|
} else {
|
|
Out-Line " instala dependencias: pip install -e `".[web]`"" Yellow
|
|
}
|
|
exit 3
|
|
}
|