HopHop

VarBook — Mi biblioteca EPUB self-hosted con sincronización multi-dispositivo

epub ebook laravel koreader open-source self-hosting desarrollo pwa

Llevo años leyendo varias horas al mes. Novelas, ensayos, un poco de todo. He probado bastantes dispositivos a lo largo del tiempo — principalmente Kobos — y siempre me he topado con el mismo problema: sincronizar mis libros y sobre todo mis posiciones de lectura entre mi e-reader, mi portátil con Linux y mi teléfono. La app de Kobo no está mal en la propia liseuse, pero en el móvil va bastante regular y no facilita nada meter EPUBs que no vengan de su tienda. Vamos, que no funcionaba ni de coña.

Al final me remangué y me puse a desarrollar mi propia solución. Como decimos por ahí: es machacando en el yunque como se ve al albañil — o algo así. Os presento VarBook, una aplicación web self-hosted para gestionar tu biblioteca EPUB con sincronización de la progresión entre todos tus dispositivos.

En resumen

  • Aplicación web Laravel 12 + React con lector EPUB integrado
  • PWA instalable con modo offline (lectura sin conexión)
  • Catálogo OPDS compatible con todas las apps de lectura del mercado
  • Plugin KOReader para sincronizar posiciones desde un e-reader Kobo
  • Plugin Calibre para envío de libros por lotes
  • Estadísticas de lectura (tiempo, sesiones, progresión)
  • Multi-usuario, trilingüe (FR/EN/ES), open source

El problema: la torre de Babel de los e-readers

Cuando lees en varios dispositivos, rápidamente te encuentras con un quebradero de cabeza. Leo un capítulo por la noche en mi Kobo en la cama, y luego quiero seguir al día siguiente en el tren con el móvil. Resultado: tengo que buscar manualmente dónde me quedé. Darle 70 veces a "página siguiente" para retomar la sesión de la noche anterior es el tipo de cosa que te hace cerrar el libro antes de lo previsto.

El ecosistema Kobo es bastante cerrado. La aplicación móvil funciona, pero está claramente orientada a su tienda. Cargar tus propios EPUBs es toda una odisea. ¿Y la sincronización de posiciones entre la app móvil y el e-reader físico? Digamos que es... teórica.

Necesitaba algo sencillo: subir mis EPUBs a un sitio, leerlos en cualquier dispositivo, y encontrar mi posición en todas partes. Ni más ni menos.

VarBook: la aplicación web

Así que construí VarBook durante estos últimos meses. Es una aplicación Laravel 12 completa con un frontend React 18 en TypeScript. El lector EPUB integrado está basado en epub.js, una librería JavaScript que gestiona el renderizado de EPUBs directamente en el navegador.

La interfaz es limpia y sencilla. Para añadir libros, basta con arrastrar y soltar. La aplicación extrae automáticamente los metadatos (título, autor, portada) del EPUB. Se puede personalizar el lector: tema claro/oscuro/sepia, tamaño de fuente, tipografía, interlineado — cada dispositivo mantiene sus propias preferencias de lectura.

Para los que gestionan grandes bibliotecas con Calibre, también he desarrollado un plugin que permite enviar EPUBs por lotes directamente a VarBook desde la interfaz de Calibre. Se acabó exportar y luego subir uno a uno.

Los libros subidos están disponibles de tres formas:

  • Directamente en el lector web / la PWA
  • A través de un catálogo OPDS compatible con todas las aplicaciones de lectura del mercado (KOReader, Moon+ Reader, Aldiko, etc.)
  • Como descarga directa

La aplicación también gestiona estadísticas de lectura: tiempo dedicado por libro, historial de sesiones, progresión global. Es el tipo de datos que no sabías que querías hasta que los tienes.

Modo offline: leer en el avión

Uno de los aspectos que más me importaba es el modo offline. VarBook es una PWA (Progressive Web App) instalable en el móvil — probada en Android por ahora. En la práctica, se puede descargar un libro en la caché local del navegador vía IndexedDB (usando Dexie como wrapper) y leerlo sin conexión a internet.

Las posiciones de lectura se cachean localmente durante la lectura offline. En cuanto vuelves a estar online, todo se sincroniza automáticamente con el servidor gracias a los Service Workers y la API Background Sync.

Pude probar esta funcionalidad durante mi último viaje en avión y funcionó de maravilla. Aunque tengo que reconocer que mis últimos cambios puede que lo hayan roto desde entonces — sindjeu (palabrota belga), es lo que tiene el desarrollo en solitario. Sigue siendo un trabajo en curso.

El redescubrimiento de KOReader

Para la parte del e-reader Kobo, redescubrí KOReader. Lo había instalado hace un tiempo pero lo descarté rápidamente porque su interfaz me pareció rudimentaria. ¡Menudo error!

KOReader es un visor de documentos open source diseñado para pantallas E Ink. Funciona en Kobo, Kindle, PocketBook, Android e incluso Linux. Con unos minutos de configuración, el lector se vuelve muy agradable. La barra de estado es una pasada: páginas restantes en el capítulo, tiempo estimado de lectura, porcentaje de progresión, batería — un montón de información útil que no siempre encuentras en los lectores comerciales.

Y hablando de batería, aquí es donde KOReader tiene una auténtica killer feature: el WiFi está desactivado por defecto y solo se activa cuando hace falta. Con el firmware original de Kobo, el WiFi se queda encendido en segundo plano, chupando batería constantemente. KOReader corta el WiFi en cuanto deja de ser necesario. Resultado: pasas de unos pocos días de autonomía a varias semanas sin despeinarte. Cuando lees varias horas al día, eso cambia las reglas del juego.

Pero sobre todo, KOReader tiene un sistema de plugins muy completo. Y ahí es donde se pone interesante para VarBook.

El plugin KOReader VarBook

He desarrollado un plugin para KOReader (todavía en pruebas pero ya muy funcional) que sincroniza las posiciones de lectura y la progresión con el servidor VarBook. Una herramienta "VarBook" aparece en el menú de herramientas con un botón "Sync to VarBook". He vinculado esta herramienta a un gesto táctil: un toque en la esquina superior derecha de la pantalla.

Mi flujo de lectura diario es algo así:

  1. Abro KOReader en mi Kobo. El catálogo OPDS de VarBook está configurado, así que tengo acceso a toda mi biblioteca directamente desde el e-reader.
  2. Abro mi libro.
  3. Si he leído en otro dispositivo entre medias, toco la esquina superior derecha de la pantalla. El plugin activa el WiFi automáticamente, se conecta a un punto de acceso disponible y obtiene la progresión del servidor. Si la posición remota es más reciente, navego automáticamente a esa posición. Una vez terminada la sincronización, el WiFi se apaga solo — nada de malgastar batería.
  4. Leo tranquilamente. Con cada cambio de página, la posición y un timestamp se cachean localmente. Todo esto pasa offline, sin necesidad de conexión.
  5. Cuando termino mi sesión, toco otra vez arriba a la derecha. El WiFi se enciende el tiempo justo para sincronizar y se apaga. Mis estadísticas de lectura (tiempo, posición, progresión) se actualizan en el servidor.

Es fluido, rápido, y sobre todo funciona sin tener que pensar en ello. El WiFi solo se enciende durante los pocos segundos necesarios para la sincronización, lo que preserva la autonomía del e-reader. Es un detalle, pero es exactamente el tipo de detalle que hace que dejes de usar el firmware original para siempre.

El rompecabezas de la sincronización epub.js / KOReader

Puede parecer poca cosa, pero la sincronización de posiciones entre el lector web (epub.js) y KOReader en el e-reader me dio bastantes quebraderos de cabeza. El problema fundamental es que estas dos aplicaciones gestionan el posicionamiento de formas completamente distintas.

epub.js utiliza el sistema CFI (Canonical Fragment Identifier) definido por la especificación EPUB. Un CFI tiene un aspecto como /6/4[chap01]!/4/2/1:0 — es una ruta tipo XPath dentro del documento HTML del capítulo, que apunta a un nodo específico del DOM y un offset de carácter. Este sistema es muy preciso en el contexto de un navegador web, pero está fuertemente acoplado al renderizado HTML.

KOReader, por su lado, trabaja con un sistema basado en páginas. Utiliza su propio motor de renderizado (CREngine) que pagina el documento de forma distinta según el tamaño de pantalla, la fuente, el tamaño del texto, etc. La posición se almacena como un porcentaje de progresión y un "xpointer" interno del motor — un formato completamente incompatible con los CFI de epub.js.

En la práctica: la misma posición en un libro genera dos representaciones totalmente diferentes según si lo lees en un navegador o en el e-reader. Y no existe una tabla de conversión universal entre los dos.

Después de bastante pelea y experimentación, acabé optando por una sincronización basada en dos datos comunes a ambos sistemas: el capítulo actual (identificado por su ruta en el archivo EPUB) y el porcentaje de progresión dentro de ese capítulo. El servidor almacena estos dos datos de forma agnóstica, y cada cliente los traduce a su propio sistema de posicionamiento.

No es perfecto — a veces la posición no es precisa a nivel de página exacta. Pero es perfectamente aceptable y sobre todo infinitamente mejor que tener que buscar manualmente tu página cada vez que cambias de dispositivo.

Open source e instancia pública

El proyecto está disponible en GitHub: github.com/ndieschburg/varbook. Está traducido al francés, inglés y español. La instalación en vuestro propio servidor está documentada en el README.

También alojo mi propia instancia abierta al registro sin ninguna restricción. Si queréis probarlo sin instalar nada, no dudéis en crearos una cuenta en varbook.hophop.be.

El stack técnico para los curiosos: Laravel 12, React 18, TypeScript, Tailwind CSS, Vite, MySQL, todo desplegado automáticamente vía CI/CD.

El invento se desarrolló en una buena decena de noches con la ayuda de mi amigo Claude, pero lo uso a diario desde hace tres meses y estoy bastante contento con él. Le voy metiendo mejoras regularmente según leo y me voy encontrando con pequeñas frustraciones por el camino. Si tenéis sugerencias, ideas o bugs que reportar, soy todo oídos — las issues de GitHub están abiertas.


Este artículo fue escrito originalmente en francés. Esta traducción fue generada automáticamente con ayuda de IA.

Interactions du Fediverse

2 1

Entrada Anterior