Retour projects
Feb 17, 2026
6 min de lecture

ReadMind

Tracker de lecture manga avec extension navigateur et PWA mobile

ReadMind est une application fullstack qui permet de tracker automatiquement sa progression de lecture de mangas, que ce soit sur desktop ou mobile.

Le concept

L’idee est simple : ne plus jamais oublier ou on en est dans un manga. ReadMind detecte automatiquement le chapitre en cours, sauvegarde la progression de scroll, et synchronise tout entre les appareils. L’utilisateur n’a rien a faire manuellement, il lit et ReadMind s’occupe du reste.

Stack technique

Le projet repose entierement sur Nuxt 3 qui gere a la fois le frontend (Vue 3, Tailwind CSS) et le backend (API server Nitro). La base de donnees MySQL est geree via Prisma ORM. Ce choix d’architecture monolithique simplifie le deploiement et evite la complexite d’un backend separe.

Extension Chrome

L’extension est le coeur du tracking desktop. Elle utilise un systeme de regex patterns pour extraire le slug du manga et le numero de chapitre depuis l’URL de lecture, quel que soit le site supporte. Elle track egalement la position de scroll et la restaure au retour sur la page. C’est ma premiere extension publiee sur le Chrome Web Store.

Le probleme du mobile

Sur desktop, l’extension gere tout le tracking. Mais sur mobile, aucun navigateur ne supporte les extensions. C’est le probleme central qui a guide toute l’architecture mobile du projet.

Premiere approche : React Native

Ma premiere idee a ete de developper une application mobile native en React Native avec un WebView integre. Le WebView aurait permis d’injecter du JavaScript dans les pages de lecture, exactement comme l’extension. Mais cette approche posait plusieurs problemes : la complexite de maintenir une app native separee, la publication sur les stores, et surtout le fait de dupliquer toute la logique de tracking dans un environnement different.

Deuxieme approche : PWA avec proxy iframe

J’ai pivote vers une Progressive Web App installable depuis le navigateur. L’avantage : un seul code a maintenir, pas de store, une experience quasi-native (mode standalone, icone sur l’ecran d’accueil).

Pour le lecteur mobile, l’idee etait de charger les pages de lecture dans un iframe via un proxy serveur. Le proxy fetche la page HTML, retire les headers bloquants (X-Frame-Options), reecrit les URLs relatives en absolu, et injecte un script de tracking qui communique avec la page parente via postMessage.

Premier probleme : Cloudflare. La plupart des sites de lecture utilisent Cloudflare comme protection anti-bot. Un simple fetch() depuis Node.js se fait immediatement bloquer (403) car l’empreinte TLS de Node.js ne correspond pas a celle d’un vrai navigateur. J’ai d’abord essaye d’ameliorer les headers HTTP pour mieux imiter Chrome, mais ca ne suffisait pas — Cloudflare detecte le fingerprint TLS, pas juste les headers. J’ai alors integre got-scraping qui imite les fingerprints TLS de Chrome, puis Humanoid.js qui est capable de resoudre les challenges JavaScript de Cloudflare directement en Node.js, sans headless browser.

Deuxieme probleme : les publicites. Les pages chargees via le proxy contenaient toutes les publicites du site original. J’ai d’abord ecrit un filtrage manuel par regex (liste de domaines publicitaires connus), mais c’etait incomplet et fragile. J’ai alors integre @ghostery/adblocker, le moteur de l’extension Ghostery, qui utilise les listes EasyList avec plus de 80 000 regles. Le moteur tourne cote serveur : il supprime les scripts et iframes publicitaires du HTML, et injecte des regles CSS cosmétiques pour masquer les elements restants. Cote client, un MutationObserver surveille le DOM pour supprimer les pubs injectees dynamiquement apres le chargement.

Troisieme probleme : le blocage des images. Le filtre etait trop agressif et supprimait aussi les images du manga. J’ai du affiner les regles pour ne bloquer que les scripts et iframes, jamais les <img>, et remplacer les selecteurs CSS trop larges (comme [class*="ad-"]) par des selecteurs cibles (div.ad-container).

Troisieme approche : le scraper

Malgre toutes ces ameliorations, l’approche proxy+iframe restait fragile. Les pubs revenaient, le rendu etait parfois casse, et chaque site posait des problemes differents. J’ai alors realise qu’il valait mieux ne pas charger la page entiere.

La solution finale est un scraper : le serveur fetch la page avec Humanoid.js, la parse avec Cheerio, et extrait uniquement les URLs des images du chapitre. Chaque site a son propre selecteur CSS adapte a son theme (WordPress/Madara, etc.). Les images sont ensuite servies via un proxy d’images (pour contourner le hotlinking et le CORS), et affichees dans un reader natif en scroll vertical.

Le resultat : zero pub, zero script externe, zero iframe, juste les images du manga dans un reader propre avec tracking du chapitre et du scroll. Si le scraping echoue pour un site, un fallback propose d’ouvrir le lien directement dans le navigateur.

Dashboard

Le dashboard offre une vue complete de la bibliotheque avec statistiques, filtres par statut, recherche, et deux modes d’affichage (grille/liste). Les covers sont recuperees automatiquement via l’API MangaDex et mises en cache localement. Un systeme de lazy loading via IntersectionObserver optimise le chargement — les images ne sont fetchees que lorsque la carte entre dans le viewport.

Integration MangaDex

Un module de planning permet de chercher des mangas sur MangaDex, les ajouter en wishlist, et recevoir des recommandations personnalisees basees sur sa bibliotheque existante.

Ce que j’ai appris

Ce projet m’a confronte a des problematiques rarement abordees dans un projet web classique : le reverse engineering de protections anti-bot, le fingerprinting TLS, le scraping resilient, le filtrage publicitaire cote serveur, et les contraintes specifiques des PWA (service worker, manifest, criteres d’installabilite). Chaque probleme resolu en a revele un nouveau, et c’est cette iteration constante qui a fait evoluer l’architecture du simple proxy iframe vers le scraper final.