feat: local content-mining platform for YouTube creator scraping

Scraper de canales de YouTube hacia notas Markdown para base de conocimiento
(Obsidian-ready), con plataforma web local.

Engine + CLI (Workstream A):
- Modular pipeline: discover/extract/parse/chapters/render/store + ratelimit
- SQLite store con migración idempotente: FTS5 (transcript search), columnas
  de metadata enriquecida, tablas cookies_meta y scrape_jobs
- Módulos: segments, cookies (Netscape vault), export (json/csv/srt/html),
  analysis (word freq/timeline/wordcloud), monitor (watch loop), pipeline
- CLI Click group: search, export, audio, channels, watch, analyze, re-render
- Fix del bug de scoping de cookies en cli.py

Webapp local (Workstream B):
- FastAPI backend: dashboard, channels, videos facetado, transcript, search
  FTS, analysis, scrape jobs con SSE, cookies drag-and-drop, exports,
  folders (abrir en OS), tools (re-render, formato)
- SPA no-build (Alpine.js + Tailwind + Chart.js por CDN): 9 vistas, tema
  dark command-center con acento rojo→rosa, cookie vault drag-drop,
  consola de scrapeo con progreso live vía SSE

Launcher + subagentes (Workstream C):
- start-server.bat / stop-server.bat con auto port-scan + browser open
- .opencode/agent/webapp-builder.md + .opencode/goals/webapp-build.md

Tests: 38 pytest verdes. Sin funcionalidad de IA (enfoque data-mining).
This commit is contained in:
urieljareth
2026-07-26 23:19:34 -06:00
commit 621bbc5f5c
45 changed files with 6541 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
Set-Location -LiteralPath $root
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
Start-Process "http://localhost:$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
}
Start-Process "http://localhost:$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 ""