add youtube iframe

This commit is contained in:
ordinarthur 2026-04-13 11:02:38 +02:00
parent a7c4aa5608
commit 49dcfb28de
2 changed files with 48 additions and 8 deletions

View File

@ -1,15 +1,17 @@
import { useRef, useState, useCallback } from 'react'
import { Play, Pause, Volume2, VolumeX, SkipBack, SkipForward } from 'lucide-react'
import { Play, Pause, Volume2, VolumeX, SkipBack, SkipForward, Maximize2, Minimize2 } from 'lucide-react'
import { usePlayerStore } from '@/stores/player'
import { formatDuration } from '@/lib/utils'
import { publicUrl } from '@/lib/storage'
import { Avatar } from '@/components/ui/Avatar'
import { isExternalUrl, getEmbedInfo } from '@/lib/embed'
export function PlayerBar() {
const { current, isPlaying, isExternal, progress, duration, volume, toggle, seek, setVolume } = usePlayerStore()
const seekRef = useRef<HTMLDivElement>(null)
const [isDragging, setIsDragging] = useState(false)
const [dragPct, setDragPct] = useState(0)
const [ytExpanded, setYtExpanded] = useState(false)
const calcPct = useCallback((clientX: number) => {
if (!seekRef.current) return 0
@ -19,6 +21,7 @@ export function PlayerBar() {
if (!current) return null
const isYouTube = isExternal && current && isExternalUrl(current.audio_url) && getEmbedInfo(current.audio_url)?.platform === 'youtube'
const pct = duration > 0 ? (progress / duration) * 100 : 0
const displayPct = isDragging ? dragPct : pct
// Allow seeking for native audio and YouTube (via IFrame API)
@ -72,6 +75,28 @@ export function PlayerBar() {
const currentTime = isDragging ? (dragPct / 100) * duration : progress
return (
<>
{/* YouTube player - mini or expanded */}
{isYouTube && (
<div
className={`fixed z-[60] rounded-xl overflow-hidden shadow-[0_4px_20px_rgba(0,0,0,0.3)] transition-all duration-300 ${
ytExpanded
? 'inset-4 bottom-[106px]'
: 'bottom-[106px] right-4'
}`}
style={ytExpanded ? {} : { width: 200, height: 150 }}
>
<div id="yt-player-portal" className="w-full h-full [&_iframe]:!w-full [&_iframe]:!h-full [&_iframe]:!border-0" />
{/* Gradient overlay to hide YouTube title bar */}
<div className="absolute inset-x-0 top-0 h-10 bg-gradient-to-b from-black/80 to-transparent pointer-events-none" />
<button
onClick={() => setYtExpanded(!ytExpanded)}
className="absolute top-2 right-2 z-10 w-8 h-8 rounded-lg bg-black/60 hover:bg-black/80 flex items-center justify-center text-white transition-colors cursor-pointer"
>
{ytExpanded ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
</button>
</div>
)}
<div className="fixed bottom-0 left-0 right-0 z-50 bg-[#1E1B33] text-white shadow-[0_-2px_20px_rgba(0,0,0,0.3)]">
<div className="max-w-6xl mx-auto px-4 sm:px-6">
{/* Main row */}
@ -192,5 +217,6 @@ export function PlayerBar() {
</div>}
</div>
</div>
</>
)
}

View File

@ -50,7 +50,15 @@ function destroyYtPlayer() {
clearProgressInterval()
try { ytPlayer?.destroy() } catch { /* ignore */ }
ytPlayer = null
if (ytContainer) { ytContainer.remove(); ytContainer = null }
if (ytContainer) {
// If it's the portal, just clear contents; otherwise remove from DOM
if (ytContainer.id === 'yt-player-portal') {
ytContainer.innerHTML = ''
} else {
ytContainer.remove()
}
ytContainer = null
}
}
function destroyIframe() {
@ -178,20 +186,26 @@ export const usePlayerStore = create<PlayerState>((set, get) => ({
// Wait for previous save to complete, then fetch saved progress
savePrevPromise.then(() => fetchSavedProgress(podcast.id)).then((savedTime) => {
loadYouTubeAPI().then(() => {
ytContainer = document.createElement('div')
ytContainer.style.cssText = 'position:fixed;width:1px;height:1px;left:-10px;top:-10px;opacity:0;pointer-events:none;'
// Use portal target if available, otherwise create visible container
const portalTarget = document.getElementById('yt-player-portal')
if (portalTarget) {
portalTarget.innerHTML = ''
ytContainer = portalTarget as HTMLDivElement
} else {
ytContainer = document.createElement('div')
ytContainer.style.cssText = 'position:fixed;bottom:90px;right:16px;width:200px;height:200px;z-index:60;border-radius:12px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,0.3);'
document.body.appendChild(ytContainer)
}
const playerDiv = document.createElement('div')
playerDiv.id = 'yt-player-' + Date.now()
ytContainer.appendChild(playerDiv)
document.body.appendChild(ytContainer)
ytPlayer = new window.YT.Player(playerDiv.id, {
videoId: embed.id,
playerVars: {
autoplay: 1,
controls: 0,
disablekb: 1,
fs: 0,
controls: 1,
fs: 1,
modestbranding: 1,
rel: 0,
start: savedTime > 0 ? Math.floor(savedTime) : undefined,