diff --git a/CLAUDE.md b/CLAUDE.md index 6456239..12c8d33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,9 +114,7 @@ Stratégie : interroger plusieurs sources **libres et gratuites, sans clé API o **Ordre décidé : BnF d'abord, OpenLibrary ensuite.** La collection est majoritairement francophone, or OpenLibrary (Internet Archive) est très fourni sur l'édition anglophone mais **lacunaire sur le fonds français** — éditions françaises récentes et poches d'éditeurs modestes y manquent souvent, ou n'ont qu'un titre sans auteur. Une source unique lacunaire ruinerait l'intérêt du scan, qui est précisément d'éviter la saisie manuelle. -1. **BnF** — source principale, via son API **SRU**, gratuite et sans clé. Le dépôt légal français garantit structurellement la meilleure couverture possible sur le francophone. - - ⚠️ Renvoie du **XML MARC**, nettement plus rébarbatif à parser que du JSON. Prévoir le travail de mapping en conséquence. - - ⚠️ **API non encore testée dans ce projet** — à valider concrètement (format exact, disponibilité, tolérance au débit) avant de s'engager sur l'implémentation. +1. **BnF** — source principale, via son API **SRU**, gratuite et sans clé. Le dépôt légal français garantit structurellement la meilleure couverture possible sur le francophone. **API testée et validée le 2026-08-17** — voir la section « API BnF » ci-dessous pour les pièges, dont un bloquant. 2. **OpenLibrary** (`https://openlibrary.org/isbn/{isbn}.json`) — source de secours, pour les livres étrangers et tout ce que la BnF ne connaît pas - ⚠️ **Bug connu identifié lors des tests avec BookLogr** : l'endpoint `/isbn/{isbn}.json` renvoie souvent le titre mais l'**auteur est juste une référence** (`/authors/OL...A`), pas le nom directement. Il faut faire un **second appel** vers `/authors/{id}.json` pour récupérer le nom. Un projet qui oublie ce second appel se retrouve avec titre rempli mais auteur vide (symptôme exact observé et diagnostiqué chez BookLogr) — **ne pas reproduire ce bug**. - La couverture est un service séparé : `https://covers.openlibrary.org/b/isbn/{isbn}-L.jpg` (peut exister même si la fiche bibliographique est incomplète) @@ -126,6 +124,51 @@ Stratégie : interroger plusieurs sources **libres et gratuites, sans clé API o Quelle que soit la source, prévoir que le formulaire de saisie manuelle reste **toujours accessible** pour compléter ou corriger une fiche incomplète. +## API BnF — validée le 2026-08-17 + +Endpoint SRU, gratuit, **sans clé API**, réponse en ~240 ms : + +``` +https://catalogue.bnf.fr/api/SRU + ?version=1.2 + &operation=searchRetrieve + &query=bib.isbn all "{isbn}" + &recordSchema=dublincore + &maximumRecords=5 +``` + +**Utiliser `recordSchema=dublincore`, pas MARC.** Le Dublin Core renvoie directement `dc:title`, `dc:creator`, `dc:publisher`, `dc:date`, `dc:language`, `dc:format` — bien plus simple à mapper que l'UNIMARC. Le nombre de résultats se lit dans ``. + +### ⚠️ Piège bloquant : ISBN-13 vs ISBN-10 + +**La BnF indexe l'ISBN tel qu'imprimé sur le livre.** Les ouvrages publiés avant 2007 ne portent qu'un **ISBN-10** et sont donc **introuvables par leur ISBN-13**, alors que le scanner de code-barres lit toujours un EAN-13. + +Mesuré sur un échantillon de livres français dont l'existence a été confirmée via OpenLibrary : + +| ISBN-13 | Recherche ISBN-13 | Recherche ISBN-10 | +|---|---|---| +| 9782070612758 (Le Petit Prince, 2007) | **1 notice** | 0 | +| 9782253004226 (Germinal, Livre de poche) | 0 | **3 notices** | +| 9782080704092 (Le Horla, Flammarion) | 0 | **1 notice** | + +Sans conversion, on perd la majorité du fonds ancien — précisément les livres d'une bibliothèque constituée. **Toujours interroger les deux formes** : ISBN-13 d'abord, puis ISBN-10 converti si aucun résultat. + +Conversion ISBN-13 → ISBN-10 (uniquement pour le préfixe `978`) : retirer `978`, garder les 9 chiffres, recalculer la clé (somme pondérée 10→2, modulo 11, `X` si le reste vaut 10). + +### Autres pièges confirmés + +- **Pas de couverture.** Le Dublin Core BnF n'en fournit aucune. Utiliser OpenLibrary pour l'image, **quelle que soit la source des métadonnées** : `https://covers.openlibrary.org/b/isbn/{isbn}-L.jpg?default=false`. Le `?default=false` est **indispensable** — sans lui, OpenLibrary renvoie une image placeholder au lieu d'un 404. Testé : disponible pour les 4 ISBN de l'échantillon, y compris ceux absents d'OpenLibrary côté bibliographique. +- **Plusieurs notices pour un même ISBN.** Germinal en renvoie 3 (rééditions successives partageant l'ISBN, éditeurs et années différents). Ne pas prendre aveuglément la première : soit proposer le choix à l'utilisateur, soit retenir la plus récente via `dc:date`. À trancher à l'implémentation. +- **Ponctuation ISBD à nettoyer.** Les champs ne sont pas exploitables bruts. Règles validées : + +| Champ | Brut | Nettoyé | +|---|---|---| +| `dc:title` | `Germinal / Émile Zola ; préface d'Armand Lanoux` | `Germinal` — couper au premier ` / ` | +| `dc:creator` | `Zola, Émile (1840-1902). Auteur du texte` | `Émile Zola` — retirer les dates entre parenthèses, le rôle après le point, puis inverser `Nom, Prénom` | +| `dc:publisher` | `le Livre de poche (Paris)` | `le Livre de poche` — retirer la ville en fin de chaîne | + +- **Livres étrangers absents**, comme attendu du dépôt légal français : *Introduction to Algorithms* et *Effective Java* introuvables sous les deux formes d'ISBN. C'est exactement le rôle d'OpenLibrary en second rideau — la cascade est donc bien nécessaire, pas seulement confortable. + ## Intégration SSO YunoHost **Mécanisme retenu : en-têtes HTTP injectés par SSOwat.** nginx authentifie le visiteur via le portail YunoHost, puis transmet l'identité à l'application dans des en-têtes que l'app se contente de lire : @@ -200,6 +243,6 @@ Conclusion : aucune des deux solutions existantes ne coche toutes les cases (pr Les grands choix structurants ont été tranchés le 2026-08-17 (offline, portée des données, sources ISBN, SSO, ebooks). Restent : -- **Validation concrète de l'API SRU de la BnF** — à faire avant d'écrire le service de lookup, puisqu'elle est désormais la source principale. +- **Choix de la notice quand la BnF en renvoie plusieurs** pour un même ISBN : proposer le choix à l'utilisateur, ou retenir la plus récente. À trancher au moment d'écrire l'écran d'ajout. - **Support technique du cache hors-ligne** : IndexedDB ou cache du service worker sur les routes `GET`. À trancher au moment de l'implémenter, une fois les écrans connus. - **Activation de l'AOT WASM** (`RunAOTCompilation`) : à décider après mesure du scan sur un vrai téléphone. Ne pas l'activer par défaut, le build devient nettement plus lent.