Compare commits
3 Commits
c69fc1cbba
...
normalise-
| Author | SHA1 | Date | |
|---|---|---|---|
| 0871c8595a | |||
| 15028da9ff | |||
| 9a4a0a490e |
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
|||||||
# IIIF Manifest service for the GreekSchools Project
|
# IIIF Manifest service for the GreekSchools Project
|
||||||
|
|
||||||
This repository holds the code for a NodeJS/Express service that implements dynamic generation of IIIF manifests, compliant with version 2 of the Presentation API. Support for version 3 should be added in the future.
|
This repository holds the code for a NodeJS/Express service that implements dynamic generation of IIIF manifests for images produced by the [GreekSchools ERC project](https://greekschools.eu). The service is compliant with version 2 of the Presentation API, support for version 3 should be added in the future.
|
||||||
|
|
||||||
The project uses `yarn` for dependency management and an `.env` file to set environment variables, an example of which can be found in `.env.example`.
|
The project uses `yarn` for dependency management and an `.env` file to set environment variables, an example of which can be found in `.env.example`.
|
||||||
|
|
||||||
@@ -8,19 +8,19 @@ The project uses `yarn` for dependency management and an `.env` file to set envi
|
|||||||
|
|
||||||
`GreekManifests` requires NodeJS v. >= 20 to be installed on the system, as well as `yarn` as a package manager, which can be installed globally via `npm`:
|
`GreekManifests` requires NodeJS v. >= 20 to be installed on the system, as well as `yarn` as a package manager, which can be installed globally via `npm`:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
npm install -g yarn
|
npm install -g yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
To install the service itself, clone this repository on the target host (replace `<target_dir>` with a suitable path, or remove to install in `./greek-manifests`):
|
To install the service itself, clone this repository on the target host (replace `<target_dir>` with a suitable path, or remove to install in `./greek-manifests`):
|
||||||
|
|
||||||
```
|
```shell
|
||||||
git clone https://git.electricmandarine.cloud/nicolo/greek-manifests <target_dir>
|
git clone https://git.electricmandarine.cloud/nicolo/greek-manifests <target_dir>
|
||||||
```
|
```
|
||||||
|
|
||||||
then run the following commands from the root folder:
|
then run the following commands from the root folder:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
yarn
|
yarn
|
||||||
node app.mjs
|
node app.mjs
|
||||||
```
|
```
|
||||||
@@ -30,9 +30,9 @@ This will start the [Express](https://expressjs.com) web server, which will rema
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Automatic JSDoc documentation for the codebase can be generated by running the following command from the root folder:
|
Automatic [JSDoc](https://jsdoc.app) documentation for the codebase can be generated by running the following command from the project's root folder:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
jsdoc -c jsdoc.json
|
jsdoc -c jsdoc.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { authors, COPYRIGHT } from '../constants.js';
|
|||||||
* @property {String} subfolder
|
* @property {String} subfolder
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const namePos = {
|
export const namePos = {
|
||||||
hiroxnir: 1,
|
hiroxnir: 1,
|
||||||
nir: 1,
|
nir: 1,
|
||||||
visr: 1,
|
visr: 1,
|
||||||
@@ -37,6 +37,28 @@ const namePos = {
|
|||||||
dn: 2
|
dn: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove leading zeroes
|
||||||
|
* from canvas and image file
|
||||||
|
* name segments (parts)
|
||||||
|
* @see getCanvasName()
|
||||||
|
* @see ManifestBuilder.getImageName()
|
||||||
|
* @param {String} segment
|
||||||
|
*/
|
||||||
|
export const normaliseSegment = (segment) => {
|
||||||
|
return segment.replaceAll(/^([a-z]+)?0*(\d+)/ig, '$1$2');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Normalize a full canvas name
|
||||||
|
* @param {String} name
|
||||||
|
*/
|
||||||
|
export const normaliseName = (name) => {
|
||||||
|
// Account for separate parts in name
|
||||||
|
// and rejoin them after normalisation
|
||||||
|
// also replace any "slipping" file extension
|
||||||
|
return name.split('&').map(normaliseSegment).join('&').replace(/\.[a-z\d]+$/ig,'');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} imgFilename
|
* @param {string} imgFilename
|
||||||
* @returns {ParsedMetadata}
|
* @returns {ParsedMetadata}
|
||||||
@@ -162,7 +184,7 @@ const extractors = {
|
|||||||
* @param {string} technique
|
* @param {string} technique
|
||||||
* @returns {ParsedMetadata}
|
* @returns {ParsedMetadata}
|
||||||
*/
|
*/
|
||||||
export function parse (imgFilename, technique) {
|
export function parse(imgFilename, technique) {
|
||||||
return extractors[technique](imgFilename);
|
return extractors[technique](imgFilename);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -180,7 +202,7 @@ export function getCanvasName(imgFilename, technique) {
|
|||||||
canvasName += imgFilename.split('_')[3].replace(/\..*$/,'');
|
canvasName += imgFilename.split('_')[3].replace(/\..*$/,'');
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvasName;
|
return normaliseName(canvasName);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Generate canvas label from canvasName
|
* Generate canvas label from canvasName
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import Manifest from '../iiif/Manifest.js';
|
||||||
|
import { normaliseName, normaliseSegment, namePos } from './FilenameParser.js';
|
||||||
/**
|
/**
|
||||||
* Handles filesystem for image repository
|
* Handles filesystem for image repository
|
||||||
* @module ImageRepository
|
* @module ImageRepository
|
||||||
@@ -58,4 +60,35 @@ export async function getParamsFromFolders () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This extacts a segment to be used
|
||||||
|
* in getImageName(), accounting for the
|
||||||
|
* HSI special case
|
||||||
|
* @param {string} filename The image filename
|
||||||
|
* @param {string} technique
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getMatchableSegment(filename, technique) {
|
||||||
|
const parts = filename.split('_');
|
||||||
|
if (technique === 'hsi') {
|
||||||
|
const name = normaliseSegment(parts[1]);
|
||||||
|
const pca = parts[3].replace(/\.[a-z\d]+$/i, '').toLowerCase();
|
||||||
|
return `${name}${pca}`;
|
||||||
|
}
|
||||||
|
return normaliseName(parts[namePos[technique]]);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get image name for given canvas
|
||||||
|
* @param {string} name The Canvas name
|
||||||
|
* @param {Manifest} manifest The Manifest object
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export async function getImageName(name, manifest) {
|
||||||
|
const images = await getImageList(manifest.resourceId);
|
||||||
|
let adjustedCanvasName = name;
|
||||||
|
|
||||||
|
return images.filter(i => {
|
||||||
|
return getMatchableSegment(i, manifest.technique) === adjustedCanvasName;
|
||||||
|
})[0];
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import Canvas from '../iiif/Canvas.js';
|
|||||||
import Image from '../iiif/Image.js';
|
import Image from '../iiif/Image.js';
|
||||||
import ManifestMetadata from '../iiif/Metadata.js';
|
import ManifestMetadata from '../iiif/Metadata.js';
|
||||||
import { parse, getCanvasLabel, getCanvasName } from './FilenameParser.js';
|
import { parse, getCanvasLabel, getCanvasName } from './FilenameParser.js';
|
||||||
import { getImageList } from './ImageRepository.js';
|
import { getImageList, getImageName } from './ImageRepository.js';
|
||||||
import { TECH_NAMES } from '../constants.js';
|
import { TECH_NAMES } from '../constants.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +52,7 @@ export async function buildManifest(manifestId) {
|
|||||||
export async function buildCanvas(manifestId, name) {
|
export async function buildCanvas(manifestId, name) {
|
||||||
const manifest = new Manifest(IIIF_API_VERSION, BASE_URL);
|
const manifest = new Manifest(IIIF_API_VERSION, BASE_URL);
|
||||||
manifest.generateID(manifestId);
|
manifest.generateID(manifestId);
|
||||||
let filename = await getImageName(name, manifestId);
|
let filename = await getImageName(name, manifest);
|
||||||
|
|
||||||
return createCanvas(manifest, filename);
|
return createCanvas(manifest, filename);
|
||||||
}
|
}
|
||||||
@@ -141,28 +141,6 @@ async function populateCanvases (manifest, images) {
|
|||||||
|
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Get image name for given canvas
|
|
||||||
* @todo Use regex in filter!!
|
|
||||||
* @param {String} name The canvas name
|
|
||||||
* @param {String} manifestId The manifest (resource) id
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
async function getImageName(name, manifestId) {
|
|
||||||
const images = await getImageList(manifestId);
|
|
||||||
|
|
||||||
// Adjust canvas name for HSI with PCA...
|
|
||||||
if (/pc(1|3)/.test(name)) {
|
|
||||||
name = name.replace(
|
|
||||||
/pc((1|3))/,
|
|
||||||
function (match, group1) {
|
|
||||||
return `_HSI_PC${group1}`;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return images.filter(i => i.includes(name))[0];
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @param {Manifest} manifest The Manifest object
|
* @param {Manifest} manifest The Manifest object
|
||||||
* @param {string} imgFilename
|
* @param {string} imgFilename
|
||||||
|
|||||||
Reference in New Issue
Block a user