diff --git a/frontend/src/components/Gallery.vue b/frontend/src/components/Gallery.vue index 02b57b0..dd2471f 100644 --- a/frontend/src/components/Gallery.vue +++ b/frontend/src/components/Gallery.vue @@ -118,7 +118,6 @@ defineExpose({ // Wrapping either end, just land outside the list if (Math.abs(d) >= N || Math.sign(d) !== Math.sign(moveto - index)) moveto = N } - console.log("Gallery cursorMove", d, index, moveto, moveto - index) store.cursor = docs[moveto]?.key ?? '' const tr = store.cursor ? document.getElementById(`file-${store.cursor}`) : '' if (select) { diff --git a/frontend/src/components/GalleryFigure.vue b/frontend/src/components/GalleryFigure.vue index f92fab6..46fcdb3 100644 --- a/frontend/src/components/GalleryFigure.vue +++ b/frontend/src/components/GalleryFigure.vue @@ -3,20 +3,17 @@ :class="{ file: !doc.dir, folder: doc.dir, cursor: store.cursor === doc.key }" @contextmenu.stop @focus.stop="store.cursor = doc.key" - @click="ev => { - if (m!.play()) ev.preventDefault() - store.cursor = doc.key - }" + @click=onclick >
- - - - + +
+
+ + {{ doc.name }} +
+
@@ -33,10 +30,15 @@ const props = defineProps<{ index: number }>() const m = ref(null) + +const onclick = (ev: Event) => { + if (m.value!.play()) ev.preventDefault() + store.cursor = props.doc.key +} diff --git a/frontend/src/components/MediaPreview.vue b/frontend/src/components/MediaPreview.vue index 28bd1f8..bd1f5a2 100644 --- a/frontend/src/components/MediaPreview.vue +++ b/frontend/src/components/MediaPreview.vue @@ -2,8 +2,10 @@ - - + +
+ +
@@ -15,20 +17,83 @@ import type { Doc } from '@/repositories/Document' const aud = ref(null) const vid = ref(null) const media = computed(() => aud.value || vid.value) +const poster = computed(() => `${props.doc.previewurl}?${props.quality}&t=${props.doc.mtime}`) const props = defineProps<{ doc: Doc quality: string }>() +const onplay = () => { + if (!media.value) return + media.value.controls = true + media.value.setAttribute('data-playing', '') +} +const onpaused = () => { + if (!media.value) return + media.value.controls = false + media.value.removeAttribute('data-playing') +} +let fscurrent: HTMLVideoElement | null = null +const next = () => { + if (!media.value) return + media.value.load() // Restore poster + const medias = Array.from(document.querySelectorAll('video, audio')) as (HTMLAudioElement | HTMLVideoElement)[] + if (medias.length === 0) return + let el: HTMLAudioElement | HTMLVideoElement | null = null + for (const i in medias) { + if (medias[i] === (fscurrent || media.value)) { + el = medias[+i + 1] || medias[0] + break + } + } + if (!el) return + if (el.tagName === "VIDEO" && document.fullscreenElement === media.value) { + // Fullscreen needs to use the current video element for the next video + // because we are not allowed to fullscreen the next one. + // FIXME: Write our own player to avoid this problem... + const elem = media.value as HTMLVideoElement + const playing = el as HTMLVideoElement + if (elem === playing) { + playing.play() // Only one video, just replay + return + } + if (!fscurrent) { + elem.addEventListener('fullscreenchange', ev => { + if (!fscurrent) return + // Restore the original video element and continue with the one that was playing + fscurrent.currentTime = elem.currentTime + fscurrent.click() + if (!elem.paused) fscurrent.play() + fscurrent = null + elem.src = props.doc.url + elem.poster = poster.value + onpaused() + }, {once: true}) + } + fscurrent = playing + elem.src = playing.src + elem.poster = '' + elem.play() + } else { + document.exitFullscreen() + el.click() + } +} defineExpose({ play() { - if (media.value) { - if (media.value.paused) media.value.play() - else media.value.pause() - return true + if (!media.value) return false + if (media.value.paused) { + media.value.play() + for (const el of Array.from(document.querySelectorAll('video, audio')) as (HTMLAudioElement | HTMLVideoElement)[]) { + if (el === media.value) continue + el.pause() + } + } else { + media.value.pause() } - return false + return true }, + media, }) @@ -44,20 +109,12 @@ const preview = () => (