MangaReader
Un lecteur de manga qui scrape des chapitres depuis des sites tiers, les fusionne en séries, et les sert via une interface web.
Ce que ça fait
MangaReader scrape des chapitres de manga depuis des sites de scanlation, les fusionne en séries, et les sert via une interface web locale. Tu peux marquer tes séries, suivre ta progression, et le serveur synchronise les nouveaux chapitres en arrière-plan.
Architecture
Trois crates Rust dans un workspace :
- mangaread-core — types du domaine, repository SQLite, logique de fusion des séries
- scraper — trait SiteAdapter avec implémentations par site (Demonicscans, Manhuaplus), binaire CLI + bibliothèque
- website — serveur web Axum avec 10 modules de routes, templates Maud, assets statiques
La base de données utilise SQLite en mode WAL — quatre tables (series, manga, chapters, pages), tous les inserts utilisent ON CONFLICT ... DO UPDATE, donc tout est sûr à relancer.
flowchart TB
CLI[ScraperEngine] --> DB[(SQLite)]
WEB[Axum Server] --> DB
WEB <--> BROWSER[Browser]
BROWSER --> JS[app.js + localStorage] État côté client sans comptes. Pas d'auth, pas de sessions. Les marque-pages et la progression vivent dans localStorage. Le JS lit localStorage et construit le DOM avec une petite fonction h(). C'est étonnamment propre — mais debugguer l'état sans base de données, c'est plus dur.
Captures d'écran
Les parties difficiles
Chromedriver. Le scraper utilise un vrai navigateur pour les sites manga chargés en JS. Faire marcher chromedriver dans Docker a pris 30% du temps de dev. Bugs de mode headless, problèmes de sandbox, processus zombies — le cirque habituel des webdrivers.
SQLite et le threading. La Connection de rusqlite n'est pas Sync, donc chaque thread de scraping ouvre sa propre connexion. Le AppState principal wrappe la sienne dans un Mutex. Ça semble simple, mais le borrow checker avait des opinions.
Se battre avec le borrow checker pour la 50ème fois à 2h du matin, c'est un rite de passage.
Ce que j'ai appris
C'était mon premier projet Rust avec plusieurs crates dans un workspace, et ça m'a plus appris que n'importe quel tutoriel. Le système de types de Rust est génial pour la modélisation du domaine — pas d'états invalides possibles — mais se battre avec le borrow checker pour la 50ème fois à 2h du matin, c'est un rite de passage.
Le pattern d'adaptateur — trait + implémentations par site — a payé immédiatement en ajoutant une deuxième source. Et la boucle de sync (thread background, intervalle de 6h, scraping parallèle) m'a appris les channels Tokio et l'arrêt gracieux à la dure.