--- name: gnula_grabber lang: py domain: infra version: 0.1.0 description: "Pipeline 2-partes: detecta pelis en castellano en gnula (crawler→catálogo SQLite) y las descarga via Chrome+CDP+NordVPN (HLS streaming+descifrado AES) → manual/movies → Radarr/Jellyfin. Captcha=humano." tags: [gnula, hls, scraping, cdp, nordvpn, media, popelis, navegator] uses_functions: - extract_hls_from_cdp_tab_py_pipelines - start_nordvpn_socks_bridge_bash_infra - popelis_import_media_drop_py_infra - chrome_load_extensions_bash_browser uses_types: [] framework: "" entry_point: "crawl.py" dir_path: "apps/gnula_grabber" repo_url: "" --- ## Propósito Sistema 2-partes para poblar la biblioteca Jellyfin con pelis en **castellano** desde gnula (gnularetro.cc), saltando el bloqueo del ISP (DPI Allot, vía NordVPN) y Cloudflare (navegador real). ## Parte 1 — Detector (`crawl.py`) Crawler vía **FlareSolverr** (Chrome headless NordVPN+CF). Recorre listados/categorías, parsea `
`, filtra los que tienen bandera **es.png** (= "Español" dentro de la página; el alt-text del listado está invertido — usar el nombre de imagen), dedup vs catálogo + Radarr, y guarda en SQLite `~/.config/popelis/gnula_catalog.db` (tabla `movies`: href, title, year, flags, lang_es, status, in_library). ```bash python3 apps/gnula_grabber/crawl.py "peliculas/estrenos" 2 python3 apps/gnula_grabber/crawl.py "peliculas/accion" 2 # paginación rota -> recorrer categorías ``` ## Parte 2 — Downloader (`download.py`) Consume el catálogo (pelis `status=pending`). Por cada una: navega a su página en el Chrome CDP, clica el server **Español** (luluvid/luluvdo, `li[data-nume]` del grupo es.png), quita el overlay de ad (`div z-index>=1000`) + `jwplayer().play()` + trusted click, espera `master.m3u8` (captcha=humano), y delega a `grab_stream.py`. Marca `downloaded`. ```bash python3 apps/gnula_grabber/download.py 1 # baja 1 peli pending ``` ## Núcleo — `grab_stream.py` Descarga HLS **streaming a disco** (no blob → no peta Chrome): in-page fetchea cada segmento crudo (sesión browser = pasa el 522 CF), lo manda por CDP, y LOCAL descifra AES-128-CBC (key+IV de `#EXT-X-KEY`; IV=media-sequence) + append a `.ts` + remux `ffmpeg -c copy` → `.mkv` directo a `F:\POPELIS\manual\movies`. El timer systemd `popelis-import.timer` lo importa (metadata Radarr) → `media` → Jellyfin (realtime monitor). ```bash python3 apps/gnula_grabber/grab_stream.py "Titulo (2025)" # con el player luluvdo reproduciendo ``` ## Prerequisitos - Bridge NordVPN (`start_nordvpn_socks_bridge --port 8889`). - gluetun+FlareSolverr arriba (para el crawler). - Chrome CDP+NordVPN con perfil **`gnula_popelis`** (cookies de gnula/luluvdo + uBlock Origin Lite ya instalado vía Web Store — persiste en el perfil, no se pasan flags de ext; `--load-extension` está MUERTO en Chrome stable 148, ver `chrome_load_extensions_bash_browser`): ```bash CHROME="/mnt/c/Program Files/Google/Chrome/Application/chrome.exe" setsid "$CHROME" --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 \ '--remote-allow-origins=*' \ '--user-data-dir=C:\Users\lucas\AppData\Local\gnula_popelis' \ --proxy-server=http://127.0.0.1:8889 --no-first-run --no-default-browser-check \ 'https://www.gnularetro.cc/' /tmp/chrome_gnula.log 2>&1 & ``` - venv con `pycryptodome` + `websocket-client` (vía `uv run --with`). ## Gotchas - Ver `README.md` para el flujo completo + gotchas (overlay ad, --load-extension Chrome 148, token efímero, 522 solo-externos, paginación gnula rota → categorías).