- Indexer: ajout du champ `warnings` dans JobStats pour les erreurs
non-fatales (fichiers inaccessibles, permissions)
- Indexer: skip les fichiers dont le stat échoue au lieu de faire
crasher tout le scan de la library
- Backoffice: affichage des warnings dans le détail job (summary,
timeline, Index Statistics) et dans la popin jobs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parsers: raw ZIP reader (flate2) contournant la validation CRC32 des
Unicode extra fields (0x7075) qui bloquait certains CBZ
- Parsers: nouvelle API publique extract_page() pour extraire une page
par index depuis CBZ/CBR/PDF avec fallbacks automatiques
- API: suppression du code d'extraction dupliqué, délégation à parsers::extract_page()
- API: retrait des dépendances directes zip/unrar/pdfium-render/natord
- Indexer: nettoyage Meili systématique à chaque sync (au lieu de ~10%)
avec pagination pour supporter les grosses collections — corrige les
doublons dans la recherche
- Indexer: retrait de la dépendance rand (plus utilisée)
- Backoffice: popin jobs rendue via createPortal avec positionnement
dynamique — corrige le débordement desktop et le header cassé en mobile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Migration 0020 : colonne format sur books, backfill depuis book_files
- batch.rs / scanner.rs : l'indexer écrit le format dans books
- books.rs : format dans BookItem + filtre ?format= dans list_books
- perf_pages.sh : benchmarks par format CBZ/CBR/PDF
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Une seule entrée illisible dans le central directory ne doit pas bloquer
l'analyse de tout le livre. Le count et la première page lisible sont
retournés même si certaines entrées sont endommagées.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- zip 8.x résout nativement les extra fields NTFS (source du bug EOCD)
- notify 8.x améliore le support inotify Linux
- lopdf 0.39 contient des correctifs de parsing PDF
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Les ZIP créés par des outils Windows (version 6.3) contiennent des extra
fields NTFS (tag 0x000A) qui font échouer ZipArchive::new() avec "Could
not find EOCD". Ajout d'un fallback via read_zipfile_from_stream qui lit
les local file headers sans dépendre du central directory.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Même correctif que dans le parsers/indexer : un .cbr qui est en réalité
un ZIP (et vice-versa) retourne maintenant la bonne page au lieu d'un 500.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Un fichier corrompu (RAR/ZIP/PDF qui ne répond plus) occupait un slot
de concurrence indéfiniment, bloquant le pipeline à ex. 1517/1521.
- Ajoute tokio::time::timeout autour de spawn_blocking(analyze_book)
- Timeout lu depuis limits.timeout_seconds en DB (défaut 120s)
- Le livre est marqué parse_status='error' en cas de timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Incremental rebuild: remplace le WalkDir de comptage par un COUNT(*) SQL
→ incrémental 67s → 25s (-62%) sur disque externe
- Full rebuild: conserve le WalkDir (DB vidée avant le comptage)
- Concurrence par défaut: num_cpus/2 clampé [2,8] au lieu de 2 fixe
- Ajoute num_cpus comme dépendance workspace
- Backoffice jobs: un seul formulaire avec formAction par bouton (icônes rétablies)
- infra/perf.sh: corrige l'endpoint /index/jobs/:id (pas /details), exporte BASE_API/TOKEN
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cancel_job: ajouter 'extracting_pages' aux statuts annulables
- cleanup_stale_jobs: couvrir 'extracting_pages' et 'generating_thumbnails' au redémarrage
- analyzer: ne pas régénérer le thumbnail si déjà existant (skip sub-phase B)
- analyzer: supprimer les dotfiles macOS (._*) encore en DB
- SSE backoffice: réduire le spam de logs en cas d'API injoignable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added a new job status 'extracting_pages' to represent the first sub-phase of thumbnail generation.
- Updated the database schema to include a timestamp for when thumbnail generation starts.
- Enhanced job progress components to handle the new status, including UI updates for displaying progress and status labels.
- Refactored job-related logic to accommodate the two-phase process: extracting pages and generating thumbnails.
- Adjusted SQL queries and job detail responses to include the new fields and statuses.
This change improves the clarity of job processing states and enhances user feedback during the thumbnail generation process.
CBR: extract_cbr_page extrayait TOUT le CBR sur disque pour lire une
seule page. Reécrit avec le crate unrar : listing en mémoire + extraction
ciblée de la page demandée uniquement. Zéro subprocess, zéro temp dir.
PDF: render_pdf_page utilisait pdftoppm subprocess + temp dir. Reécrit
avec pdfium-render in-process. Zéro subprocess, zéro temp dir.
CBZ: sort naturel (natord) pour l'ordre des pages.
Dockerfile API: retire unar et poppler-utils, ajoute libpdfium.so.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CBR: remplace unrar/unar CLI par le crate `unrar` (bindings libunrar
vendorisé, zéro dépendance système). Supprime XADRegexException, les
forks de processus et les dossiers temporaires.
PDF: remplace pdfinfo + pdftoppm par pdfium-render. Le PDF est ouvert
une seule fois pour obtenir le nombre de pages ET rasteriser la première
page. lopdf reste pour parse_metadata (page count seul).
convert_cbr_to_cbz: reécrit sans subprocess ni dossier temporaire —
les images sont lues en mémoire via unrar puis packées directement en ZIP.
Dockerfile indexer: retire unrar-free, unar, poppler-utils. Télécharge
libpdfium.so depuis bblanchon/pdfium-binaries au build.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CBR: contourner le bug XADRegexException de unar en appelant unar
avec un symlink à nom neutre (archive.cbr) au lieu du chemin réel,
qui peut contenir des caractères regex spéciaux comme [ ] ( )
- CBR/CBZ: remplacer le tri lexicographique par natord (tri naturel)
pour que page2.jpg soit trié avant page10.jpg
- PDF: brancher pdftoppm -scale-to sur config.width.max(config.height)
au lieu d'une valeur hardcodée (800px → 400px par défaut)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
La contrainte index_jobs_status_check ne listait pas 'cancelled', ce qui
causait une erreur 500 à chaque tentative d'annulation de job.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
L'analyzer ne vérifiait jamais le statut cancelled en DB, ce qui faisait
continuer le traitement des thumbnails jusqu'au bout, puis écraser le
statut 'cancelled' avec 'success'. Ajout d'un poller background toutes
les 2s avec AtomicBool partagé pour stopper proprement le stream concurrent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Déplace les migrations du service `migrate` séparé vers un entrypoint.sh
- L'API exécute `sqlx migrate run` au démarrage avant de lancer le binaire
- Gestion de la rétrocompatibilité : détecte un schéma pre-sqlx et crée
une baseline `_sqlx_migrations` pour éviter les conflits sur les instances existantes
- Installe sqlx-cli dans le builder, copie le binaire et les migrations dans l'image finale
- Supprime le service `migrate` du docker-compose.yml ; l'indexer dépend maintenant
du healthcheck de l'API (qui garantit que les migrations sont appliquées)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- meili.rs: corrige la désérialisation de la réponse paginée de
Meilisearch (attendait Vec<Value>, l'API retourne {results:[...]}) —
la suppression des documents obsolètes ne s'exécutait jamais, laissant
d'anciens UUIDs qui généraient des 404 sur les thumbnails
- books.rs: fallback sur render_book_page_1 si le fichier thumbnail
n'est plus accessible sur le disque (au lieu de 500)
- pages.rs: retourne 404 au lieu de 500 quand le fichier CBZ est absent
- search.rs + api.ts + BookCard: ajout série hits, statut lecture,
pagination OFFSET, filtre reading_status, et placeholder onError
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
API:
- Remplace cursor par page (1-indexé) + OFFSET sur GET /books et GET /libraries/:id/series
- BooksPage et SeriesPage retournent total, page, limit
- GET /libraries/:id/series supporte ?q pour filtrer par nom (ILIKE)
Backoffice:
- Remplace CursorPagination par OffsetPagination sur les 3 pages de liste
- Adapte fetchBooks et fetchSeries (cursor → page)
- Met à jour les types BooksPageDto, SeriesPageDto, SeriesDto, BookDto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fix(auth): parse_prefix supporte les préfixes de token contenant '_'
- feat: GET /books expose reading_status, reading_current_page, reading_last_read_at
- feat: GET /books accepte ?reading_status=unread,reading (CSV multi-valeur)
- feat: SeriesItem expose books_read_count pour dériver le statut de lecture
- feat: GET /libraries/:id/series accepte ?reading_status=unread,reading
- feat: BooksPage et SeriesPage exposent total (count matchant les filtres)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- API : nouvelle table book_reading_progress (migration 0016) et module
reading_progress.rs avec GET/PATCH /books/:id/progress (token read)
- API : GET /books/:id enrichi avec reading_status, reading_current_page,
reading_last_read_at via LEFT JOIN
- Backoffice : badge de statut (Non lu / En cours · p.N / Lu) sur la page
de détail et overlay sur les BookCards
- OpenSpec : change reading-progress avec proposal/design/specs/tasks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajoute migration 0015 : colonne phase2_started_at sur index_jobs
- Indexer : renseigne phase2_started_at lors du passage à generating_thumbnails
- API : expose phase2_started_at et book_id dans IndexJobDetailResponse
- Page détail : timeline avec durée de chaque phase (Discovery / Thumbnails)
- Page détail : banners contextuels (success/failed/cancelled) avec résumé en une ligne
- Page détail : description textuelle du type de job, durée dans l'overview
- Page détail : stats normalisées selon le type (index vs thumbnail-only)
- JobRow : affiche le type via JobTypeBadge (cohérence visuelle)
- Badge : labels lisibles pour tous les types de jobs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cleanup_orphaned_thumbnails chargeait uniquement les book IDs de la library
en cours de rebuild, considérant les thumbnails des autres libraries comme
orphelins et les supprimant. La fonction charge désormais tous les book IDs
toutes libraries confondues.
Ajout d'un test de régression dans infra/smoke.sh qui vérifie que le
full_rebuild d'une library ne réduit pas le nombre de thumbnails des autres.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajout de DynamicSettings dans AppState (Arc<RwLock>) chargé depuis la DB
- rate_limit_per_second, timeout_seconds : plus hardcodés, lus depuis settings
- image_processing (format, quality, filter, max_width) : appliqués comme
valeurs par défaut sur les requêtes de pages (overridables via query params)
- cache.directory : lu depuis settings au lieu de la variable d'env
- update_setting recharge immédiatement le DynamicSettings en mémoire
pour les clés limits, image_processing et cache (sans redémarrage)
- parse_filter() : mapping lanczos3/triangle/nearest → FilterType
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute la possibilité de convertir un livre CBR en CBZ depuis le backoffice.
La conversion est sécurisée : le CBR original n'est supprimé qu'après vérification
du CBZ généré et mise à jour de la base de données.
- parsers: nouvelle fn `convert_cbr_to_cbz` (unar extract → zip pack → vérification → rename atomique)
- api: `POST /books/:id/convert` crée un job `cbr_to_cbz` (vérifie format CBR, détecte collision)
- indexer: nouveau `converter.rs` dispatché depuis `job.rs`
- backoffice: bouton "Convert to CBZ" sur la page détail (visible si CBR), label dans JobRow
- migrations: colonne `book_id` sur `index_jobs` + type `cbr_to_cbz` dans le check constraint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajout d'un menu hamburger mobile (MobileNav) avec drawer animé via React Portal (évite le piège du backdrop-filter du header)
- Popin JobsIndicator adaptée mobile : positionnement fixed plein-écran sur petit écran, backdrop semi-transparent
- Navigation tablette (md→lg) : icônes seules avec tooltip natif, labels visibles uniquement sur lg+
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add missing utoipa annotations:
- GET /books/{id}/thumbnail
- GET/POST /settings, /settings/{key}
- POST /settings/cache/clear
- GET /settings/cache/stats, /settings/thumbnail/stats
Add 'settings' tag and register all new schemas.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows 5 pages at a time in a full-width grid with prev/next navigation.
Pages are fetched via the existing proxy route with webp format.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 (discovery): walkdir + filename-only metadata, zero archive I/O.
Books are visible immediately in the UI while Phase 2 runs in background.
Phase 2 (analysis): open each archive once via analyze_book() to extract
page_count and first page bytes, then generate WebP thumbnail directly in
the indexer — removing the HTTP roundtrip to the API checkup endpoint.
- Add parse_metadata_fast() (infallible, no archive I/O)
- Add analyze_book() returning (page_count, first_page_bytes) in one pass
- Add looks_like_image() magic bytes check for unrar p stdout validation
- Add lsar fallback in list_cbr_images() for UTF-16BE encoded filenames
- Add directory_mtimes table to skip unchanged dirs on incremental scans
- Add analyzer.rs: generate_thumbnail, analyze_library_books, regenerate_thumbnails
- Remove run_checkup() from API; indexer handles thumbnail jobs directly
- Remove api_base_url/api_bootstrap_token from IndexerConfig and AppState
- Add unar + poppler-utils to indexer Dockerfile
- Fix smoke.sh: wait for job completion, check thumbnail_url field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Refactor multiple API routes to utilize a centralized configuration function for base URL and token management, improving code consistency and maintainability.
- Replace direct environment variable access with a unified config function in the `lib/api.ts` file.
- Remove redundant error handling and streamline response handling in various API endpoints.
- Delete unused job-related API routes and settings, simplifying the overall API structure.
- Add CLAUDE.md at root and AGENTS.md in apps/api, apps/indexer,
apps/backoffice, crates/parsers with module-specific guidelines
- Unify all service ports to 70XX (no more internal/external split):
API 7080, Indexer 7081, Backoffice 7082
- Update docker-compose.yml, Dockerfiles, config.rs defaults,
.env.example, backoffice routes, bench.sh, smoke.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change all instances of AppState to reference the new state module across multiple files for consistency.
- Clean up imports in auth, books, index_jobs, libraries, pages, search, settings, thumbnails, and tokens modules.
- Simplify main.rs by removing unused code and organizing middleware and route handlers under the new handlers module.
- Extend thumbnail regeneration logic to support full rebuilds, allowing for the deletion of orphaned thumbnails.
- Implement database updates to clear thumbnail paths for books during regeneration and full rebuild processes.
- Improve logging to provide detailed insights on the number of deleted thumbnails and cleared database entries.
- Refactor code for better organization and clarity in handling thumbnail files.
- Introduce a structured approach to collect book file information before parsing.
- Implement parallel processing for metadata extraction to improve performance.
- Refactor file handling to utilize a new FileInfo struct for better organization.
- Update database interactions to use collected file information for batch inserts.
- Improve logging for scanning and parsing processes to provide better insights.
- Introduce dynamic loading of concurrent render limits from the database for both page rendering and thumbnail generation.
- Update API to utilize the loaded concurrency settings, defaulting to 8 for page renders and 4 for thumbnails.
- Modify front-end settings page to reflect changes in concurrency limits and provide user guidance on their impact.
- Ensure that changes to limits require a server restart to take effect, with clear messaging in the UI.
- Remove unused image dependencies from Cargo.lock.
- Update API to handle thumbnail generation and checkup processes.
- Introduce new routes for rebuilding and regenerating thumbnails.
- Enhance job tracking with progress indicators for thumbnail jobs.
- Update front-end components to display thumbnail job status and progress.
- Add backend logic for managing thumbnail jobs and integrating with the API.
- Refactor existing code to accommodate new thumbnail functionalities.
- Try multiple entries in CBR archive until finding valid image
- Increase timeout from 12s to 30s for large files
- Better error messages for debugging
- Add magic bytes validation for extracted CBR images
- Add hex dump for debugging invalid images
- Show first entries when listing CBR archive
- Remove unused structs and functions from settings.rs
- Add -y flag to unrar for auto-confirm
- Add isActive checks before writing to SSE controller
- Wrap controller operations in try/catch to prevent 'already closed' errors
- Fix race condition when client disconnects during SSE streaming
- Batching BDD: group INSERT/UPDATE operations in batches of 100 using UNNEST
- Incremental MeiliSearch: only sync books modified since last sync
- Optimized fingerprint: use only size+mtime+filename (100x faster)
- Increased DB connections from 5 to 20
- Reduced progress update frequency (every 1s or 10 files)
- Add sync_metadata table to track last MeiliSearch sync