Skip to content

Commit

Permalink
feat: thumbnail fallback
Browse files Browse the repository at this point in the history
for non-viewable media, e.g. PDFs, isShownAt, TIFFs

and use same for viewable images without IIIF service until the
thumbnail is interacted with
  • Loading branch information
rwd committed Nov 27, 2024
1 parent 3b2c0b2 commit 16597ab
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 140 deletions.
81 changes: 34 additions & 47 deletions packages/portal/src/components/item/ItemMediaPresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,66 +43,41 @@
:provider-url="providerUrl"
/>
<MediaImageViewer
v-else-if="imageTypeResource"
v-else-if="viewableImageResource && !displayThumbnail"
:url="resource.id"
:item-id="itemId"
:width="resource.width"
:height="resource.height"
:format="resource.format"
:service="resource.service"
:thumbnail="thumbnail"
@error="handleImageError"
>
<MediaImageViewerControls
:fullscreen="fullscreen"
@toggleFullscreen="toggleFullscreen"
/>
</MediaImageViewer>
<MediaPDFViewer
v-else-if="resource?.format === 'application/pdf'"
:url="resource.id"
:item-id="itemId"
class="media-viewer-content"
/>
<MediaAudioVisualPlayer
v-else-if="resource?.edm.isPlayableMedia"
v-else-if="resource?.edm?.isPlayableMedia"
:url="resource.id"
:format="resource.format"
:item-id="itemId"
class="media-viewer-content"
/>
<EmbedOEmbed
v-else-if="resource?.edm.isOEmbed"
v-else-if="resource?.edm?.isOEmbed"
:url="resource.id"
class="media-viewer-content"
/>
<MediaImageViewer
v-else-if="resource?.edm?.preview"
:url="resource.edm.preview.about"
:item-id="itemId"
:width="resource.edm.preview.ebucoreWidth"
:height="resource.edm.preview.ebucoreHeight"
:thumbnail="thumbnail"
/>
<MediaImageViewer
v-else-if="thumbnail"
:url="resource.edm.about"
:item-id="itemId"
:annotation="activeAnnotation"
:width="resource.edm.ebucoreWidth"
:height="resource.edm.ebucoreHeight"
:thumbnail="thumbnail"
<MediaCardImage
v-else-if="resource?.edm"
:media="resource.edm"
:lazy="false"
:edm-type="edmType"
:linkable="false"
thumbnail-size="large"
@click.native="() => thumbnailInteractedWith = true"
/>
<code
v-else
class="media-viewer-content h-50 w-100 p-5"
>
<pre
:style="{ color: 'white', 'overflow-wrap': 'break-word' }"
><!--
-->{{ JSON.stringify(resource?.edm, null, 2) }}
</pre>
</code>
</template>
</div>
<div
Expand Down Expand Up @@ -140,6 +115,7 @@

<script>
import LoadingSpinner from '../generic/LoadingSpinner.vue';
import MediaCardImage from '../media/MediaCardImage.vue';
import useItemMediaPresentation from '@/composables/itemMediaPresentation.js';
export class ItemMediaPresentationError extends Error {
Expand All @@ -161,9 +137,9 @@
ItemMediaThumbnails: () => import('./ItemMediaThumbnails.vue'),
LoadingSpinner,
MediaAudioVisualPlayer: () => import('../media/MediaAudioVisualPlayer.vue'),
MediaCardImage,
MediaImageViewer: () => import('../media/MediaImageViewer.vue'),
MediaImageViewerControls: () => import('../media/MediaImageViewerControls.vue'),
MediaPDFViewer: () => import('../media/MediaPDFViewer.vue')
MediaImageViewerControls: () => import('../media/MediaImageViewerControls.vue')
},
props: {
Expand Down Expand Up @@ -221,7 +197,8 @@
return {
fullscreen: false,
showPages: true,
showSidebar: !!this.$route.hash
showSidebar: !!this.$route.hash,
thumbnailInteractedWith: false
};
},
Expand Down Expand Up @@ -259,6 +236,14 @@
},
computed: {
displayThumbnail() {
return !((
(this.viewableImageResource && (this.service || this.thumbnailInteractedWith)) ||
this.resource?.edm?.isPlayableMedia ||
this.resource?.edm?.isOEmbed
));
},
hasManifest() {
return !!this.uri;
},
Expand All @@ -271,20 +256,16 @@
return this.resourceCount >= 2;
},
thumbnail() {
return this.resource?.edm.thumbnails?.(this.$nuxt.context)?.large;
},
imageTypeResource() {
return this.resource?.edm.isHTMLImage;
viewableImageResource() {
return this.resource?.edm?.isHTMLImage;
},
addPaginationToolbarMaxWidth() {
return !this.imageTypeResource && this.multiplePages;
return !this.viewableImageResource && this.multiplePages;
},
addSidebarToggleMaxWidth() {
return !this.imageTypeResource && this.sidebarHasContent;
return !this.viewableImageResource && this.sidebarHasContent;
}
},
Expand All @@ -299,6 +280,12 @@
}
},
mounted() {
console.log('IMP resource', this.resource);
console.log('IMP resource.edm', this.resource?.edm);
console.log('IMP resource.edm.preview', this.resource?.edm?.preview);
},
methods: {
handleImageError(error) {
this.$fetchState.error = error;
Expand Down
8 changes: 4 additions & 4 deletions packages/portal/src/components/item/ItemMediaThumbnail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class="item-media-thumbnail text-lowercase text-decoration-none"
>
<MediaCardImage
:media="resource.edm"
:media="resource"
:lazy="lazy"
:offset="offset"
:edm-type="edmType"
Expand All @@ -23,7 +23,7 @@

<script>
import MediaCardImage from '../media/MediaCardImage.vue';
import EuropeanaMediaResource from '@/utils/europeana/media/Resource.js';
import WebResource from '@/plugins/europeana/edm/WebResource.js';
export default {
name: 'ItemMediaThumbnail',
Expand All @@ -33,7 +33,7 @@
},
props: {
resource: {
type: EuropeanaMediaResource,
type: WebResource,
required: true
},
offset: {
Expand Down Expand Up @@ -62,7 +62,7 @@
return this.offset + 1;
},
mediaTypeIconClass() {
const mediaType = this.resource.edm.edmType || this.edmType;
const mediaType = this.resource.edmType || this.edmType;
return mediaType ? `icon-${mediaType.toLowerCase()}-bold` : '';
},
label() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
:offset="firstRenderedResourceIndex + index"
class="d-flex-inline mr-3 mr-lg-auto"
:class="{ 'selected': index === selectedIndex }"
:resource="resource"
:resource="resource.edm"
:edm-type="edmType"
:lazy="true"
/>
Expand Down
17 changes: 15 additions & 2 deletions packages/portal/src/components/media/MediaCardImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@

<script>
import WebResource from '@/plugins/europeana/edm/WebResource.js';
import {
LARGE_WIDTH as LARGE_THUMBNAIL_WIDTH,
SMALL_WIDTH as SMALL_THUMBNAIL_WIDTH
} from '@/plugins/europeana/thumbnail.js';
export default {
name: 'MediaCardImage',
Expand Down Expand Up @@ -107,7 +111,16 @@
return this.$apis.record.mediaProxyUrl(this.media.about, this.europeanaIdentifier, { disposition: 'inline' });
},
thumbnails() {
return this.media.thumbnails(this.$nuxt.context);
if (this.media.svcsHasService) {
// TODO: assess impact of this outside of new ItemMediaPresentation component
const serviceId = this.media.svcsHasService.id || this.media.svcsHasService.about || this.media.svcsHasService;
return {
large: `${serviceId}/full/${LARGE_THUMBNAIL_WIDTH},/0/default.jpg`,
small: `${serviceId}/full/${SMALL_THUMBNAIL_WIDTH},/0/default.jpg`
};
} else {
return this.$apis.thumbnail.forWebResource(this.media);
}
},
thumbnailSrc() {
return this.thumbnails[this.thumbnailSize];
Expand All @@ -116,7 +129,7 @@
if (!this.media.ebucoreWidth) {
return null;
}
const thumbnailMaxSize = this.thumbnailSize === 'large' ? 400 : 200;
const thumbnailMaxSize = this.thumbnailSize === 'large' ? LARGE_THUMBNAIL_WIDTH : SMALL_THUMBNAIL_WIDTH;
if (this.media.ebucoreWidth < thumbnailMaxSize) {
return this.media.ebucoreWidth;
}
Expand Down
68 changes: 9 additions & 59 deletions packages/portal/src/components/media/MediaImageViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
},
props: {
// FIXME: this isn't used; rm
format: {
type: String,
default: null
Expand All @@ -80,10 +81,6 @@
type: EuropeanaMediaService,
default: null
},
thumbnail: {
type: String,
default: null
},
url: {
type: String,
required: true
Expand Down Expand Up @@ -170,9 +167,10 @@
},
methods: {
// FIXME: restore functionality; move up to IMP component?
handleKeyboardToggleKeydown(event) {
if (['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', '-', '+'].includes(event.key)) {
this.fullImageRendered || this.renderFullImage();
this.fullImageRendered || null; // this.renderFullImage();
}
},
Expand Down Expand Up @@ -236,11 +234,7 @@
}
},
async highlightAnnotation() {
if (!this.fullImageRendered) {
await this.renderFullImage();
}
highlightAnnotation() {
this.initOlAnnotationLayer();
const layer = this.olMap.getLayers().item(1);
Expand Down Expand Up @@ -304,47 +298,6 @@
this.configureZoomLevels();
},
async renderThumbnail() {
if (!this.thumbnail) {
this.renderFullImage();
return;
}
let mapOptions;
const thumbWidth = 400;
const thumbHeight = (this.height / this.width) * thumbWidth;
mapOptions = await this.initOlImageLayerStatic(this.thumbnail, thumbWidth, thumbHeight);
this.initOlMap(mapOptions);
this.olMap.getInteractions().forEach((interaction) => interaction.setActive(false));
this.olMap.on('click', this.renderFullImage);
this.olMap.getView().on('change:resolution', this.renderFullImageOnFirstZoomIn);
this.fullImageRendered = false;
},
renderFullImageOnFirstZoomIn(event) {
// check if zoom in, not out
if (event.oldValue < 1) {
this.renderFullImage();
}
},
async renderFullImage() {
if (this.olMap) {
this.olMap.un('click', this.renderFullImage);
this.olMap.getView().un('change:resolution', this.renderFullImageOnFirstZoomIn);
this.olMap.getInteractions().forEach((interaction) => interaction.setActive(true));
}
// TODO: should we always be using the media proxy for static images?
const url = this.$apis.record.mediaProxyUrl(this.url, this.itemId, { disposition: 'inline' });
const mapOptions = await this.initOlImageLayerStatic(url, this.width, this.height);
this.initMapWithFullImage(mapOptions);
},
// IIIF Image API
// https://openlayers.org/en/latest/examples/iiif.html
initOlImageLayerIIIF() {
Expand Down Expand Up @@ -392,17 +345,14 @@
if (this.source === 'IIIF') {
const mapOptions = this.initOlImageLayerIIIF();
this.initMapWithFullImage(mapOptions);
} else if (this.activeAnnotation) {
this.renderFullImage();
this.initOlMap(mapOptions);
} else {
this.renderThumbnail();
// TODO: should we always be using the media proxy for static images?
const url = this.$apis.record.mediaProxyUrl(this.url, this.itemId, { disposition: 'inline' });
const mapOptions = await this.initOlImageLayerStatic(url, this.width, this.height);
this.initOlMap(mapOptions);
}
},
initMapWithFullImage(mapOptions) {
this.initOlMap(mapOptions);
this.fullImageRendered = true;
this.highlightAnnotation();
},
Expand Down
Empty file.
4 changes: 3 additions & 1 deletion packages/portal/src/pages/item/_.vue
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,9 @@
this.iiifPresentationManifest = item.iiifPresentationManifest;
this.isShownAt = item.providerAggregation.edmIsShownAt;
this.ogImage = new WebResource(item.providerAggregation.displayableWebResources[0], this.identifier)?.thumbnails(this.$nuxt.context)?.large;
this.ogImage = this.$apis.thumbnail.forWebResource(
new WebResource(item.providerAggregation.displayableWebResources[0], this.identifier)
).large;
// don't store the web resources when using iiif as the manifest will be used
if (!this.iiifPresentationManifest) {
Expand Down
Loading

0 comments on commit 16597ab

Please sign in to comment.