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>
3.2 KiB
3.2 KiB
1. Migration DB
- 1.1 Créer
infra/migrations/0013_add_book_id_to_index_jobs.sql: ajouter colonnebook_id UUID NULL REFERENCES books(id) ON DELETE SET NULLàindex_jobs - 1.2 Mettre à jour
sqlx-data.json/ préparer les queries sqlx si nécessaire
2. Parsers — Fonction de conversion
- 2.1 Ajouter
pub fn convert_cbr_to_cbz(cbr_path: &Path) -> Result<PathBuf>danscrates/parsers/src/lib.rs- Extraire le CBR vers
tmp_diravecunar - Lister et trier les images extraites
- Créer
{stem}.cbz.tmpdans le dossier parent du CBR avec lazipcrate - Vérifier que le CBZ contient le même nombre d'images que le CBR
- Renommer
.cbz.tmp→.cbz - Nettoyer
tmp_dir - Retourner le chemin du CBZ créé
- Extraire le CBR vers
- 2.2 Ajouter la gestion d'erreur : collision de fichier existant, échec unar, échec zip, décompte incorrect
3. API — Endpoint de conversion
- 3.1 Dans
apps/api/src/books.rs, ajouter le handlerPOST /books/:id/convert- Vérifier que le livre existe
- Vérifier que
file_format == 'cbr' - Résoudre le chemin physique avec
LIBRARIES_ROOT_PATH - Vérifier qu'aucun fichier
{stem}.cbzn'existe déjà - Insérer un job
cbr_to_cbzavecbook_iden DB - Retourner le job créé
- 3.2 Enregistrer la route dans
apps/api/src/main.rs - 3.3 Ajouter les annotations
#[utoipa::path]pour OpenAPI - 3.4 Mettre à jour
IndexJobResponse/map_rowdansindex_jobs.rspour inclurebook_id(le champ est nullable)
4. Indexer — Worker de conversion
- 4.1 Créer
apps/indexer/src/converter.rsavec la fonctionconvert_book(job_id, book_id, pool, config)- Lire le livre en DB (file_path, file_format)
- Résoudre le chemin physique
- Appeler
parsers::convert_cbr_to_cbz() - Mettre à jour
books.file_path,books.file_format,books.updated_aten DB - Supprimer le CBR (log warning si la suppression échoue, ne pas faire échouer le job)
- Mettre à jour le statut du job (
successoufailed)
- 4.2 Dans
apps/indexer/src/worker.rs, ajouter le dispatch pour le typecbr_to_cbz - 4.3 Ajouter
converterdansapps/indexer/src/lib.rs
5. Backoffice — Bouton de conversion
- 5.1 Créer
apps/backoffice/app/components/ConvertButton.tsx(composant client)- Appelle
POST /books/:id/convertviaapiFetch - Affiche un état de chargement pendant l'appel
- En succès : affiche un message avec lien vers le job créé
- En erreur : affiche le message d'erreur de l'API
- Appelle
- 5.2 Intégrer
ConvertButtondansapps/backoffice/app/books/[id]/page.tsx- Visible uniquement si
book.file_format === 'cbr' - Placer dans la section des métadonnées ou comme action en haut de page
- Visible uniquement si
- 5.3 Mettre à jour
JobRow.tsxpour afficher le typecbr_to_cbzcorrectement (label lisible)
6. Vérification
- 6.1
cargo buildpasse sans erreurs ni warnings clippy - 6.2 Tester manuellement la conversion d'un livre CBR depuis la page détail
- 6.3 Vérifier que le CBR est supprimé et que
file_formaten DB est bien'cbz' - 6.4 Vérifier le cas d'erreur : tenter de convertir un livre CBZ → 409
- 6.5 Vérifier le cas d'erreur : CBZ déjà présent → 409