Skip to content

feat: onboard additional IIIF libraries through a shared provider architecture #105

Closed

Description

@nikazzio

Summary

Onboard additional libraries through the shared provider architecture. Includes IIIF-native providers AND non-IIIF Italian providers accessible via MAG/XML API.

Depends on #104. → #104 CLOSED ✅


🟢 IIIF nativo — confermato live

Tier A — implementazione eccellente, alta priorità

Biblioteca Paese Endpoint Note
Bayerische Staatsbibliothek (BSB) 🇩🇪 api.digitale-sammlungen.de/iiif/presentation/v2/{id}/manifest v2 verificato. Enorme collezione, molti manoscritti italiani. SRU+OAI per ricerca.
Österr. Nationalbibliothek (ÖNB) 🇦🇹 api.onb.ac.at/iiif/presentation/v3/manifest/{id} v3 verificato. Codici medievali e archivi imperiali.
Wellcome Collection 🇬🇧 wellcomelibrary.org/iiif/{id}/manifest v2. Search API integrata. Focus medicina/scienza.
UB Leipzig 🇩🇪 iiif.ub.uni-leipzig.de/{id}/manifest.json v2 verificato. Manoscritti medievali, public domain.

Tier B — spike breve prima di implementare

  • Estense Digital Library (Modena) — IIIF nativo probabile, endpoint da trovare
  • Handschriftenportal HSP (Germania) — aggrega tutte le biblioteche tedesche
  • National Library of Scotland
  • BVMM / IRHT-CNRS (manoscritti miniati francesi)
  • e-manuscripta.ch (Zurigo, Basilea, Berna)

DSpace/GLAM (un resolver parametrizzato per tutti)

  • Digital Library Pavia
  • Biblioteca Civica Rovereto

🇮🇹 Provider italiani — Internet Culturale (MAG/XML API)

Scoperta chiave: le grandi biblioteche italiane (BML, Marciana, BNCF, BNCR) non espongono IIIF, ma Internet Culturale (ICCU) ha una API XML pubblica (formato MAG) che le copre tutte.

API

# Lista pagine con URL immagini:
GET https://www.internetculturale.it/jmms/magparser?id={oai_id}&teca={teca}&mode=all&offset=0&pag={n}

# Immagine singola:
GET https://www.internetculturale.it/jmms/thumbnail?type=normal&id={oai_id}&teca={teca}&page={n}

# Ricerca:
GET https://www.internetculturale.it/it/16/search?q={query}&instance=magindice&searchType=avanzato&channel__typeTipo=Manoscritto

Biblioteche coperte (un resolver unico)

Biblioteca teca_id Verifica
Biblioteca Nazionale Marciana (Venezia) marciana ✅ live
Biblioteca Medicea Laurenziana (Firenze) Laurenziana - FI ✅ live
BNCF (Firenze) da trovare 🔍
BNCR (Roma) da trovare 🔍
+ ~50 istituzioni partner ICCU vari —

Limitazione: immagini JPEG ~1500-2000px, non tile IIIF. Qualità inferiore ma accettabile.


Fuori portata

  • Biblioteca Ambrosiana — viewer proprietario
  • MLOL (Gazzetta Parma, Fondo Canova) — login richiesto

Ordine di implementazione suggerito

  1. BSB — IIIF v2, massimo valore immediato
  2. ÖNB — IIIF v3
  3. Internet Culturale — sblocca BML + Marciana + BNCF in un colpo solo
  4. Estense — spike + implementazione se confermato
  5. Wellcome — IIIF v2 con search API
  6. DSpace (Pavia, Rovereto) — resolver parametrizzato
  7. HSP, NLS, BVMM, e-manuscripta — spike + implementazione

Acceptance Criteria

  • Ogni provider risolve pagine/manifesti validi in Discovery e CLI.
  • La ricerca è opzionale; direct-resolution-first accettabile dove search pubblica è debole.
  • Provider esistenti non regrediscono.
  • Issue Aggiungere librerie IIIF #26 superseded da questa epic.

Activity

  1. added
    minorIncrements the minor version when adding new functionality in a backward-compatible manner.
    on Mar 13, 2026
  2. added this to the Next (P2) milestone on Mar 13, 2026
  3. added and removed
    minorIncrements the minor version when adding new functionality in a backward-compatible manner.
    on Apr 18, 2026
  4. nikazzio commented on Apr 19, 2026

    @nikazzio
    OwnerAuthor

    Stato avanzamento — sessione aperta (branch feat/iccu-internetculturale-provider)

    Aggiornamento dello stato dopo il lavoro su ICCU e lo spike Estense.

    ✅ Internet Culturale (ICCU) — funzionante end-to-end

    Resolver + search + download implementati e verificati live:

    • Resolver InternetCulturaleResolver con can_resolve per URL viewresource/magparser/iccuviewer e OAI ID raw (inferenza teca)
    • Parser MAG XML → IIIF v2 manifest (mag_parser.py) con URL immagini dal reale attributo src delle <page> (verificato: il param ?page=N dell'endpoint /jmms/thumbnail è ignorato dal server → pagine duplicate; il path cacheman/normal/{teca}/.../N.jpg funziona)
    • Search HTML scraping con paginazione nativa (pag=N, non paginate_pageNum)
    • Estrazione totale risultati da "Pagina X di Y (Z risultati trovati)" → mostrato in UI come "Mostrati A di Z"
    • Filtro tipo materiale (Manoscritto / Libro moderno / Musica / Fotografia)

    UI integration:

    • "Apri viewer" → pattern canonico /jmms/iccuviewer/iccu.jsp?id=...&mode=all&teca=... (il vecchio viewresource era pagina bianca per alcune teche es. BNCF)
    • Probe manifest XML-aware (il vecchio probe get_json falliva sempre)
    • Helper fetch_manifest_dict ICCU-aware per 5 call site UI (add_to_library, add_and_download, reader Studio, refresh_metadata, pdf_capability)
    • Endpoint proxy /api/iccu/manifest?url=... per servire il manifest convertito a Mirador (origin-CORS friendly)
    • "Carica altri" attivo per ICCU (_PAGINATABLE_STRATEGIES esteso) con propagazione ic_type filter
    • Fallback build_viewer_url nel provider registry usando extract_oai_and_teca_from_url per ricostruire URL esterno

    Download resilience:

    • Flag allow_partial_finalize automatico sui manifest con _iccu marker → i record "teaser" (XML dichiara N pagine ma solo M<N sono servite, es. BNCF CFIE004771 "Marozzo") finalizzano le M disponibili invece di non finalizzare nulla
    • Supporto canvas con resource.@id diretto (non-IIIF service): PageDownloader._locate_direct_image_url + _fetch_direct

    Test: 12 test unit in test_iccu_unit.py + fixture iccu_mag_sample.xml; regressione complessiva 85/85 green.

    File toccati (uncommitted):

    M  src/studio_ui/components/discovery_results.py
    M  src/studio_ui/routes/_studio/manifest_helpers.py
    M  src/studio_ui/routes/_studio/workspace.py
    M  src/studio_ui/routes/discovery.py
    M  src/studio_ui/routes/discovery_handlers.py
    M  src/studio_ui/routes/discovery_helpers.py
    M  src/studio_ui/routes/library_handlers.py
    M  src/universal_iiif_core/logic/downloader.py
    M  src/universal_iiif_core/logic/downloader_runtime.py
    M  src/universal_iiif_core/providers.py
    M  src/universal_iiif_core/resolvers/mag_parser.py
    M  src/universal_iiif_core/resolvers/search/internetculturale.py
    ?? src/universal_iiif_core/resolvers/manifest_fetch.py
    ?? tests/fixtures/iccu_mag_sample.xml
    ?? tests/test_iccu_unit.py
    

    ⚠️ Limitazioni note ICCU (upstream)

    • IIIF Image API ICCU v2.1 level 0: esiste (internetculturale.it/iiif/image/2.1/{id_b64}/full/full/0/default.jpg) ma è solo fullsize statica — nessun tile/zoom lato server
    • CORS: le immagini ICCU non mandano Access-Control-Allow-Origin (ok per <img>, rompe fetch/XHR di Mirador quando fa tile requests — ma con canvas no-service Mirador carica static image senza problemi)
    • Qualità variabile: alcuni record (es. CFIE004771 Marozzo) hanno solo il frontespizio digitalizzato upstream; il manifest dichiara 4 pagine ma 3 sono 404. Comportamento ora corretto via partial finalize
    • _OAI_PREFIX_TO_TECA copre solo Marciana + BML; BNCF/BNCR/altre ~50 teche non risolvono da OAI raw (ok da URL magparser completo)

    🔍 Spike Estense completato — IIIF nativo reale disponibile

    Scoperta: EDL ha backend Jarvis (jarvis.edl.beniculturali.it) con IIIF completo, NON solo MLOL come suggerisce la issue.

    Capability Valore
    Presentation API v2 + v3 nativi
    Image API level 2 (tile/zoom/rescale server-side)
    Totale items 126.537
    Search endpoint GET /meta/culturalItems/search/findBySgttOrAutnOrPressmark?text=...&page=0&size=20 (Spring Pageable, include totalElements+totalPages)
    Formati extra METS/MODS, DZI, Mirador embed

    Pattern URL:

    Manifest v2:  https://jarvis.edl.beniculturali.it/meta/iiif/{uuid}/manifest
    Manifest v3:  https://jarvis.edl.beniculturali.it/meta/iiif/v3/{uuid}/manifest
    Viewer:       https://jarvis.edl.beniculturali.it/images/viewers/mirador/?manifest=...
    

    Search test live: query "dante" → 79 risultati, primo hit Vita nuova. Autografo di Muratori (pressmark A.M.02.12.A), manifest già cliccabile in Mirador.

    Implementazione stimata: ~3-4 file (resolver + search handler + registrazione provider + test). Significativamente più semplice di ICCU perché il downloader IIIF standard esistente funziona senza modifiche.

    Open item: mapping edl.beniculturali.it/beu/{public_id} → Jarvis UUID non documentato pubblicamente. La SPA lo risolve client-side via XHR non ispezionabile da CLI. Workaround: provider usa solo URL Jarvis diretti + search (che restituisce già gli UUID); per input utente /beu/{id} eventuale fallback scraping o via Europeana dataProvider="Estense University Library".

    📌 Per la prossima sessione

    Punto di ripartenza suggerito:

    1. Committare il lavoro ICCU (15 file, 12 test, 1 nuovo modulo + fixture) sul branch feat/iccu-internetculturale-provider e aprire PR
    2. Nuovo branch feat/estense-jarvis-provider per EDL: resolver riconosce host jarvis.edl.beniculturali.it, search usa findBySgttOrAutnOrPressmark, integrazione provider registry con search_mode="search_first" e paginazione nativa Spring

    Nota architetturale: Estense sarà il primo test del codice per manifest IIIF v3 nel codebase (ora supportiamo solo v2 esplicitamente). Il manifest v2 di Jarvis è già funzionante ma vale la pena spingere su v3 per future-proofing.

  5. nikazzio commented on Apr 19, 2026

    @nikazzio
    OwnerAuthor

    Update — chiusura come integrazione di prodotto

    Chiuso il secondo giro sui blocchi non-code individuati dopo la review:

    Network policy (backend)

    src/universal_iiif_core/network_policy.py: aggiunti alias (internet culturale, internet culturale (iccu), iccu, internetculturale → internet_culturale), la chiave in LIBRARY_KEYS, e i default policy conservativi per il provider:

    Field Valore
    use_custom_policy true
    workers_per_job 2
    min_delay_s / max_delay_s 1.0 / 3.0
    retry_max_attempts 4
    backoff_base_s / backoff_cap_s 15 / 300
    cooldown_on_403_s / cooldown_on_429_s 300 / 300
    burst_window_s / burst_max_requests 60 / 40

    Verificato live: normalize_library_key("ICCU") → "internet_culturale", resolve_library_network_policy restituisce il nuovo bundle.

    Settings UI

    src/studio_ui/components/settings/panes/network.py: aggiunti tab button "Internet Culturale" e card override (use_custom_policy / workers / delay / retry / backoff / retry-after) coerente con gli altri provider esistenti.

    Docs

    • docs/reference/provider-support.md: ICCU in registry, matrix, dedicated note (MAG/XML, partial finalize, viewer JSP canonico, note su ICCU Image API level 0 e sul separato provider Estense futuro), provider filter elencato
    • docs/reference/configuration.md: chiave internet_culturale nel set di library supportate + rationale dei default conservativi
    • docs/CONFIG_REFERENCE.md: internet_culturale aggiunto alla lista library supportate; internetculturale inserito tra i provider paginabili in settings.discovery; nota sul page size fisso upstream (20/pag)
    • docs/guides/discovery-and-library.md: paragrafo dedicato a ICCU in "Search Pagination And Provider Behavior" (aggregatore ~50 biblioteche italiane, "Mostrati X di Y", partial records)

    Paginazione end-to-end verificata

    • search_internetculturale("dante", page=1) → 20 risultati, _search_total_results=2408, _search_total_pages=121
    • resolve_provider_input("Internet Culturale", "dante", filters={"page": 2}) → 20 risultati distinti
    • Pag 1/2/3 testati: overlap zero, ID tutti unici
    • Filtro ic_type propagato in load_more_results e sopravvive alla pagination

    Prossimi passi invariati

    1. Commit sul branch feat/iccu-internetculturale-provider + PR
    2. Nuovo branch feat/estense-jarvis-provider — resolver IIIF v2/v3 nativo sfruttando l'endpoint jarvis.edl.beniculturali.it/meta/iiif/{uuid}/manifest scoperto nello spike
  6. nikazzio commented on Apr 20, 2026

    @nikazzio
    OwnerAuthor

    Update — Estense provider mergiato (PR #160)

    Secondo provider italiano integrato, questa volta nativo IIIF.

    Estense in produzione (PR #160 merged)

    • Resolver: https://jarvis.edl.beniculturali.it/meta/iiif/{uuid}/manifest (v2) + v3, Mirador wrapper URLs, UUID nudi (8-4-4-4-12).
    • Search: GET /meta/culturalItems/search/findBySgttOrAutnOrPressmark?text=...&size=20&page=0 — copre short title / autore / pressmark in una chiamata. Spring Pageable (0-based), espone totalElements e totalPages → UI mostra "Mostrati X di Y" e "Carica altri".
    • Network policy: default non-custom (Jarvis è un backend IIIF moderno, no rate limiting speciale).
    • Settings UI: card "Estense" in Network & Libraries.
    • Test: 11 unit + fixture live Jarvis.
    • Docs: provider-support (registry + matrix + Per-Provider Notes), configuration, CONFIG_REFERENCE, discovery guide.

    Lezione dallo spike

    EDL (edl.cultura.gov.it) è un portal MLOL aggregatore, non solo Estense. Solo i ~126k record genuinamente digitalizzati dalla Biblioteca Estense di Modena sono in Jarvis. Record di altra provenienza (es. Marozzo della Rosenwald Collection LoC) sono indicizzati da MLOL su edl.cultura.gov.it ma puntano ai manifest delle biblioteche originali (LoC, BSB, ecc.).

    Conseguenza: il nostro provider Estense copre correttamente il catalogo Jarvis. Chi vuole un Marozzo LoC usa il provider Library of Congress con l'URL loc.gov/item/75229023/manifest.json. Integrare MLOL come provider richiederebbe analisi API separata e non è in scope di questo epic.

    Stato epic

    Gruppo Provider Stato
    🟢 IIIF nativo Tier A BSB, ÖNB, Wellcome, UB Leipzig ⚪ da fare
    🇮🇹 Italiani ICCU, Estense ✅ mergiati (#159, #160)
    🟢 IIIF nativo Tier B HSP, NLS, BVMM, e-manuscripta ⚪ da fare (spike prima)
    DSpace/GLAM Pavia, Rovereto ⚪ da fare (1 resolver parametrizzato)
    ⛔ Fuori portata Ambrosiana (viewer proprietario), MLOL con login n/a

    Prossimi step suggeriti (nessuno in sessione corrente)

    1. BSB — IIIF v2 maturo + SRU/OAI, ottimo per scaling internazionale
    2. ÖNB — primo test IIIF v3 in produzione
    3. Wellcome — search API discovery-first, workflow confortevole
    4. UB Leipzig — v2 semplice, buon candidato rapido
    5. Poi Tier B e DSpace in parallelo con feat(discovery): ricerca federata multi-libreria asincrona e concorrente #56 (ricerca federata) + Discovery: Refactor UIX + Search Jobs (puntuale + aggregata) #108 (Discovery UX)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions