Files
yt-channel-scraper/README.md
T
urieljareth 621bbc5f5c 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).
2026-07-26 23:19:34 -06:00

164 lines
4.3 KiB
Markdown

# yt-channel-scraper
Scraper de canales de YouTube que extrae transcripciones, capítulos y metadatos completos, generando notas Markdown listas para Obsidian.
Basado en la ingeniería inversa de [Obsidian Web Clipper](https://obsidian.md/) — usa `yt-dlp` internamente, que implementa el mismo mecanismo InnerTube (`youtubei/v1/player` con clientes ANDROID/IOS/WEB) que la extensión audita.
## Instalación
```bash
cd yt-channel-scraper
pip install -e ".[dev]"
```
Requiere Python ≥ 3.10.
## Uso
### Scrape completo de un canal
```bash
yt-scraper --channel "https://www.youtube.com/@Nostal-Vlad/videos"
```
### Dry run (ver qué descubriría sin descargar)
```bash
yt-scraper --channel "https://www.youtube.com/@Nostal-Vlad/videos" --dry-run --limit 10
```
### Solo vídeos recientes
```bash
yt-scraper --channel "https://www.youtube.com/@Nostal-Vlad/videos" --since 2025-01-01
```
### Reanudar tras interrupción
```bash
yt-scraper --channel "https://www.youtube.com/@Nostal-Vlad/videos"
```
El estado se guarda en `data/state.db` (SQLite). Los vídeos ya procesados se saltan automáticamente.
### Reintentar vídeos con error
```bash
yt-scraper --channel "https://www.youtube.com/@Nostal-Vlad/videos" --reset-errors
```
## Opciones
| Flag | Descripción | Default |
|---|---|---|
| `--channel, -c` | URL del canal | de config.yaml |
| `--config` | Ruta al YAML de configuración | `config.yaml` |
| `--limit N` | Procesar solo N vídeos | sin límite |
| `--since DATE` | Solo vídeos desde YYYY-MM-DD | sin filtro |
| `--languages, -l` | Idiomas preferidos (coma-sep) | `es,en` |
| `--no-auto` | Ignorar subtítulos auto-generados | false |
| `--no-shorts` | Excluir Shorts | de config |
| `--include-shorts` | Incluir Shorts | de config |
| `--resume/--no-resume` | Saltar procesados | `--resume` |
| `--dry-run` | Solo discovery, no descargar | false |
| `--reset-errors` | Reintentar vídeos con error | false |
| `--verbose, -v` | Logging DEBUG | false |
## Configuración
Copia `config.example.yaml` a `config.yaml` y edita:
```yaml
channel_url: "https://www.youtube.com/@Nostal-Vlad/videos"
languages: ["es", "es-419", "en"]
prefer_manual: true
include_shorts: false
min_duration_sec: 30
delay:
min_seconds: 1.5
max_seconds: 3.5
```
## Output
Cada vídeo genera un archivo Markdown en `data/markdown/@canal/`:
```
data/markdown/Nostal Vlad/
├── 2026-07-26_asi-era-ser-una-adolescente-edgy-en-los-2000.md
├── 2026-07-12_la-estetica-que-romantiza-ser-un-perdedor-losercore.md
└── ...
```
Formato de cada nota:
```markdown
---
video_id: gOUyxFwWQqA
title: "La Estética Que ROMANTIZA ser un \"PERDEDOR\" | Losercore"
channel: Nostal Vlad
upload_date: 2026-07-12
duration: 1069
url: https://www.youtube.com/watch?v=gOUyxFwWQqA
transcript_lang: es-orig
transcript_src: auto
views: 100523
likes: 8196
---
# La Estética Que ROMANTIZA ser un "PERDEDOR" | Losercore
> [Ver en YouTube](https://www.youtube.com/watch?v=gOUyxFwWQqA)
## Transcripcion
### Intro (00:15)
**00:15** · Una de las estéticas que ha cobrado más relevancia últimamente...
**00:28** · que hacer esto. Y es que el loser core como tal es muy difuso...
### Losercore (01:38)
**01:38** · ...
```
## Estados en SQLite
| Status | Significado |
|---|---|
| `pending` | Descubierto, sin procesar |
| `done` | Transcripción extraída y Markdown generado |
| `no_subtitles` | El vídeo no tiene subtítulos (ni manuales ni auto) |
| `error` | Error al procesar (miembros-only, privado, bloqueo, etc.) |
## Arquitectura
```
cli.py Orquestador (Click + Rich progress)
├── discover.py yt-dlp --flat-playlist → lista de videoIds
├── store.py SQLite: estado, resume, dedup
├── extract.py yt-dlp.extract_info → metadata + subtítulos
├── parse.py JSON3/VTT → segmentos {start, end, text}
├── chapters.py align_chapters: capítulos ↔ segmentos
├── render.py Jinja2 → Markdown con frontmatter YAML
└── ratelimit.py delays aleatorios + backoff exponencial
```
## Tests
```bash
python -m pytest tests/ -v
```
## Mantenimiento
Si la extracción falla tras una actualización de YouTube:
```bash
yt-dlp -U # actualizar yt-dlp
pip install -U yt-dlp
```
Las versiones de cliente InnerTube (ANDROID `20.10.x`, IOS `20.10.x`, WEB `2.2024xxxx`) rotan mensualmente. `yt-dlp` las mantiene actualizadas.