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).
164 lines
4.3 KiB
Markdown
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.
|