forked from BRETT/BRETT
- Audio/video attachments render as media cards (cover or icon, title/artist/size) instead of plain file chips; image attachments use 3:2 thumbnails matching the audio card height - Players open in a Popover (autoplay on open) with a dimming overlay; AudioPlayerDialog/VideoPlayerDialog removed - New useMediaVolume composable shares volume/mute between all audio players and Plyr via the 4brett-player storage key - Player buttons and Plyr controls follow the theme border radius - Portrait videos: aspect is applied even when metadata loaded before hydration, and the raw video is height-capped pre-JS Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
68 lines
1.6 KiB
Vue
68 lines
1.6 KiB
Vue
<script setup lang="ts">
|
|
import type Plyr from 'plyr'
|
|
import 'plyr/dist/plyr.css'
|
|
|
|
defineProps<{
|
|
src: string
|
|
autoplay?: boolean
|
|
}>()
|
|
|
|
const videoEl = ref<HTMLVideoElement | null>(null)
|
|
const aspect = ref<number | null>(null)
|
|
let player: Plyr | null = null
|
|
|
|
onMounted(async () => {
|
|
if (!videoEl.value) return
|
|
// SSR: Metadaten können vor der Hydration schon geladen sein —
|
|
// das loadedmetadata-Event feuert dann nicht mehr
|
|
if (videoEl.value.readyState >= HTMLMediaElement.HAVE_METADATA) {
|
|
onLoadedMetadata()
|
|
}
|
|
const { default: PlyrClass } = await import('plyr')
|
|
player = new PlyrClass(videoEl.value, {
|
|
disableContextMenu: false,
|
|
storage: { enabled: true, key: '4brett-player' },
|
|
})
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
player?.destroy()
|
|
player = null
|
|
})
|
|
|
|
function onLoadedMetadata() {
|
|
const v = videoEl.value
|
|
if (v?.videoWidth && v.videoHeight) {
|
|
aspect.value = v.videoWidth / v.videoHeight
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="video-player mx-auto w-full max-w-4xl"
|
|
:style="aspect ? { maxWidth: `min(100%, 56rem, calc(80vh * ${aspect}))` } : undefined"
|
|
>
|
|
<video
|
|
ref="videoEl"
|
|
:src="src"
|
|
:autoplay="autoplay"
|
|
playsinline
|
|
controls
|
|
class="mx-auto max-h-[80vh] w-auto max-w-full rounded-md"
|
|
@loadedmetadata="onLoadedMetadata"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.video-player {
|
|
--plyr-color-main: var(--accent);
|
|
--plyr-border-radius: var(--radius-md);
|
|
}
|
|
.video-player :deep(.plyr__control--overlaid),
|
|
.video-player :deep(.plyr__control) {
|
|
border-radius: var(--radius-md);
|
|
}
|
|
</style>
|