Skip to content

Commit

Permalink
refactor: add resource and service media classes (#2467)
Browse files Browse the repository at this point in the history
* refactor: add resource and service media classes

* test: spec EuropeanaMediaService

* test: spec EuropeanaMediaResource
  • Loading branch information
rwd authored Nov 4, 2024
1 parent e88405a commit e6b1981
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 70 deletions.
30 changes: 15 additions & 15 deletions packages/portal/src/components/item/ItemMediaPresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,35 @@
/>
<MediaImageViewer
v-if="imageTypeResource"
:url="resource.about"
:url="resource.id"
:item-id="itemId"
:width="resource.ebucoreWidth"
:height="resource.ebucoreHeight"
:format="resource.ebucoreHasMimeType"
:service="resource.svcsHasService"
:width="resource.width"
:height="resource.height"
:format="resource.format"
:service="resource.service"
:annotation="activeAnnotation"
/>
<MediaPDFViewer
v-else-if="resource?.ebucoreHasMimeType === 'application/pdf'"
:url="resource.about"
v-else-if="resource?.format === 'application/pdf'"
:url="resource.id"
:item-id="itemId"
/>
<MediaAudioVisualPlayer
v-else-if="resource?.isPlayableMedia"
:url="resource.about"
:format="resource.ebucoreHasMimeType"
v-else-if="resource?.edm.isPlayableMedia"
:url="resource.id"
:format="resource.format"
:item-id="itemId"
/>
<EmbedOEmbed
v-else-if="resource?.isOEmbed"
:url="resource.about"
v-else-if="resource?.edm.isOEmbed"
:url="resource.id"
/>
<code
v-else
class="h-50 w-100 p-5"
>
<pre
:style="{ color: 'white' }"
:style="{ color: 'white', 'overflow-wrap': 'break-word' }"
><!--
-->{{ JSON.stringify(resource, null, 2) }}
</pre>
Expand Down Expand Up @@ -128,8 +128,8 @@
</template>
<script>
import hideTooltips from '@/mixins/hideTooltips';
import useItemMediaPresentation from '@/composables/itemMediaPresentation.js';
import hideTooltips from '@/mixins/hideTooltips';
export default {
name: 'ItemMediaPresentation',
Expand Down Expand Up @@ -239,7 +239,7 @@
},
imageTypeResource() {
return this.resource?.ebucoreHasMimeType?.startsWith('image/');
return this.resource?.format?.startsWith('image/');
}
},
Expand Down
26 changes: 15 additions & 11 deletions packages/portal/src/components/media/MediaCardImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class="media-card-image"
>
<b-link
v-if="linkable && imageLink && thumbnails.large && !media.forEdmIsShownAt"
v-if="linkable && imageLink && thumbnails.large && !webResource.forEdmIsShownAt"
:href="imageLink"
target="_blank"
>
Expand Down Expand Up @@ -54,7 +54,8 @@
</template>

<script>
import WebResource from '@/plugins/europeana/edm/WebResource';
import EuropeanaMediaResource from '@/utils/europeana/media/Resource.js';
import WebResource from '@/plugins/europeana/edm/WebResource.js';
export default {
name: 'MediaCardImage',
Expand All @@ -65,7 +66,9 @@
props: {
media: {
type: WebResource,
// TODO: refactor to only receive EuropeanaMediaResource, once legacy
// media presentation is gone
type: [EuropeanaMediaResource, WebResource],
default: null
},
lazy: {
Expand Down Expand Up @@ -96,38 +99,39 @@
data() {
return {
webResource: (this.media instanceof WebResource) ? this.media : this.media.edm,
showDefaultThumbnail: false
};
},
computed: {
imageLink() {
return this.$apis.record.mediaProxyUrl(this.media.about, this.europeanaIdentifier, { disposition: 'inline' });
return this.$apis.record.mediaProxyUrl(this.webResource.about, this.europeanaIdentifier, { disposition: 'inline' });
},
thumbnails() {
return this.media.thumbnails(this.$nuxt.context);
return this.webResource.thumbnails(this.$nuxt.context);
},
thumbnailSrc() {
return this.thumbnails[this.thumbnailSize];
},
thumbnailWidth() {
if (!this.media.ebucoreWidth) {
if (!this.webResource.ebucoreWidth) {
return null;
}
const thumbnailMaxSize = this.thumbnailSize === 'large' ? 400 : 200;
if (this.media.ebucoreWidth < thumbnailMaxSize) {
return this.media.ebucoreWidth;
if (this.webResource.ebucoreWidth < thumbnailMaxSize) {
return this.webResource.ebucoreWidth;
}
return thumbnailMaxSize;
},
thumbnailHeight() {
if (!this.media.ebucoreHeight || !this.thumbnailWidth) {
if (!this.webResource.ebucoreHeight || !this.thumbnailWidth) {
return null;
}
return (this.media.ebucoreHeight / this.media.ebucoreWidth) * this.thumbnailWidth;
return (this.webResource.ebucoreHeight / this.webResource.ebucoreWidth) * this.thumbnailWidth;
},
edmTypeWithFallback() {
return this.media.edmType || this.edmType;
return this.webResource.edmType || this.edmType;
}
},
Expand Down
7 changes: 3 additions & 4 deletions packages/portal/src/components/media/MediaImageViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
</template>

<script>
import axios from 'axios';
// NOTE: each of the imported OpenLayers modules needs to be added to
// build.transpile in nuxt.config.js
import IIIFSource from 'ol/source/IIIF.js';
Expand All @@ -29,6 +28,7 @@
import useZoom from '@/composables/zoom.js';
import EuropeanaMediaAnnotation from '@/utils/europeana/media/Annotation.js';
import EuropeanaMediaService from '@/utils/europeana/media/Service.js';
import MediaImageViewerKeyboardToggle from './MediaImageViewerKeyboardToggle.vue';
Expand Down Expand Up @@ -58,7 +58,7 @@
default: null
},
service: {
type: Object,
type: EuropeanaMediaService,
default: null
},
url: {
Expand Down Expand Up @@ -98,8 +98,7 @@
async fetch() {
if (this.service?.id) {
const infoUri = `${this.service.id}/info.json`;
const infoResponse = await axios.get(infoUri);
const infoResponse = await this.service.fetchInfo();
this.info = infoResponse.data;
this.source = 'IIIF';
// this.fullsize = true;
Expand Down
3 changes: 2 additions & 1 deletion packages/portal/src/composables/itemMediaPresentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { computed, ref } from 'vue';

import EuropeanaMediaAnnotationList from '@/utils/europeana/media/AnnotationList.js';
import EuropeanaMediaPresentation from '@/utils/europeana/media/Presentation.js';
import EuropeanaMediaResource from '@/utils/europeana/media/Resource.js';

const annotations = ref([]);
const annotationSearchHits = ref([]);
Expand Down Expand Up @@ -81,7 +82,7 @@ const fetchPresentation = async(uri) => {
const setPresentationFromWebResources = (webResources) => {
presentation.value = new EuropeanaMediaPresentation({
canvases: webResources.map((resource) => ({
resource
resource: EuropeanaMediaResource.fromEDM(resource)
}))
});
};
Expand Down
11 changes: 10 additions & 1 deletion packages/portal/src/utils/europeana/media/Base.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ export default class EuropeanaMediaBase {
// factory method to create an instance and parse some data to initialise its
// properties
static parse(data) {
if (!data) {
return undefined;
}

const resource = new this;
resource.parse(data);

return resource;
}

Expand All @@ -67,6 +72,10 @@ export default class EuropeanaMediaBase {
});
}

static omitIsUndefined(data) {
return omitBy(data, isUndefined);
}

static getHashParam(hash, key) {
if (hash?.startsWith?.('#')) {
return new URLSearchParams(hash.slice(1)).get(key);
Expand Down Expand Up @@ -94,7 +103,7 @@ export default class EuropeanaMediaBase {
}

postParseData(data) {
return omitBy(data, isUndefined);
return this.constructor.omitIsUndefined(data);
}

parse(data) {
Expand Down
20 changes: 3 additions & 17 deletions packages/portal/src/utils/europeana/media/Presentation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import WebResource from '@/plugins/europeana/edm/WebResource.js';
import Base from './Base.js';
import Resource from './Resource.js';
import {
IIIF_PRESENTATION_V2_CONTEXT,
IIIF_PRESENTATION_V3_CONTEXT
Expand Down Expand Up @@ -38,32 +38,18 @@ export default class EuropeanaMediaPresentation extends Base {
}

static extractV2Canvases(manifest) {
// TODO: limit to images w/ motivation "painting"?
return (manifest.sequences || []).map((sequence) => sequence.canvases.map((canvas) => ({
id: canvas.id,
annotations: canvas.otherContent,
resource: EuropeanaMediaPresentation.webResource(canvas.images[0].resource)
resource: Resource.parse(canvas.images[0].resource)
}))).flat();
}

static extractV3Canvases(manifest) {
// TODO: limit to "annotations" w/ motivation "painting"?
return (manifest.items || []).map((canvas) => ({
id: canvas.id,
annotations: canvas.annotations,
resource: EuropeanaMediaPresentation.webResource(canvas.items[0].items[0].body)
resource: Resource.parse(canvas.items[0].items[0].body)
}));
}

static webResource(data) {
return new WebResource({
// TODO: rename this `id`, but do so throughout the app too
about: data.id,
ebucoreHasMimeType: data.format,
ebucoreHeight: data.height,
ebucoreWidth: data.width,
// TODO: filter for IIIF Image service
svcsHasService: [].concat(data.service || [])[0]
});
}
}
62 changes: 62 additions & 0 deletions packages/portal/src/utils/europeana/media/Resource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Base from './Base.js';
import Service from './Service.js';
import EDMWebResource from '@/plugins/europeana/edm/WebResource.js';

export default class EuropeanaMediaResource extends Base {
$edm;

static fromEDM(edm) {
if (!edm) {
return undefined;
}

let data = {};
if (typeof edm === 'string') {
data.id = edm;
} else {
data = this.omitIsUndefined({
edm,
id: edm.about,
format: edm.ebucoreHasMimeType,
height: edm.ebucoreHeight,
width: edm.ebucoreWidth,
service: Service.fromEDM([].concat(edm.svcsHasService)[0])
});
}

return new this(data);
}

parseData(data) {
data = super.parseData(data);

const parsed = {
id: data.id,
format: data.format,
height: data.height,
service: Service.parse([].concat(data.service)[0]),
width: data.width
};

return parsed;
}

get edm() {
if (!this.$edm) {
const data = this.constructor.omitIsUndefined({
about: this.id,
ebucoreHasMimeType: this.format,
ebucoreHeight: this.height,
ebucoreWidth: this.width,
svcsHasService: [].concat(this.service || [])[0]?.edm
});

this.$edm = new EDMWebResource(data);
}
return this.$edm;
}

set edm(value) {
this.$edm = value;
}
}
53 changes: 53 additions & 0 deletions packages/portal/src/utils/europeana/media/Service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Base from './Base.js';
import EDMService from '@/plugins/europeana/edm/Service.js';

export default class EuropeanaMediaService extends Base {
$edm;

static fromEDM(edm) {
if (!edm) {
return undefined;
}

let data = {};
if (typeof edm === 'string') {
data.id = edm;
} else {
data = this.omitIsUndefined({
edm,
context: 'http://iiif.io/api/image/2/context.json',
id: edm.about,
profile: edm.doapImplements
});
}

return new this(data);
}

get infoUrl() {
return `${this.id}/info.json`;
}

fetchInfo() {
return this.constructor.fetch({
url: this.infoUrl
});
}

get edm() {
if (!this.$edm) {
const data = this.constructor.omitIsUndefined({
about: this.id,
doapImplements: this.profile,
dctermsConformsTo: ['http://iiif.io/api/image']
});

this.$edm = new EDMService(data);
}
return this.$edm;
}

set edm(value) {
this.$edm = value;
}
}
Loading

0 comments on commit e6b1981

Please sign in to comment.