- Historial pasa a Librería: pestañas Leyendo, Siguiendo, Terminados, En el dispositivo y listas propias (Favoritos, Pendientes, Abandonados por defecto) - Recomendaciones en el inicio según tus géneros más leídos (populares de MangaDex) - Descarga anticipada de los N capítulos siguientes (opcionalmente en HD) al pasar la mitad de uno - PWA: manifest, iconos y service worker; capítulos guardados en el dispositivo para leer sin conexión, progreso offline sincronizado después - Seguridad: baneo de IP tras 6 intentos fallidos (configurable), IP real detrás de proxies de confianza, CSP y cabeceras, cookie Secure/HSTS en HTTPS, login sin diferencias de tiempo, registro cerrado por defecto, contraseñas de 8+, protección SSRF en el proxy de imágenes, errores 500 genéricos - Ajustes: sección Seguridad con IPs bloqueadas, intentos recientes y la IP vista por el servidor - LeerCapitulo: reintentos ante caídas (522/5xx), copia anterior si sigue caído y mensaje claro Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.8 KiB
Manga Reader
Lector web de manga autoalojado, con las dos fuentes de
Tanko (src/servers/):
- LeerCapitulo (español, por defecto): mucho más catálogo. El scraper está reescrito para el sitio actual y ya no necesita navegador headless (las páginas vienen en el HTML).
- MangaDex (multi-idioma): español = es-la + es combinados. Muchos títulos populares no tienen capítulos por licencias; la ficha ofrece buscarlos en LeerCapitulo.
- Búsqueda, populares y actualizados recientemente.
- Lector por página (toques/flechas, izq→der o der→izq) o en vertical, modo ahorro de datos.
- Usuarios con contraseña; el historial y la página exacta se guardan en el servidor
(
data/manga.db), así que puedes seguir desde cualquier dispositivo/sesión. - Descargas: botón "⬇ Descargar" en la ficha. Baja el manga entero en segundo plano a
/zdata/media/mangas/<Título>/Capitulo NNNN.cbz(+cover.jpg,ComicInfo.xmlen cada CBZ), compatible con Kavita/Komga/Mihon. Se reanuda tras reinicios; repetirla baja solo los capítulos nuevos. Los capítulos descargados se leen desde disco y, sin conexión, la ficha usa los datos guardados. - Novedades: los mangas que empiezas a leer (o marcas con "Seguir") se revisan cada
CHECK_INTERVAL_HOURS(3 h). El inicio muestra "N caps. nuevos", la ficha marca NUEVO, los mangas con copia local descargan lo nuevo solos, y se puede avisar por ntfy o Telegram (Ajustes). - Lector: una página, doble página (tablet horizontal/PC; portada y páginas apaisadas van solas) o vertical. Modo, ajuste y dirección se recuerdan por manga (por defecto: manhwa/manhua en vertical, manga por página de derecha a izquierda). Zoom con pellizco, doble toque en el centro, Ctrl+rueda o + / − / 0.
- Reescalado HD: en la ficha de un manga descargado, "Reescalar a HD" pasa sus capítulos por
Real-ESRGAN (
realesr-animevideov3×2, en la GPU por Vulkan) y guarda la versión HD en<Título>/.hd/(carpeta oculta: el CBZ original no se toca y Kavita/Komga no ven duplicados). Con el manga marcado, los capítulos que se descarguen después también se reescalan. La lista de capítulos marca HD, el lector usa la versión HD (se puede desactivar en sus ajustes) y desde allí también se reescala un capítulo suelto. En una Intel HD 630 tarda ~13 s por página y cada página ocupa ~2,5× más (WebP calidad 85). - Portadas HD: las portadas de los mangas que sigues o tienes en el historial se reescalan solas en
segundo plano (×4 las pequeñas, ×2 el resto; ~4 s cada una) y desde entonces se sirven en HD. Las
de búsquedas y populares se quedan como están. Caché en
data/covers/(se puede borrar: se regeneran). - Recortes: en el lector, ✂ y arrastrar sobre un panel lo guarda en "Recortes" con su manga,
capítulo y página ("Ir a la página" lleva directo allí). Se recorta del original y solo el recorte se
reescala con Real-ESRGAN (×4, o ×2 si ya mide más de 1200 px; unos segundos). Se guardan en
data/clips/(<id>.pngoriginal y<id>-hd.webp), con nota opcional y descarga. - Colas (antes "Descargas"): descargas en curso y en espera, cola de reescalado HD por manga (capítulo y página en curso, siguientes, tiempo restante, errores con "Reintentar", "Detener"), recortes y portadas pendientes, y debajo la biblioteca descargada con su tamaño; todo en tarjetas verticales con portada, como el historial.
- Librería (antes "Historial"): pestañas Leyendo, Siguiendo, Terminados, En el dispositivo y listas propias (Favoritos, Pendientes, Abandonados y las que crees). Desde la ficha, el botón "Listas" añade o quita el manga de cada lista.
- Recomendado para ti (inicio): populares de MangaDex con capítulos en tu idioma dentro de tus 3 géneros más leídos, sin lo que ya tienes en la librería.
- Descarga anticipada (Ajustes): al pasar la mitad de un capítulo, el servidor baja los N siguientes (por defecto 2) a la copia local y, si se marca, los reescala a HD.
- App instalable y lectura sin conexión: manifest + service worker. En la ficha, "En este
dispositivo" guarda capítulos (páginas incluidas) en el navegador para leerlos sin internet; el
progreso leído sin red se envía al volver. Requiere HTTPS (o
localhost): porhttp://IP:puertoel navegador no permite service workers. Por ejemplotailscale serveo un proxy con certificado. - Estadísticas: rachas, capítulos y páginas por semana, calendario de actividad, géneros favoritos y lo más leído.
- Las imágenes pasan por el servidor (
/api/img), solo con URLs firmadas por él (clave endata/secret).
Uso
cp .env.example .env # opcional: ajusta rutas, puerto, usuario…
docker compose up -d --build
# http://<ip-del-servidor>:3352
El contenedor no depende del sistema operativo: imagen Node fijada (node:24.22.2-trixie-slim,
con Mesa Vulkan y Real-ESRGAN verificado por SHA-256), sin dependencias npm, y todo lo propio de la máquina está en .env (ver .env.example).
Sin .env arranca igual: puerto 3352 en todas las interfaces y descargas en ./mangas.
El primer usuario puede registrarse siempre; después el registro queda cerrado salvo
ALLOW_SIGNUP=true en .env.
Exponerlo a internet
- Pon HTTPS delante (Caddy, Nginx, Cloudflare Tunnel o
tailscale funnel): sin HTTPS la contraseña y la cookie viajan en claro. Con HTTPS la cookie llevaSecurey se envía HSTS. - Con un proxy inverso, define
REAL_IP_HEADERen.env(x-forwarded-foren Caddy/Nginx,cf-connecting-ipen Cloudflare) para que el baneo vea la IP real. Solo se cree esa cabecera si la conexión llega desde una IP privada (el proxy), así que desde internet no se puede falsificar. - Baneo de IP: 6 intentos fallidos (contraseña incorrecta, registro con el registro cerrado,
contraseña actual incorrecta) en 60 min bloquean la IP 24 h para toda la web. Ajustable con
BAN_MAX_FAILS,BAN_WINDOW_MIN,BAN_HOURS; red local, loopback y Tailscale nunca se bloquean. En Ajustes se ven las IPs bloqueadas y se pueden desbloquear. - Registro cerrado por defecto (
ALLOW_SIGNUP=false); contraseñas de 8+ caracteres; cambiar la contraseña cierra las demás sesiones. - Cabeceras: CSP estricta (sin scripts en línea),
X-Frame-Options: DENY,nosniff,Referrer-Policy. - El proxy de imágenes solo descarga URLs firmadas por el servidor y nunca hacia IPs privadas (comprobado también en cada redirección). Los errores internos no se muestran al cliente.
Qué guardar / mover a otro servidor
| Qué | Dónde | Contenido |
|---|---|---|
| Código | este repositorio | se recrea con git clone |
.env |
raíz del proyecto (no está en git) | rutas, puerto, PUID/PGID, URL pública |
data/ |
raíz del proyecto (no está en git) | manga.db (+ -wal/-shm): usuarios, historial, estadísticas, seguimiento, ajustes; secret: clave del proxy de imágenes; clips/: recortes; covers/: portadas HD (caché) |
| Mangas descargados | MANGA_DIR_HOST (aquí /zdata/media/mangas) |
CBZ + portadas |
Pasos en el sistema nuevo:
git clone <repo> manga-reader && cd manga-reader
# copiar .env y data/ desde el servidor anterior (con el contenedor parado), y la carpeta de mangas
id -u; id -g # si no son 1000, ponlos en PUID/PGID del .env
docker compose up -d --build
Para el reescalado HD el host necesita /dev/dri (GPU Intel/AMD con Mesa) y RENDER_GID en .env
con el grupo render. Sin GPU, quita devices/group_add del compose: todo lo demás funciona igual.
Si el usuario del host no coincide con PUID/PGID, el contenedor no arranca y el log dice
exactamente qué chown hacer. Si la carpeta de mangas no existe, Docker la crea como root:
créala antes (mkdir -p con tu usuario) para que las descargas funcionen.