- Add StreamEmbed component supporting Twitch and YouTube embeds with live badge, platform icon, aspect-ratio 16:9, external link - Replace AudioPlayer on AdminHome with StreamEmbed (configure channel name in streamConfig ref) - Move course progress % to bottom-right footer corner alongside price/cert badge; remove separate progress row above footer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
3.9 KiB
Vue
114 lines
3.9 KiB
Vue
<template>
|
|
<div class="rounded-xl overflow-hidden border border-outline-gray-2 bg-surface-gray-9">
|
|
<!-- Stream header -->
|
|
<div class="flex items-center justify-between px-4 py-3 bg-surface-gray-9 border-b border-outline-gray-7">
|
|
<div class="flex items-center gap-2.5">
|
|
<!-- Live badge -->
|
|
<span
|
|
v-if="isLive"
|
|
class="flex items-center gap-1.5 px-2 py-0.5 rounded text-[11px] font-bold uppercase tracking-wide bg-red-600 text-white"
|
|
>
|
|
<span class="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
|
|
LIVE
|
|
</span>
|
|
<span
|
|
v-else
|
|
class="flex items-center gap-1.5 px-2 py-0.5 rounded text-[11px] font-semibold uppercase tracking-wide bg-surface-gray-7 text-ink-gray-4"
|
|
>
|
|
OFFLINE
|
|
</span>
|
|
|
|
<!-- Platform icon + channel -->
|
|
<div class="flex items-center gap-1.5">
|
|
<!-- Twitch icon -->
|
|
<svg v-if="platform === 'twitch'" class="w-4 h-4 text-purple-400" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z"/>
|
|
</svg>
|
|
<!-- YouTube icon -->
|
|
<svg v-else-if="platform === 'youtube'" class="w-4 h-4 text-red-500" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
|
|
</svg>
|
|
|
|
<span class="text-sm font-semibold text-white">{{ channelDisplay }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Open in new tab -->
|
|
<a
|
|
:href="externalUrl"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="flex items-center gap-1 text-xs text-ink-gray-4 hover:text-white transition-colors"
|
|
>
|
|
<ExternalLink class="w-3.5 h-3.5" />
|
|
<span class="hidden sm:inline">{{ __('Open') }}</span>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Embed iframe -->
|
|
<div class="relative w-full" style="aspect-ratio: 16/9">
|
|
<iframe
|
|
v-if="embedUrl"
|
|
:src="embedUrl"
|
|
class="absolute inset-0 w-full h-full"
|
|
frameborder="0"
|
|
allowfullscreen
|
|
allow="autoplay; fullscreen"
|
|
:title="channelDisplay"
|
|
/>
|
|
<!-- Placeholder when no channel configured -->
|
|
<div
|
|
v-else
|
|
class="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-surface-gray-9"
|
|
>
|
|
<Radio class="w-12 h-12 text-ink-gray-6 stroke-1" />
|
|
<p class="text-sm text-ink-gray-5">{{ __('Stream channel not configured') }}</p>
|
|
<p class="text-xs text-ink-gray-4">{{ __('Set channel prop to a Twitch or YouTube channel') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { ExternalLink, Radio } from 'lucide-vue-next'
|
|
|
|
const props = withDefaults(defineProps<{
|
|
/** 'twitch' or 'youtube' */
|
|
platform?: 'twitch' | 'youtube'
|
|
/** Twitch: channel name. YouTube: video/stream ID or channel handle */
|
|
channel?: string
|
|
/** Show as live */
|
|
isLive?: boolean
|
|
}>(), {
|
|
platform: 'twitch',
|
|
channel: '',
|
|
isLive: true,
|
|
})
|
|
|
|
const channelDisplay = computed(() => props.channel || '—')
|
|
|
|
const embedUrl = computed(() => {
|
|
if (!props.channel) return ''
|
|
|
|
if (props.platform === 'twitch') {
|
|
const parent = window.location.hostname
|
|
return `https://player.twitch.tv/?channel=${props.channel}&parent=${parent}&autoplay=false`
|
|
}
|
|
|
|
if (props.platform === 'youtube') {
|
|
// Supports both video ID and live stream
|
|
return `https://www.youtube.com/embed/${props.channel}?autoplay=0&rel=0`
|
|
}
|
|
|
|
return ''
|
|
})
|
|
|
|
const externalUrl = computed(() => {
|
|
if (!props.channel) return '#'
|
|
if (props.platform === 'twitch') return `https://twitch.tv/${props.channel}`
|
|
if (props.platform === 'youtube') return `https://youtube.com/watch?v=${props.channel}`
|
|
return '#'
|
|
})
|
|
</script>
|