Files
yt-channel-scraper/scripts/start-server.ps1
T
urieljareth 0682d2d806 feat: batch .md download, multi-select, thumbnails, all OPPORTUNITIES tools
Videos view: multi-select (checkboxes) + select-all/deselect-all + bulk action
bar (Download .md batch, Download thumbnails, Download audio). Per-video buttons:
.md download (done) or Process to .md (pending), clip.

Channels: per-channel pending count + 'Download pending .md' (batches pending
video_ids). Each channel keeps Videos/.md folder/Remove actions.

Backend (jobs.py + api.py):
- POST /api/scrape/batch {video_ids} + POST /api/scrape/video/{id} (on-demand
  .md for any video incl. pending) as SSE-tracked jobs
- GET /api/videos/{id}/markdown (file download)
- POST /api/tools/thumbnails + GET /api/thumbnails/{id} (local cache, offline)
- GET /api/clip/{id}?from=&to= (transcript segment)
- GET /api/stats (aggregate totals)
- POST /api/tools/audio (mp3 job)

Tools view rebuilt as full Knowledge Base grid surfacing every OPPORTUNITIES.md
feature as electable buttons (md download, re-render, export, search, analysis,
thumbnails, audio, clip, stats, cookies, channels, watch, open folders).

Floating job widget (SSE) for any running job. Thumbnails served via
/api/thumbnails/{id} everywhere (works offline once cached).

Launcher: robust Open-Browser helper (3 fallback methods) auto-opens
http://127.0.0.1:<port> on start + on idempotent re-open. data/ gitignored.
2026-07-26 23:39:05 -06:00

103 lines
4.1 KiB
PowerShell

$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
Set-Location -LiteralPath $root
# Robust browser opener — tries several Windows methods until one works.
function Open-Browser($url) {
$opened = $false
# Method 1: Start-Process with the URL (default protocol handler)
try { Start-Process -FilePath $url; $opened = $true } catch {}
if (-not $opened) {
# Method 2: explorer.exe with the URL (opens default browser)
try { & explorer.exe $url; $opened = $true } catch {}
}
if (-not $opened) {
# Method 3: cmd `start` builtin
try { & cmd.exe /c start "" $url; $opened = $true } catch {}
}
if ($opened) { Write-Host " [ok] abriendo navegador -> $url" -ForegroundColor DarkGray }
else { Write-Host " [warn] no se pudo abrir el navegador. Abre manualmente: $url" -ForegroundColor Yellow }
return $opened
}
Write-Host ""
Write-Host " [yt-scraper] iniciando servidor local..." -ForegroundColor Cyan
if (-not (Test-Path '.run')) { New-Item -ItemType Directory -Path '.run' | Out-Null }
# Already running?
if (Test-Path '.run\server.info') {
$firstLine = (Get-Content '.run\server.info' -TotalCount 1)
$existingPort = ($firstLine -split '\s+')[0]
try {
$r = Invoke-WebRequest -Uri "http://localhost:$existingPort/healthz" -UseBasicParsing -TimeoutSec 2
if ($r.Content -match 'ok') {
Write-Host " [ok] servidor ya activo en el puerto $existingPort" -ForegroundColor Green
Open-Browser "http://127.0.0.1:$existingPort"
exit 0
}
} catch {}
}
# Collect candidate free ports scanning from 8000.
$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) {
Write-Host " [error] no se encontro ningun puerto libre (8000-8100)" -ForegroundColor Red
exit 2
}
# Launch uvicorn DETACHED in its own persistent minimized console window, logging to .run/server.log.
# The `cmd /c` window is an independent console: it survives this launcher exiting.
$launched = $false
$proc = $null
$port = 0
foreach ($candidate in $candidates) {
# clear previous log
Remove-Item '.run\server.log' -Force -ErrorAction SilentlyContinue
$cmdLine = '/c python -m uvicorn yt_scraper.webapp.app:app --host 127.0.0.1 --port ' + $candidate + ' --log-level info > .run\server.log 2>&1'
$proc = Start-Process -FilePath 'cmd.exe' -ArgumentList $cmdLine -WorkingDirectory $root -WindowStyle Minimized -PassThru
Set-Content -Path '.run\server.info' -Value "$candidate $($proc.Id) $(Get-Date -Format o)" -Encoding ASCII
$ok = $false
for ($i = 0; $i -lt 40; $i++) {
Start-Sleep -Milliseconds 500
try {
$r = Invoke-WebRequest -Uri "http://localhost:$candidate/healthz" -UseBasicParsing -TimeoutSec 2
if ($r.Content -match 'ok') { $ok = $true; break }
} catch {}
}
if ($ok) { $port = $candidate; $launched = $true; break }
# failed on this port — show why from the log, then try the next candidate
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Remove-Item '.run\server.info' -Force -ErrorAction SilentlyContinue
}
if (-not $launched) {
Write-Host " [error] no se pudo iniciar el servidor." -ForegroundColor Red
if (Test-Path '.run\server.log') {
Write-Host " --- ultimo log del servidor ---" -ForegroundColor Yellow
Get-Content '.run\server.log' -Tail 25 | ForEach-Object { Write-Host " $_" }
} else {
Write-Host " instala dependencias: pip install -e `".[web]`"" -ForegroundColor Yellow
}
exit 3
}
Open-Browser "http://127.0.0.1:$port"
Write-Host ""
Write-Host " [ok] servidor activo" -ForegroundColor Green
Write-Host " URL: http://localhost:$port"
Write-Host " PID: $($proc.Id) (ventana minimizada 'cmd' independiente)"
Write-Host " log: .run\server.log"
Write-Host " stop: stop-server.bat"
Write-Host ""