UNPKG

@allmaps/iiif-parser

Version:

Allmaps IIIF parser

263 lines (262 loc) 10.3 kB
import { Collection2Schema, Collection3Schema, CollectionSchema } from '../schemas/iiif.js'; import { EmbeddedManifest, Manifest } from './manifest.js'; import { parseVersion2String, parseVersion3String, parseVersion2Metadata, parseVersion3Metadata, parseVersion2Attribution, parseVersion2Thumbnail, parseVersion2Rendering, parseVersion2Related } from '../lib/convert.js'; const CollectionTypeString = 'collection'; const defaulfFetchNextOptions = { maxDepth: Number.POSITIVE_INFINITY, fetchCollections: true, fetchManifests: true, fetchImages: false, fetchFn: globalThis.fetch }; export class EmbeddedCollection { uri; type = CollectionTypeString; majorVersion; // TODO: add description? // TODO: add metadata? label; embedded = true; constructor(parsedCollection) { if ('@type' in parsedCollection) { // IIIF Presentation API 2.0 this.uri = parsedCollection['@id']; this.majorVersion = 2; this.label = parseVersion2String(parsedCollection.label); } else if ('type' in parsedCollection) { // IIIF Presentation API 3.0 this.uri = parsedCollection.id; this.majorVersion = 3; this.label = parseVersion3String(parsedCollection.label); } else { // TODO: improve error message throw new Error('Unsupported Collection'); } } /** * Parses a IIIF Collection and returns a [Collection](#collection) containing the parsed version * @param iiifCollection - Source data of IIIF Collection * @param majorVersion - IIIF API version of Collection. If not provided, it will be determined automatically * @returns Parsed IIIF Collection * @static */ static parse(iiifCollection, majorVersion = null) { let parsedCollection; if (majorVersion === 2) { parsedCollection = Collection2Schema.parse(iiifCollection); } else if (majorVersion === 3) { parsedCollection = Collection3Schema.parse(iiifCollection); } else { parsedCollection = CollectionSchema.parse(iiifCollection); } return new Collection(parsedCollection); } } /** * Parsed IIIF Collection * * @property uri - URI of Collection * @property label - Label of Collection * @property items - Items in Collection * @property majorVersion - IIIF API version of Collection * @property type - Resource type, equals 'collection' */ export class Collection extends EmbeddedCollection { items = []; embedded = false; description; metadata; navDate; navPlace; homepage; thumbnail; rendering; seeAlso; summary; requiredStatement; annotations; constructor(parsedCollection) { super(parsedCollection); if ('@type' in parsedCollection) { // IIIF Presentation API 2.0 this.description = parseVersion2String(parsedCollection.description); this.label = parseVersion2String(parsedCollection.label); this.metadata = parseVersion2Metadata(parsedCollection.metadata); this.navDate = parsedCollection.navDate; this.navPlace = parsedCollection.navPlace; this.requiredStatement = parseVersion2Attribution(parsedCollection.attribution); this.thumbnail = parseVersion2Thumbnail(parsedCollection.thumbnail); this.rendering = parseVersion2Rendering(parsedCollection.rendering); this.homepage = parseVersion2Related(parsedCollection.related); const manifests = 'manifests' in parsedCollection && parsedCollection.manifests ? parsedCollection.manifests : []; const collections = 'collections' in parsedCollection && parsedCollection.collections ? parsedCollection.collections : []; const members = 'members' in parsedCollection && parsedCollection.members ? parsedCollection.members : []; const items = [...manifests, ...collections, ...members]; this.items = items.map((item) => { if (item['@type'] === 'sc:Collection') { return new Collection(item); } else { return new EmbeddedManifest(item); } }); } else if ('type' in parsedCollection) { // IIIF Presentation API 3.0 this.description = parseVersion3String(parsedCollection.description); this.metadata = parseVersion3Metadata(parsedCollection.metadata); this.navDate = parsedCollection.navDate; this.navPlace = parsedCollection.navPlace; this.homepage = parsedCollection.homepage; this.thumbnail = parsedCollection.thumbnail; this.rendering = parsedCollection.rendering; this.seeAlso = parsedCollection.seeAlso; this.summary = parsedCollection.summary; this.requiredStatement = parsedCollection.requiredStatement; this.annotations = parsedCollection.annotations; if ('items' in parsedCollection) { this.items = parsedCollection.items.map((item) => { if (item.type === 'Collection') { if ('items' in item) { return new Collection(item); } else { item; return new EmbeddedCollection(item); } } else { return new EmbeddedManifest(item); } }); } } else { // TODO: improve error message throw new Error('Unsupported Collection'); } } /** * Parses a IIIF Collection and returns a [Collection](#collection) containing the parsed version * @param iiifCollection - Source data of IIIF Collection * @param majorVersion - IIIF API version of Collection. If not provided, it will be determined automatically * @returns Parsed IIIF Collection */ static parse(iiifCollection, majorVersion = null) { let parsedCollection; if (majorVersion === 2) { parsedCollection = Collection2Schema.parse(iiifCollection); } else if (majorVersion === 3) { parsedCollection = Collection3Schema.parse(iiifCollection); } else { parsedCollection = CollectionSchema.parse(iiifCollection); } return new Collection(parsedCollection); } get canvases() { const initialValue = []; return this.items.reduce((canvases, item) => { if (item instanceof Manifest) { return [...canvases, ...item.canvases]; } else if (item instanceof EmbeddedManifest) { return canvases; } else if (item instanceof Collection) { return [...canvases, ...item.canvases]; } return canvases; }, initialValue); } get images() { return this.canvases.map((canvas) => canvas.image); } async fetchAll(options) { const results = []; for await (const next of this.fetchNext(options)) { results.push(next); } return results; } async *fetchNext(options, depth = 0) { options = { ...defaulfFetchNextOptions, ...options }; if (Number.isNaN(options.maxDepth)) { return; } if (options.maxDepth === undefined) { options.maxDepth = defaulfFetchNextOptions.maxDepth; } if (options.fetchImages === undefined) { options.fetchImages = defaulfFetchNextOptions.fetchImages; } if (options.fetchManifests === undefined) { options.fetchManifests = defaulfFetchNextOptions.fetchManifests; } let fetchFn = defaulfFetchNextOptions.fetchFn; if (options.fetchFn) { fetchFn = options.fetchFn; } if (depth >= options.maxDepth) { return; } for (const itemIndex in this.items) { let item = this.items[itemIndex]; if (item instanceof Manifest) { if (options.fetchImages) { yield* item.fetchNext(fetchFn, depth + 1); } } else if (item instanceof EmbeddedManifest && options.fetchManifests) { const manifestUri = item.uri; const iiifManifest = await fetchFn(manifestUri).then((response) => response.json()); const newParsedManifest = Manifest.parse(iiifManifest); this.items[itemIndex] = newParsedManifest; yield { item: newParsedManifest, depth: depth + 1, parent: { uri: this.uri, type: this.type } }; if (depth + 1 < options.maxDepth && options.fetchImages) { yield* newParsedManifest.fetchNext(fetchFn, depth + 2); } } else if (item instanceof EmbeddedCollection && options.fetchCollections) { const collectionUri = item.uri; const iiifCollection = await fetchFn(collectionUri).then((response) => response.json()); const newParsedCollection = Collection.parse(iiifCollection); this.items[itemIndex] = newParsedCollection; yield { item: newParsedCollection, depth: depth + 1, parent: { uri: this.uri, type: this.type } }; item = newParsedCollection; if (depth + 1 < options.maxDepth) { yield* newParsedCollection.fetchNext(options, depth + 2); } } } } }