Versión frontend-only (sin backend) del proyecto de streaming de Lerna Soft.
Este repositorio está optimizado para correr en GitHub Pages y usar localStorage como capa de persistencia local (catálogo, progreso y cachés).
- Repositorio:
lerna-admin/devil-tv - URL pública:
https://lerna-admin.github.io/devil-tv/ - Publicación activa desde rama:
gh-pages - Rama de desarrollo:
main - Reproductor primario: VidAPI (
https://vaplayer.ru/embed/...) - Los episodios se publican como assets estáticos por serie:
assets/episodes/index.jsonassets/episodes/<imdbId>.txt
- El catálogo de episodios no se carga como un solo archivo grande en runtime.
- La app consulta un manifest liviano en
assets/episodes/index.json. - Cada serie tiene su propio archivo en
assets/episodes/<imdbId>.txt. - El generador está en
tools/build-episode-assets.mjs. - El pipeline de Pages sincroniza issues con label
episode-syncantes de reconstruir episodios. - El sincronizador está en
tools/sync-episode-targets-from-issues.mjs. - Después del deploy, el workflow cierra automáticamente los issues ya procesados para no reprocesarlos.
- Cada issue con label
episode-synctambién dispara un workflow dedicado que intenta resolverlo de inmediato y deja los assets actualizados enmain. - El progreso de visualización entre dispositivos se sincroniza con label
watch-progress-sync. - El sincronizador está en
tools/sync-watch-progress-from-issues.mjs. - El workflow dedicado escribe en
assets/watch-progress/y conservalocalStoragecomo fallback local. - Cada issue con label
watch-progress-synctambién dispara un workflow dedicado que intenta resolverlo de inmediato y deja el progreso actualizado enmain. - Los targets actuales están en
assets/episodes/targets.json. - El workflow de Pages ejecuta ese script antes de publicar.
- Fuente usada para construir los assets: TVMaze.
- El botón de reporte puede crear issues directo por API si defines un token ofuscado en
app.js:GITHUB_ISSUE_TOKEN_CIPHERGITHUB_ISSUE_TOKEN_SEED- El token se decodifica en el cliente y ya no pide prompt ni abre una pestaña nueva.
- Flujo principal funcional:
- búsqueda -> detalle -> watch fullscreen.
- En detalle se ocultan distracciones del fondo.
- En watch de series hay controles de navegación (volver, anterior, siguiente).
- Persistencia local de progreso/vistos funcional.
- Estado: usable para pruebas reales, con ajustes UX todavía iterables.
- Flujo funcional completo en navegación y reproducción.
- Rutas hash y restauración de estado operativas.
- Persistencia local y cache operativos.
- Estado: estable para uso de prueba.
- Búsqueda de títulos en fuentes públicas (VidAPI + fallback IMDb vía proxy web).
- Listado combinado con ranking de relevancia.
- Guardado incremental de resultados en
localStoragepara acelerar búsquedas futuras. - Filtro para no listar series sin episodios disponibles.
- Vista de detalle tipo plataforma (en mobile, foco en detalle y sin distracciones del fondo).
- Rutas hash SPA:
#/browse?...#/title/<movie|series>/<id>?...#/watch/<movie>/<id>?...#/watch/<series>/<id>/<season>/<episode>?...
- Reproductor fullscreen en modal.
- Para series:
- temporadas y capítulos,
- marcado de vistos,
- reanudar siguiente capítulo,
- navegación en player:
Volver a la serie,Capítulo anterior,Siguiente capítulo, - autoplay al siguiente episodio en evento
completed.
Claves principales:
mep_static_catalog: cache de títulos descubiertos.mep_series_eps_<imdbId>: cache de temporadas/capítulos por serie (con TTL).mep_series_progress_<id>: progreso por serie (vistos, último capítulo).mep_last_watch: snapshot de reproducción reciente.mep_last_selection: última selección en UI.mep_watch_progress_<email>: caché local del progreso sincronizado por usuario.assets/watch-progress/: persistencia compartida para progreso entre dispositivos.
TTL actual de episodios por serie: 14 días.
La plataforma usa el lenguaje operativo EPE para identificar y manejar archivos sensibles.
- Un archivo EPE tiene contenido ofuscado con prefijo
EPE2:(actual). EPE2agrega verificación de integridad (HMAC) para detectar manipulación.- La llave de cifrado/descifrado es el nombre del archivo (basename), no una seed fija global.
- Si cambias el nombre del archivo EPE, cambia su llave y hay que re-encriptarlo.
- El sistema opera en EPE2-only (sin retrocompatibilidad).
Convenciones EPE:
puntero: el nombre del archivo (actúa como llave).giro: rotación/cambio de puntero (renombrar + re-encriptar).boveda: archivo.vaultdentro deassets/secure/.
- Usuarios autenticados se almacenan como
assets/users/<email>.json. - El índice vive en
assets/users/index.json. - Esto facilita trazabilidad y búsqueda operativa por correo.
Esquema del archivo <email>.json (campos opcionales tienen default seguro si faltan):
{
"salt": "mep_auth_salt_v1_<16 hex>",
"passwordHash": "<sha256 hex>",
"mustChangePassword": false,
"resetToken": null,
"resetTokenExpiresAt": null,
"lastResetRequestAt": null
}mustChangePassword: cuando estrue, el próximo login obliga al usuario a cambiar su contraseña antes de continuar. Útil para resets manuales hechos por admin (el admin no necesita conocer la nueva contraseña: setea una temporal + este flag).resetToken,resetTokenExpiresAt: lo escribe el workflow de password-reset cuando el usuario solicita recuperación. Token aleatorio de 32 hex, expira en 1h.lastResetRequestAt: timestamp ISO del último request. Usado por el workflow como rate-limit (1 request/hora por email).
Scripts:
# Descifrar boveda EPE
node tools/read-encrypted-vault.mjs assets/secure/<archivo>.vault
# Cifrar en formato EPE (llave = nombre del archivo de salida)
node tools/write-encrypted-vault.mjs <input-plano> assets/secure/<archivo>.vaultNota: EPE es una capa de ofuscación fuerte para operación estática, no reemplaza un KMS/backend dedicado.
Las cuentas, dashboards y secrets de servicios de terceros usados por este proyecto están listados en docs/services.md.
- Sin backend no hay verificación server-side robusta de disponibilidad por URL.
- Dependencia de endpoints públicos para búsqueda, metadata y build de episodios: puede haber CORS/rate-limit intermitente.
- Calidad de metadata depende de lo que entreguen las fuentes.
Trabaja en main:
git checkout mainSe publica desde gh-pages.
Pasos mínimos:
git checkout main
# commit/push cambios
git checkout gh-pages
git checkout main -- index.html app.js styles.css
git add index.html app.js styles.css
git commit -m "Sync gh-pages"
git push
git checkout maingh api -X POST repos/lerna-admin/devil-tv/pages/buildsVer último estado:
gh api repos/lerna-admin/devil-tv/pages/builds/latest- Instalar
gitygh(opcional pero recomendado). - Clonar repo:
git clone https://github.com/lerna-admin/devil-tv.git
- Entrar al repo y verificar ramas:
git branch -a
- Trabajar en
main. - Sincronizar a
gh-pagespara publicar.
Si “Play” no arranca en mobile:
- Confirmar que la URL cambió a
#/watch/.... - Revisar que el ID sea válido (
tt...o tmdb numérico). - Probar hard refresh.
- Si persiste, revisar consola del navegador móvil remoto (o replicar en desktop responsive).
Si Pages queda “pegado”:
- Ver
pages/builds/latest. - Forzar build.
- Confirmar que fuente siga en
gh-pages(publicación estática).