<template> <div class="media"> <div v-if="release.trailer || release.teaser" class="trailer-container" > <video v-if="release.trailer" :src="`/media/${release.trailer.path}`" :poster="release.poster && (sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`)" :alt="release.title" :class="{ sfw: sfw && paused }" class="item trailer" controls @playing="playing = true; paused = false;" @pause="playing = false; paused = true;" >Sorry, the tailer cannot be played in your browser</video> <video v-else-if="release.teaser && /^video\//.test(release.teaser.mime)" :src="`/media/${release.teaser.path}`" :poster="release.poster && (sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`)" :alt="release.title" :class="{ sfw: sfw && paused }" class="item trailer" controls @playing="playing = true; paused = false;" @pause="playing = false; paused = true;" >Sorry, the tailer cannot be played in your browser</video> <img v-else-if="release.teaser && /^image\//.test(release.teaser.mime)" :src="sfw ? `/img/${release.teaser.sfw.thumbnail}` : `/media/${release.teaser.path}`" :alt="release.title" loading="lazy" class="item trailer" > <a v-if="release.poster" v-tooltip="'View poster'" :href="`/media/${release.poster.path}`" :class="{ playing }" target="_blank" rel="noopener noreferrer" class="poster-link" ><Icon icon="image" /></a> <span v-if="sfw && !playing" class="warning" > <Icon icon="warning2" />NSFW </span> </div> <template v-if="release.covers && release.covers.length > 0"> <a v-for="cover in release.covers" :key="`cover-${cover.id}`" :href="`/media/${cover.path}`" target="_blank" rel="noopener noreferrer" > <img :src="`/media/${cover.thumbnail}`" :style="{ 'background-image': sfw ? `url(/media/${cover.sfw.lazy})` : `url(/media/${cover.lazy})` }" class="item cover" loading="lazy" @load="$emit('load', $event)" > </a> </template> <div v-for="photo in photos" :key="`media-${photo.id}`" class="item-container" > <a :href="`/media/${photo.path}`" :class="{ sfw }" class="item-link" target="_blank" rel="noopener noreferrer" > <img :src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`" :style="{ 'background-image': sfw ? `url(/img/${photo.sfw.lazy})` : `url(/media/${photo.lazy})` }" :alt="`Photo ${photo.index + 1}`" loading="lazy" class="item" @load="$emit('load', $event)" > <span v-if="sfw" class="warning" > <Icon icon="warning2" />NSFW </span> </a> </div> </div> </template> <script> function sfw() { return this.$store.state.ui.sfw; } function photos() { const clips = this.release.clips || []; const clipPostersById = clips.reduce((acc, clip) => ({ ...acc, [clip.poster.id]: clip.poster }), {}); const uniqueClipPosters = Array.from(new Set(clips.map(clip => clip.poster.id) || [])).map(posterId => clipPostersById[posterId]); const photosWithClipPosters = (this.release.photos || []).concat(uniqueClipPosters); if (this.release.trailer || this.release.teaser) { // poster will be on trailer video return photosWithClipPosters; } if (this.release.poster) { // no trailer, add poster to photos return [this.release.poster].concat(photosWithClipPosters); } // no poster available return photosWithClipPosters; } export default { props: { release: { type: Object, default: null, }, expanded: { type: Boolean, default: false, }, test: { type: String, default: null, }, }, data() { return { playing: false, paused: false, }; }, computed: { photos, sfw, }, }; </script> <style lang="scss" scoped> @import 'theme'; .media { flex-shrink: 0; white-space: nowrap; font-size: 0; &.expanded { display: grid; grid-template-columns: repeat(auto-fill, minmax(32rem, 1fr)); grid-gap: 1rem; justify-content: center; margin: 0 0 1rem 0; } } .poster-link { position: absolute; top: .5rem; right: .5rem; transition: opacity .1s ease; .icon { width: 1.5rem; height: 1.5rem; fill: var(--lighten-strong); filter: drop-shadow(0 0 1px var(--darken-weak)); } &.playing { opacity: 0; } &:hover { cursor: pointer; opacity: 1; .icon { fill: var(--text-light); } } } .item-container, .trailer-container { max-width: 100%; position: relative; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; .warning { display: none; width: 100%; height: 100%; flex-direction: column; align-items: center; justify-content: center; position: absolute; background: var(--darken-weak); color: var(--text-light); font-size: 1.2rem; font-weight: bold; text-shadow: 0 0 3px var(--darken-strong); pointer-events: none; animation: alert .5s ease infinite .1s; .icon { display: block; fill: var(--text-light); width: 3rem; height: 3rem; margin: 0 0 .25rem 0; filter: drop-shadow(0 0 3px var(--darken)); animation: alert .5s ease infinite .1s; } } &:hover .warning { display: inline-flex; } } .item { height: 18rem; max-width: 100%; box-shadow: 0 0 3px var(--shadow-weak); background-size: cover; } .trailer-container { height: 18rem; width: 32rem; max-width: 100%; } .trailer { width: 100%; max-height: 100%; object-fit: cover; &.sfw { filter: blur(2rem); } } @keyframes alert { 0% { color: var(--text-light); fill: var(--text-light); } 50% { color: var(--alert); fill: var(--alert); } } @media(max-width: $breakpoint0) { .media:not(.expanded) .item, .trailer-container { height: 56vw; /* 16:9 ratio for full-width video */ } } </style>