import React, { useState, useEffect } from 'react' import { usePlaybackContinuity } from '../hooks/usePlaybackContinuity' import { PlaybackContinuityIndicator } from './PlaybackContinuityIndicator' import { MediaItem } from '../api/jellyfin' import './PlaybackContinuityDemo.css' export const PlaybackContinuityDemo: React.FC = () => { const { playbackState, connectionState, isOnline, connectionQuality, updatePlaybackState, syncPlaybackPosition, getSmartFallbackUrl, seamlessHandoff, canPlayOffline, getEstimatedOfflineTime } = usePlaybackContinuity() const [demoTrack] = useState({ Id: 'demo-track-123', Name: 'Demo Track', Artists: ['Demo Artist'], Album: 'Demo Album' } as MediaItem) const [position, setPosition] = useState(0) const [isPlaying, setIsPlaying] = useState(false) const [volume, setVolume] = useState(0.8) const [fallbackResult, setFallbackResult] = useState('') // Simulate playback position updates useEffect(() => { let interval: NodeJS.Timeout | null = null if (isPlaying) { interval = setInterval(() => { setPosition(prev => { const newPosition = prev + 1 syncPlaybackPosition(demoTrack.Id, newPosition) return newPosition }) }, 1000) } return () => { if (interval) clearInterval(interval) } }, [isPlaying, demoTrack.Id, syncPlaybackPosition]) const handlePlay = () => { setIsPlaying(true) updatePlaybackState({ trackId: demoTrack.Id, position, isPlaying: true, volume, queue: [demoTrack], currentIndex: 0, shuffle: false, repeat: 'off', timestamp: Date.now(), deviceId: '' }) } const handlePause = () => { setIsPlaying(false) updatePlaybackState({ trackId: demoTrack.Id, position, isPlaying: false, volume, queue: [demoTrack], currentIndex: 0, shuffle: false, repeat: 'off', timestamp: Date.now(), deviceId: '' }) } const handleSeek = (newPosition: number) => { setPosition(newPosition) syncPlaybackPosition(demoTrack.Id, newPosition) } const testFallback = async () => { try { const mockAudioStorage = { getPlayableUrl: async () => ({ url: 'blob://cached-demo', type: 'audio' }) } const mockApi = { getStreamUrl: () => 'https://demo-server/stream' } const result = await getSmartFallbackUrl(demoTrack.Id, mockAudioStorage, mockApi, 320000) setFallbackResult(`Source: ${result.source}, URL: ${result.url || result.error}`) } catch (error) { setFallbackResult(`Error: ${error}`) } } const simulateOffline = () => { // Simulate going offline Object.defineProperty(navigator, 'onLine', { value: false, configurable: true }) window.dispatchEvent(new Event('offline')) } const simulateOnline = () => { // Simulate going online Object.defineProperty(navigator, 'onLine', { value: true, configurable: true }) window.dispatchEvent(new Event('online')) } const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins}:${secs.toString().padStart(2, '0')}` } return (

🎡 Playback Continuity System Demo

{/* Connection Status */}

Connection Status

{/* Playback Controls */}

Playback Controls

{demoTrack.Name}
{demoTrack.Artists?.[0]}
{formatTime(position)} handleSeek(Number(e.target.value))} className="position-slider" /> 3:00
πŸ”Š setVolume(Number(e.target.value))} className="volume-slider" />
{/* Smart Fallback Test */}

Smart Fallback Logic

{fallbackResult && (
Result: {fallbackResult}
)}
{/* Playback State */}

Current Playback State

{playbackState ? (
{playbackState.trackId}
{playbackState.position.toFixed(3)}s
{playbackState.isPlaying ? 'βœ…' : '❌'}
{(playbackState.volume * 100).toFixed(0)}%
{playbackState.deviceId.slice(-8)}
{new Date(playbackState.timestamp).toLocaleTimeString()}
) : (
No playback state available
)}
{/* Offline Capabilities */}

Offline Capabilities

{Math.floor(getEstimatedOfflineTime() / 60)} minutes
{canPlayOffline(demoTrack.Id) ? 'βœ…' : '❌'}
{/* Connection Details */}

Connection Details

{isOnline ? '🌐 Online' : 'πŸ“± Offline'}
{connectionQuality.charAt(0).toUpperCase() + connectionQuality.slice(1)}
{new Date(connectionState.lastOnlineTime).toLocaleTimeString()}
) } export default PlaybackContinuityDemo