- add uv configuration for the backend - update frontend to make auth work - add new auth endpoints - add bookmars feat - add reader feat
3.6 KiB
010 — Open Library metadata on import
Status: Implemented
Created: 2026-06-03
Objective
After a user uploads an EPUB/PDF (POST /api/books/ebooks/), enrich the EBook with metadata and a cover from Open Library, using only title and author from the upload form. Prefer Spanish editions when available; fall back to English/any.
Upload must never fail if Open Library is down or no match is found.
Trigger
- Automatic:
EBookUploadSerializer.create()callsenrich_ebook_metadata(ebook)after file save. - Manual:
POST /api/books/ebooks/{id}/enrich-metadata/re-runs enrichment (owner only).
Open Library usage
Search
GET https://openlibrary.org/search.json
| Param | Value |
|---|---|
title |
User-provided title |
author |
User-provided author |
lang |
es (primary) or en (fallback) |
limit |
5 |
fields |
key,title,author_name,cover_i,first_publish_year,subject,language,edition_key,number_of_pages_median,publisher |
Primary pass also uses query filter language:spa. Fallback omits language filter.
Covers
GET https://covers.openlibrary.org/b/id/{cover_i}-L.jpg — downloaded and stored on EBook.cover_image.
Match scoring
| Score | Behavior |
|---|---|
| ≥ 0.8 | Apply OL title/author + cover + full metadata |
| 0.6 – 0.8 | Metadata + cover only; keep user title/author |
| < 0.6 | match_status: not_found; no field changes except metadata stub |
Author overlap + title similarity (normalized strings, difflib.SequenceMatcher). Prefer hits with cover_i.
Data model
No migration. Uses existing fields on EBook:
metadata_json— full enrichment payload (see below)cover_image— downloaded cover filetitle/author— updated when match score ≥ 0.8
metadata_json shape
{
"source": "openlibrary",
"matched_at": "2026-06-03T12:00:00+00:00",
"match_language": "es",
"match_score": 0.92,
"match_status": "matched",
"user_input": { "title": "...", "author": "..." },
"openlibrary": {
"work_key": "/works/OL...",
"edition_key": "...",
"title": "...",
"authors": ["..."],
"cover_id": 12345,
"cover_url": "https://covers.openlibrary.org/b/id/12345-L.jpg",
"first_publish_year": 1605,
"subjects": ["..."],
"languages": ["spa"],
"publishers": ["..."],
"number_of_pages_median": 320
}
}
API changes
Upload response (unchanged path)
POST /api/books/ebooks/ — response may include populated cover_image and updated title/author after sync enrichment.
Detail
GET /api/books/ebooks/{id}/ — adds read-only metadata (alias of metadata_json).
Manual refresh
POST /api/books/ebooks/{id}/enrich-metadata/ — returns updated EBookDetailSerializer payload.
Configuration
| Env var | Default | Description |
|---|---|---|
OPENLIBRARY_ENABLED |
true |
Kill switch |
OPENLIBRARY_PREFERRED_LANG |
es |
Primary lang param |
OPENLIBRARY_FALLBACK_LANG |
en |
Fallback lang param |
OPENLIBRARY_TIMEOUT_SECONDS |
5 |
HTTP timeout |
OPENLIBRARY_USER_AGENT |
CloudReader/1.0 |
User-Agent header |
Code layout
backend/apps/books/services/
├── openlibrary.py # Search, scoring, cover download
└── metadata.py # enrich_ebook_metadata orchestrator
Verification
- Upload with title
Don Quijote, authorCervantes→ cover + Spanish-friendly metadata. - Upload with nonsense title/author → 201, no cover,
match_status: not_found. POST .../enrich-metadata/on existing ebook refreshes metadata.