Update production setup docs with new Spotify secret; enhance video components for fullscreen handling; add Spotify now-playing API module.

This commit is contained in:
Nikita
2025-12-23 13:54:34 -08:00
parent 244aa9324a
commit 4a6b510a5e
6 changed files with 418 additions and 36 deletions

View File

@@ -1,3 +1,4 @@
import { useEffect, useRef } from "react"
import { Stream } from "@cloudflare/stream-react"
type CloudflareStreamPlayerProps = {
@@ -15,23 +16,68 @@ export function CloudflareStreamPlayer({
muted = false,
onReady,
}: CloudflareStreamPlayerProps) {
const containerRef = useRef<HTMLDivElement>(null)
const handleReady = () => {
onReady?.()
}
useEffect(() => {
const container = containerRef.current
if (!container) return
const ensureFullscreenAllow = () => {
const iframe = container.querySelector("iframe")
if (!iframe) return false
const allow = iframe.getAttribute("allow") ?? ""
const parts = allow
.split(";")
.map((part) => part.trim())
.filter(Boolean)
if (!parts.includes("fullscreen")) {
parts.push("fullscreen")
iframe.setAttribute("allow", parts.join("; "))
}
if (!iframe.hasAttribute("allowfullscreen")) {
iframe.setAttribute("allowfullscreen", "")
}
return true
}
if (ensureFullscreenAllow()) {
return
}
if (typeof MutationObserver === "undefined") {
return
}
const observer = new MutationObserver(() => {
if (ensureFullscreenAllow()) {
observer.disconnect()
}
})
observer.observe(container, { childList: true, subtree: true })
return () => observer.disconnect()
}, [uid, customerCode])
return (
<Stream
className="h-full w-full"
src={uid}
customerCode={customerCode}
controls
autoplay={autoPlay}
muted={muted}
responsive={false}
height="100%"
width="100%"
onCanPlay={handleReady}
onPlaying={handleReady}
/>
<div ref={containerRef} className="h-full w-full">
<Stream
className="h-full w-full"
src={uid}
customerCode={customerCode}
controls
autoplay={autoPlay}
muted={muted}
responsive={false}
height="100%"
width="100%"
onCanPlay={handleReady}
onPlaying={handleReady}
/>
</div>
)
}

View File

@@ -12,6 +12,7 @@ export function VideoPlayer({
autoPlay = true,
muted = false,
}: VideoPlayerProps) {
const containerRef = useRef<HTMLDivElement>(null)
const videoRef = useRef<HTMLVideoElement>(null)
const hlsRef = useRef<Hls | null>(null)
const [isPlaying, setIsPlaying] = useState(autoPlay)
@@ -150,7 +151,8 @@ export function VideoPlayer({
const handleFullscreen = async () => {
const video = videoRef.current
if (!video) return
const container = containerRef.current
if (!video || !container) return
const doc = document as Document & {
webkitFullscreenElement?: Element | null
@@ -162,6 +164,9 @@ export function VideoPlayer({
webkitRequestFullscreen?: () => Promise<void> | void
webkitDisplayingFullscreen?: boolean
}
const containerEl = container as HTMLElement & {
webkitRequestFullscreen?: () => Promise<void> | void
}
const isDocFullscreen = !!doc.fullscreenElement || !!doc.webkitFullscreenElement
const isVideoFullscreen = !!videoEl.webkitDisplayingFullscreen
@@ -184,15 +189,40 @@ export function VideoPlayer({
return
}
if (video.requestFullscreen) {
await video.requestFullscreen()
setIsFullscreen(true)
} else if (videoEl.webkitRequestFullscreen) {
await videoEl.webkitRequestFullscreen()
setIsFullscreen(true)
} else if (videoEl.webkitEnterFullscreen) {
videoEl.webkitEnterFullscreen()
setIsFullscreen(true)
const requestContainerFullscreen = async () => {
if (containerEl.requestFullscreen) {
await containerEl.requestFullscreen()
return true
}
if (containerEl.webkitRequestFullscreen) {
await containerEl.webkitRequestFullscreen()
return true
}
return false
}
try {
if (await requestContainerFullscreen()) {
setIsFullscreen(true)
return
}
} catch {
// Fall through to video fullscreen methods.
}
try {
if (video.requestFullscreen) {
await video.requestFullscreen()
setIsFullscreen(true)
} else if (videoEl.webkitRequestFullscreen) {
await videoEl.webkitRequestFullscreen()
setIsFullscreen(true)
} else if (videoEl.webkitEnterFullscreen) {
videoEl.webkitEnterFullscreen()
setIsFullscreen(true)
}
} catch {
// Ignore fullscreen errors to avoid breaking playback.
}
}
@@ -216,6 +246,7 @@ export function VideoPlayer({
return (
<div
ref={containerRef}
className="group relative h-full w-full bg-black"
onMouseMove={handleMouseMove}
onMouseLeave={() => isPlaying && setShowControls(false)}