Files
BRETT/app/components/VideoPlayer.vue
T
FliesentischKingandDevin fe9e723c72 Media attachment cards with popover players and shared volume
- 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>
2026-10-04 03:06:11 +02:00

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>