diff --git a/.env.example b/.env.example index a596b67..cf1cea6 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ BASE_URL = 'https://something.com' IIIF_API_VERSION = 2 # The Image API service's base URL IMAGE_SERVER_URL = 'https://images.something.com' +IMAGES_DIR = '/path/to/iiif/images' diff --git a/controllers/manifest.mjs b/controllers/manifest.mjs index 0faea86..0a42a74 100644 --- a/controllers/manifest.mjs +++ b/controllers/manifest.mjs @@ -6,6 +6,10 @@ import Canvas from '../src/Canvas.js'; import Image from '../src/Image.js'; import * as fs from 'fs'; + +/** + * @typedef {Obj} + */ /** * Generate a manifest object to serve * @param {string} manifestId @@ -14,17 +18,14 @@ export default async function generateManifest(manifestId) { const IIIF_API_VERSION = process.env.IIIF_API_VERSION; const BASE_URL = process.env.BASE_URL; - const manifest = new Manifest(IIIF_API_VERSION, BASE_URL); - const sequence = new Sequence(); - const canvas = new Canvas(IIIF_API_VERSION, BASE_URL); - const image = new Image(4000, 3000); - canvas.setThumbnail(); - canvas.addImage(image); - sequence.addCanvas(canvas); - manifest.addSequence(sequence); + let manifest = new Manifest(IIIF_API_VERSION, BASE_URL); manifest.generateID(manifestId); - - console.log(await getImageList(manifestId)); + + manifest = await populateCanvases( + manifest, + await getImageList(manifestId), + manifestId + ); return manifest.toObject(); } @@ -32,9 +33,74 @@ export default async function generateManifest(manifestId) { * @param {string} manifestId */ async function getImageList(manifestId) { - let folderName = manifestId.replace(/pherc-(\d+)-(\w+)$/, function (match, g1, g2) { + let folderName = manifestId.replace(/pherc-(\d+)-(\w+)$/, function (_match, g1, g2) { return `PHerc_${g1}_${g2.toUpperCase()}`; }); - return await fs.promises.readdir(`./test-images/${folderName}`); + folderName += '_iiif'; + + return await fs.promises.readdir(`${process.env.IMAGES_DIR}/${folderName}`); +} +/** + * @todo Read height & width from info.json + * @param {Manifest} manifest + * @param {string[]} images List of image filenames from folder + * @returns {Manifest} + */ +async function populateCanvases(manifest, images, manifestId) { + const IIIF_API_VERSION = process.env.IIIF_API_VERSION; + const BASE_URL = process.env.BASE_URL; + const sequence = new Sequence(BASE_URL); + // There's only one sequence + sequence.generateID(manifestId, 0); + + for (let img of images) { + let canvas = new Canvas(IIIF_API_VERSION, BASE_URL); + const canvasName = img.split('_')[3].replace(/\.[\w\d]{2,3}$/,''); + + canvas.generateID(manifestId, canvasName); + canvas.setLabel(`${manifestId}-${canvasName.toLowerCase()}`); + + let image = new Image(canvas.id); + image.generateID(process.env.IMAGE_SERVER_URL, img); + const imgSize = await getImageSize(image.id); + image.setSize(imgSize.height, imgSize.width); + + canvas.setThumbnail( + imgSize.thumb.height, + imgSize.thumb.width, + image.id + ); + + canvas.addImage(image); + sequence.addCanvas(canvas); + } + + manifest.addSequence(sequence); + + return manifest; +} +/** + * @todo Read height & width from info.json + * @param {string} imageId The image's id as a URL to the image server + * @returns {{width: number, height: number, thumb: {width: number, height: number}}} + */ +async function getImageSize(imageId) { + let infoURL = imageId.replace(/full.*$/,'info.json'); + const res = await fetch(infoURL); + + let size = {}; + + if (res.ok) { + const infoJson = await res.json(); + const maxSize = infoJson.sizes[infoJson.sizes.length - 1]; + size.height = maxSize.height; + size.width = maxSize.width; + size.thumb = { + width: infoJson.sizes[1].width, + height: infoJson.sizes[1].height, + } + } + + return size; } \ No newline at end of file diff --git a/src/Canvas.js b/src/Canvas.js index 29a37f0..3d4d561 100644 --- a/src/Canvas.js +++ b/src/Canvas.js @@ -18,11 +18,11 @@ class Canvas { this.BASE_URL = baseURL; } /** - * @param {string} idParam From the request + * @param {string} resourceId The resource ID for this canvas * @param {int|string} name A unique name for this canvas */ - generateID(idParam, name) { - this.id = `${this.BASE_URL}/iiif/${idParam}/canvas/${name}`; + generateID(resourceId, name) { + this.id = `${this.BASE_URL}/iiif/${resourceId}/canvas/${name}`; } /** * @param {string} label A label for this canvas @@ -39,12 +39,13 @@ class Canvas { /** * Add a thumbnail object * @todo Support multiple thumbs? - * @param {int} [height=300] - * @param {int} [width=300] + * @param {number} height + * @param {number} width + * @param {string} imageId The full image ID as server URL */ - setThumbnail(height = 300, width = 300) { + setThumbnail(height, width, imageId) { this.thumbnail = { - "@id" : '', + "@id" : `${imageId.replace(/\/full.*$/,'')}/full/${width},${height}/0/default.jpg`, "@type" : 'dctypes:Image', height, width diff --git a/src/Image.js b/src/Image.js index 6d31430..964f49d 100644 --- a/src/Image.js +++ b/src/Image.js @@ -3,35 +3,63 @@ import IIIFResource from './IIIFResource.js'; * @implements IIIFResource */ class Image { - /** - * @var {string} id A URL pointing to the image resource - */ id = ''; - type = 'dctypes:Image'; - format = 'image/jpeg'; + context = `http://iiif.io/api/presentation/${process.env.IIIF_API_VERSION}/context.json`; + type = 'oa:Annotation'; + motivation = 'sc:painting'; + __type = 'dctypes:Image'; + __format = 'image/jpeg'; height = 0; width = 0; service = {}; - - constructor(height, width) { + canvasId = ''; + /** + * + * @param {string} canvasId The canvas IIIF id + */ + constructor(canvasId) { + this.canvasId = canvasId; + } + /** + * @param {number} height + * @param {number} width + */ + setSize(height, width) { this.height = height; this.width = width; } + /** + * Generate IIIF id pointing to image + * server endpoint for this image + * @param {string} serviceURL The image server base URL + * @param {string} filename The image's complete filename + */ + generateID(serviceURL, filename) { + let splitFilename = filename.split('_'); + const baseFolder = splitFilename.slice(0,2).join('_') + '_iiif'; + const subfolder = splitFilename.slice(0,3).join('_') + '_iiif'; + + this.id = `${serviceURL}/2/${baseFolder}%2F${subfolder}%2F${filename}/full/full/0/default.jpg`; + } /** * Object representation of * image resource - * @returns {object} + * @returns {Object} */ toObject() { return { + "@context" : this.context, + "@type" : this.type, + motivation : this.motivation, resource : { "@id" : this.id, - "@type" : this.type, - format: this.format, + "@type" : this.__type, + format: this.__format, service : this.service, height: this.height, width: this.width, - } + }, + on : this.canvasId } } } diff --git a/src/Sequence.js b/src/Sequence.js index 1766d66..ea51b39 100644 --- a/src/Sequence.js +++ b/src/Sequence.js @@ -8,8 +8,18 @@ class Sequence { canvases = []; id = ''; type = 'sc:Sequence'; + + constructor(baseUrl) { + this.BASE_URL = baseUrl; + } + /** + * @param {string} resourceId The resource ID for this sequence + * @param {int|string} name A unique name for this sequence + */ + generateID(resourceId, name) { + this.id = `${this.BASE_URL}/iiif/${resourceId}/sequence/${name}`; + } /** - * @todo Implement * @param {Canvas} canvas The Canvas object */ addCanvas(canvas) {