docs: documentacion completa para humanos y agentes IA

- README reescrito: ejemplos CLI corregidos (flags van en el subcomando scrape),
  webapp+doctor documentados, requisitos por OS, cookies, datos generados, migracion de maquina
- CLAUDE.md actualizado: conteo de tests, refs de linea reparadas, scripts nuevos,
  checklist de actualizacion de docs
- AGENTS.md nuevo: entrada estandar para cualquier agente de codigo (mapa de modulos,
  comandos, reglas de oro, punteros a docs)
- docs/GETTING-STARTED.md: de cero a webapp por OS con troubleshooting
- docs/CONFIG.md: referencia completa de las ~25 claves de config
- docs/COOKIES.md: adquisicion, import (web/navegador/CLI), rotacion y seguridad
- docs/backlog.md reemplaza OPPORTUNITIES.md (distilado: shipped vs pendiente)
- auditorias movidas a docs/audits/ y CLIPPER-COMPARISON-AUDIT.md versionado
This commit is contained in:
urieljareth
2026-09-10 00:32:32 -06:00
parent 3c49f73fa2
commit 450aee216d
11 changed files with 791 additions and 454 deletions
+110
View File
@@ -0,0 +1,110 @@
# Referencia de configuración (`config.yaml`)
`config.yaml` es tu configuración privada (gitignored). La plantilla versionada `config.example.yaml` documenta todas las claves y es el punto de partida (`cp config.example.yaml config.yaml`). Todas las rutas del archivo se resuelven **relativas al CWD** — ejecuta siempre desde la raíz del repo.
Los defaults que siguen son los de `config.example.yaml` (entre paréntesis, cuando difiere, el default del código en `src/yt_scraper/config.py`).
## Bloque raíz — canal, idiomas y filtros
| Clave | Tipo | Default | Qué afecta |
|---|---|---|---|
| `channel_url` | str | `"https://www.youtube.com/@Nostal-Vlad/videos"` | Canal por defecto de `yt-scraper scrape` / webapp. Se ignora si pasas `--channel`/`-c`. Acepta URL completa, `/channel/UC...` o handle |
| `languages` | dict \| list | `{es: any, es-419: any, en: any}` | Idiomas de subtítulos aceptables y su modo (ver abajo). Forma legacy: lista `["es","en"]` — todos con el modo de `prefer_manual`; evítala, un canal solo-auto quedaría en `no_subtitles` |
| `prefer_manual` | bool | `true` | En modo `any`: prueba subtítulos manuales antes que los automáticos |
| `include_shorts` | bool | `false` | Descubrir/procesar Shorts. En CLI: `--include-shorts` / `--no-shorts` |
| `include_live` | bool | `true` | Procesar directos. En CLI: `--no-live` |
| `min_duration_sec` | int | `30` (código: `0`) | Duración mínima del vídeo en segundos; filtra en discovery |
Modos por idioma de `languages`:
| Valor | Significado |
|---|---|
| `manual` | **Solo** subtítulos subidos por el creador; si solo hay automáticos → `no_subtitles` |
| `auto` | **Solo** subtítulos automáticos (ASR) |
| `any` | Manuales primero (según `prefer_manual`), automáticos como fallback. **Recomendado** |
Nota: `languages` es una preferencia entre idiomas que sabes leer, no una orden de traducir. El idioma hablado del vídeo siempre gana si está en la lista; nunca se guarda una traducción automática si existe el transcript original.
## Bloque `delay` — pacing y rate-limit
YouTube no publica límites; el techo práctico conocido (wiki de yt-dlp) es ~300 vídeos/hora en sesión sin cuenta. Estos valores apuntan por debajo. Con los defaults conservadores, una tirada real cuesta **del orden de 12–13 s por vídeo** (~270 vídeos/hora).
| Clave | Tipo | Default | Qué afecta |
|---|---|---|---|
| `min_seconds` | float | `1.5` | Pausa aleatoria mínima entre vídeos (el gap real se sortea entre `min` y `max`) |
| `max_seconds` | float | `3.5` | Pausa aleatoria máxima entre vídeos |
| `backoff_base` | float | `2.0` | Backoff tras un rate-limit: `min(base * 2**n + jitter, cap)`, con n = fallos consecutivos |
| `backoff_cap` | float | `60.0` | Techo del backoff en segundos |
| `throttle_threshold` | int | `3` | Rate-limits **consecutivos** que hacen parar la tirada (circuit breaker). Lo no procesado queda `pending` y es recuperable |
| `min_request_interval` | float | `0.0` (example) | Separación mínima entre **cualquier** dos peticiones a YouTube del proceso (Pacer global, incluye los `/api/tools/*` fuera del job runner). `0` = desactivado. Es la única palanca que actúa en todas partes a la vez |
| `audio_rate_limit` | int | `0` | Techo de bytes/segundo para descargas de audio (ratelimit de yt-dlp). `0` = ilimitado |
Referencia práctica: la config real del autor usa `min_request_interval: 2.5` para ser aún más conservador cuando la webapp y el CLI conviven.
## Bloque `yt_dlp` — reintentos y timeouts
| Clave | Tipo | Default | Qué afecta |
|---|---|---|---|
| `retries` | int | `10` | Reintentos de descarga; solo muerde en el camino de audio (el extractor de YouTube de yt-dlp no reintenta 403/429) |
| `sleep_subrequests` | float | `2` | Segundos entre las peticiones HTTP **dentro** de una extracción (watch page, llamada player, continuations). Se reenvía a yt-dlp como `sleep_interval_requests` (nombre interno del proyecto; el yt-dlp no tiene ninguna opción llamada `sleep_subrequests`) |
| `extractor_retries` | int | `3` | Reintentos durante la extracción (solo 5xx y red) |
| `socket_timeout` | float | `30.0` | Timeout de socket; sin él una conexión colgada bloquea el worker para siempre |
## Bloque `sync` — discovery incremental
Re-escanear un canal trackeado lee la pestaña `/videos` (cronológica inversa) y corta al ver `overlap` vídeos ya conocidos: un sync rutinario cuesta 1 página, no el canal entero.
| Clave | Tipo | Default | Qué afecta |
|---|---|---|---|
| `incremental` | bool | `true` | Activar el modo ventana. `false` (o `--full` / "Full rescan" en la webapp) recorre todo el canal |
| `window` | int | `30` | Entradas leídas en la primera pasada |
| `max_window` | int | `300` | Techo: si TODA la ventana resulta nueva, se duplica hasta aquí antes de declarar el scan truncado (aviso "usa `--full`") |
| `overlap` | int | `3` | Vídeos conocidos consecutivos que dan la sincronización por alcanzada |
## Rutas y plantillas
| Clave | Tipo | Default | Qué afecta |
|---|---|---|---|
| `database_path` | str | `"data/state.db"` | Ruta de la BD SQLite (WAL). Relativa al CWD |
| `output_dir` | str | `"data/markdown"` | Directorio de notas; el resto de datos (`audio/`, `thumbnails/`, ...) se deriva de su padre |
| `template_path` | str | `"templates/video.md.j2"` | Plantilla Jinja2 de la nota. OJO: el formato del `.md` es un contrato bidireccional (los regex de `segments.py` lo re-lean) |
| `filename_template` | str | `"{upload_date}_{slug}"` | Patrón del nombre de las notas. Placeholders: `{upload_date}` (fecha de subida; `unknown-date` si se desconoce), `{slug}` (slug del título, máx 60), `{title}` (título crudo), `{video_id}` (id estable de YouTube — útil porque título y fecha son inestables) |
## Ejemplos
**Mínima** (defaults razonables, primer contacto):
```yaml
channel_url: "https://www.youtube.com/@MiCanal/videos"
languages:
es: any
```
**Conservadora** (muchos vídeos, webapp y CLI a la vez, o historial de rate-limits):
```yaml
channel_url: "https://www.youtube.com/@MiCanal/videos"
languages:
es: any
es-419: any
en: any
prefer_manual: true
include_shorts: false
min_duration_sec: 30
delay:
min_seconds: 1.5
max_seconds: 3.5
backoff_base: 2.0
backoff_cap: 60.0
throttle_threshold: 3
min_request_interval: 2.5 # pacer global: la palanca más eficaz contra el bot-wall
sync:
incremental: true
window: 30
max_window: 300
overlap: 3
```
Si cambias los tiempos, vuelve a medir sobre tu canal: la constante es ~3 peticiones a `youtube.com` por vídeo y de ahí sale todo lo demás.