test / client /src /Player.jsx
akborana4's picture
Update client/src/Player.jsx
ce63e50 verified
import React, { useEffect, useMemo, useState } from 'react';
import ReactPlayer from 'react-player';
import { getThumb, safeTitle } from './utils.js';
import { useToasts } from './Toasts.jsx';
export default function Player({ socket, roomId, state, isHost }) {
const { push } = useToasts();
const [useProxy, setUseProxy] = useState(false);
const mediaUrl = useMemo(() => {
if (!state?.track?.url) return '';
return useProxy
? `/api/proxy?url=${encodeURIComponent(state.track.url)}`
: state.track.url;
}, [state?.track?.url, useProxy]);
const title = state?.track ? safeTitle(state.track) : 'No track selected';
const thumb = state?.track?.thumb || getThumb(state?.track?.meta);
// Auto-advance when ended (host only)
const handleEnded = () => {
if (isHost) socket.emit('ended', { roomId });
};
const handleError = (e) => {
console.warn('ReactPlayer error', e);
if (!useProxy) {
setUseProxy(true);
push('CORS blocked direct URL, retrying via proxy…', 'warn', 3000);
} else {
push('Playback failed (even via proxy)', 'bad', 4000);
}
};
return (
<div style={{ position: 'relative' }}>
<div
className="hero-thumb"
style={{ backgroundImage: thumb ? `url("${thumb}")` : undefined }}
aria-hidden
/>
<div className="ambient"></div>
<div className="now-playing" style={{ marginBottom: 10 }}>
<div
className="thumb"
style={{
width: 72,
height: 72,
backgroundImage: thumb ? `url("${thumb}")` : undefined
}}
/>
<div style={{ minWidth: 0, flex: 1 }}>
<div
style={{
fontWeight: 700,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}
>
{title}
</div>
<div
style={{
fontSize: 12,
color: 'var(--muted)',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}
>
{state?.track?.meta?.artists?.join?.(', ') ||
state?.track?.meta?.artist ||
state?.track?.meta?.album ||
state?.track?.kind?.toUpperCase()}
</div>
</div>
{!isHost && <div className="tag">Listener</div>}
</div>
{state?.track?.url ? (
<ReactPlayer
url={mediaUrl}
playing={state.isPlaying}
controls={isHost}
onEnded={handleEnded}
onError={handleError}
width="100%"
height={state.track.kind === 'video' ? '56vh' : '50px'}
config={{
file: {
attributes: {
crossOrigin: 'anonymous'
}
}
}}
/>
) : (
<div style={{ color: 'var(--muted)', padding: '12px 0' }}>
No track selected
</div>
)}
{useProxy && (
<div style={{ marginTop: 8, fontSize: 12, color: 'var(--muted)' }}>
Using proxy due to CORS on the original media URL.
</div>
)}
</div>
);
}