# Guía de Importación de Cursos

Cómo capturar un curso desde cualquier plataforma (ClickFunnels, Teachable,
Kajabi, etc.) y dejarlo disponible offline en esta plataforma.

---

## Scripts Disponibles

| Script | Función |
|--------|---------|
| `download_all.py` | Navega el curso con Playwright e intercepta URLs de video (Vidalytics/HLS) para descargarlos con yt-dlp |
| `download_videos.py` | Descarga videos usando las URLs ya capturadas en `course_structure.json` |
| `download_content.py` | Descarga archivos de audio (MP3) y documentos (PDF/ZIP) a storage local |
| `scrape_subtitles.py` | Navega el curso con Playwright, intercepta archivos VTT del player Vidalytics y los guarda en `public/subtitles/` |
| `scrape_lesson_content.py` | Navega el curso con Playwright, extrae el texto bilingüe (ES/EN) de cada lección del HTML de la página |
| `fix_subtitle_languages.py` | Detecta idioma real de cada VTT por contenido y renombra PT/ES mal etiquetados |
| `clean_lesson_notes.py` | Post-procesa el texto de lecciones: elimina ruido del player, formatea pares bilingües EN/ES |
| `build_course_map.py` | Lee `course_content.json` y genera `course_map.json` con títulos, capítulos y secciones |
| `inspect_lesson_dom.py` | Herramienta de diagnóstico: abre el browser y guarda el HTML/texto de las primeras lecciones para identificar selectores |

---

## Flujo Completo: Importar un Curso Nuevo

### Pre-requisitos
- Python 3.11+ con `playwright`, `yt-dlp`, `beautifulsoup4` instalados
- ffprobe (incluido en ffmpeg) en el PATH
- XAMPP corriendo con este proyecto en `http://127.0.0.1:8080`

```bash
pip install playwright yt-dlp beautifulsoup4
playwright install chromium
```

---

### Paso 0 — Obtener Cookies de Sesión

El browser debe estar logueado en la plataforma. Las cookies se guardan en
`.course_cookies.json` para que los scripts las reutilicen sin re-loguearse.

**Opción A — Con Playwright (automático):**
```python
# Incluir en cualquier script de configuración inicial:
from playwright.async_api import async_playwright
async with async_playwright() as pw:
    browser = await pw.chromium.launch(headless=False)
    page = await browser.new_page()
    await page.goto("https://tuplataforma.com/login")
    # Loguearse manualmente en el browser que se abre
    input("Presiona Enter cuando hayas iniciado sesión...")
    cookies = await browser.contexts[0].cookies()
    import json
    open("scripts/.course_cookies.json", "w").write(json.dumps(cookies))
```

**Opción B — Exportar desde Chrome:**
Instalar la extensión "Cookie-Editor", navegar a la plataforma logueado,
exportar todas las cookies en formato JSON y guardar como `scripts/.course_cookies.json`.

---

### Paso 1 — Capturar Estructura del Curso

Navegar la plataforma e interceptar los IDs/URLs de video por lección:

```bash
python scripts/download_all.py
```

Genera: `scripts/course_structure.json`

```json
[
  {"num": 1, "title": "Introducción", "vidalytics_id": "abc123", "stream_url": "https://..."},
  {"num": 2, "title": "Lección 2",    "vidalytics_id": "def456", "stream_url": "https://..."}
]
```

---

### Paso 2 — Descargar Videos

Con `course_structure.json` ya generado, descarga todos los videos:

```bash
python scripts/download_videos.py
```

Los videos se guardan como `storage/app/course-media/{slug}/{num:02d}.mp4`

Para cursos sin Vidalytics, editar `download_videos.py` para apuntar a las URLs correctas.

---

### Paso 3 — Descargar Audio y Documentos

```bash
python scripts/download_content.py
```

Descarga los archivos enlazados en cada lección (MP3, PDF, ZIP) de S3 u otro CDN.
Guarda en `storage/app/course-media/{slug}/audio/` y `docs/`.

---

### Paso 4 — Descargar Subtítulos

Solo para cursos con Vidalytics (el player genera VTTs dinámicamente):

```bash
python scripts/scrape_subtitles.py
```

Guarda en `public/subtitles/{num:02d}-{lang}.vtt`
Idiomas soportados: `es`, `en`, `pt`

Si los VTTs tienen idioma incorrecto:
```bash
python scripts/fix_subtitle_languages.py
```

---

### Paso 5 — Scrape de Contenido de Lecciones

Extrae el texto bilingüe / descripción que aparece debajo del video:

```bash
python scripts/scrape_lesson_content.py
```

Guarda el contenido en `course_map.json` → campo `notes`.

Si la extracción tiene ruido (elementos del player de video):
```bash
python scripts/clean_lesson_notes.py
```

---

### Paso 6 — Construir el Mapa del Curso

Genera `course_map.json` con títulos reales, capítulos y secciones:

```bash
python scripts/build_course_map.py
```

---

### Paso 7 — Importar a la Base de Datos

```bash
php artisan course:import --force
```

Esto crea o actualiza el curso y todas sus lecciones en SQLite.
Lee metadatos del curso desde `scripts/course_config.json`.
Para un curso nuevo, editar ese archivo con el slug/título/instructor correcto.

---

### Paso 8 — Calcular Duraciones de Video

```bash
# Desde tinker o script rápido:
php artisan tinker
>>> App\Models\Lesson::whereNull('duration_seconds')
      ->whereNotNull('video_path')
      ->each(function($l) {
          $out = shell_exec("ffprobe -v quiet -print_format json -show_format \"{$l->video_path}\"");
          $data = json_decode($out, true);
          if ($sec = $data['format']['duration'] ?? null) {
              $l->update(['duration_seconds' => (int)$sec]);
          }
      });
```

---

## Adaptar para un Curso Nuevo

### Paso 1 — Editar constantes en los scripts Python

```python
MEMBERS_URL = "https://nueva-plataforma.com/members-capcut"  # URL del curso
COURSE_SLUG = "curso-capcut"                                  # slug en la DB
```

### Paso 2 — Crear course_config.json

```json
{
  "slug": "curso-capcut",
  "title": "CapCut Pro",
  "instructor": "Nombre del instructor",
  "tagline": "Descripción corta del curso",
  "description": "Descripción larga para la página del curso."
}
```

Guardar en `scripts/course_config.json` (o en cualquier ruta y pasarla con `--config`).

### Paso 3 — Ejecutar el pipeline normalmente

```bash
php artisan course:import --force
# O con rutas explícitas:
php artisan course:import --file=scripts/capcut_map.json --config=scripts/capcut_config.json --force
```

La plataforma soporta múltiples cursos automáticamente — cada slug crea un curso separado en la DB.

---

## Estructura de Archivos

```
scripts/
  .course_cookies.json      ← cookies de sesión (no commitear a git)
  course_structure.json     ← IDs y stream URLs por lección
  course_content.json       ← metadatos: audio, docs, subtítulos
  course_map.json           ← mapa final con títulos, capítulos, notas
  course_config.json        ← metadatos del curso (slug, título, instructor)
  IMPORT_GUIDE.md           ← esta guía

  download_all.py           ← intercepta videos con Playwright
  download_videos.py        ← descarga videos con yt-dlp
  download_content.py       ← descarga audio/docs
  scrape_subtitles.py       ← captura VTTs de Vidalytics
  scrape_lesson_content.py  ← extrae texto bilingüe de las páginas
  fix_subtitle_languages.py ← corrige idioma de VTTs
  clean_lesson_notes.py     ← limpia y formatea los notes
  build_course_map.py       ← genera course_map.json final
  inspect_lesson_dom.py     ← herramienta de diagnóstico DOM

  archive/                  ← scripts de debug (no usar en producción)

PHP artisan commands:
  course:import             ← importa cualquier curso desde course_map.json + course_config.json
  raio:import               ← alias heredado (mantener para compatibilidad)
```

---

## Notas Importantes

- Los subtítulos VTT de Vidalytics se cargan dinámicamente por JS, **no están en el m3u8**.
  El script de subtítulos usa `page.on("request")` para interceptarlos mientras el player carga.
- El texto bilingüe está en el HTML de la página ClickFunnels debajo del video.
  El `body.innerText` incluye la barra de navegación lateral — el script la filtra por patrones.
- Las cookies expiran. Si un script falla con 401/403, re-exportar cookies y volver a ejecutar.
- `--force` en `raio:import` elimina y recrea todas las lecciones. Sin `--force`, solo actualiza campos vacíos.
