akborana4 commited on
Commit
5dd991b
·
verified ·
1 Parent(s): f90b43a

Update client/src/Player.jsx

Browse files
Files changed (1) hide show
  1. client/src/Player.jsx +38 -32
client/src/Player.jsx CHANGED
@@ -1,9 +1,10 @@
1
  import React, { useEffect, useMemo, useRef, useState } from 'react';
2
- import { detectMediaTypeFromUrl, safeTitle } from './utils.js';
3
  import { log } from './logger.js';
4
  import { useToasts } from './Toasts.jsx';
 
5
 
6
- export default function Player({ socket, roomId, state }) {
7
  const { push } = useToasts();
8
  const audioRef = useRef(null);
9
  const videoRef = useRef(null);
@@ -12,10 +13,10 @@ export default function Player({ socket, roomId, state }) {
12
  const mediaType = useMemo(() => {
13
  if (!state?.track?.url) return 'none';
14
  if (state?.track?.kind) return state.track.kind;
15
- return detectMediaTypeFromUrl(state.track.url) || 'audio';
 
16
  }, [state?.track?.url, state?.track?.kind]);
17
 
18
- // Computee desired current time
19
  const logicalTime = () => {
20
  if (!state) return 0;
21
  const { anchor, anchorAt, isPlaying } = state;
@@ -24,7 +25,6 @@ export default function Player({ socket, roomId, state }) {
24
  return Math.max(0, (anchor || 0) + elapsed);
25
  };
26
 
27
- // Common load for media element
28
  const loadInto = async (el) => {
29
  if (!el || !state?.track?.url) return;
30
  el.src = state.track.url;
@@ -32,11 +32,7 @@ export default function Player({ socket, roomId, state }) {
32
  el.load();
33
  const target = logicalTime();
34
  el.currentTime = Number.isFinite(target) ? target : 0;
35
- if (state.isPlaying) {
36
- await el.play();
37
- } else {
38
- el.pause();
39
- }
40
  setErr(null);
41
  } catch (e) {
42
  log.error('Playback error for', state.track.url, e);
@@ -45,27 +41,25 @@ export default function Player({ socket, roomId, state }) {
45
  }
46
  };
47
 
48
- // Track/url change
49
  useEffect(() => {
50
- const el = mediaType === 'video' ? videoRef.current : audioRef.current;
51
- if (el) loadInto(el);
 
 
 
 
 
52
  // eslint-disable-next-line react-hooks/exhaustive-deps
53
  }, [state?.track?.url, mediaType]);
54
 
55
- // Respond to play/pause/seek from server
56
  useEffect(() => {
 
57
  const el = mediaType === 'video' ? videoRef.current : audioRef.current;
58
  if (!el) return;
59
  const target = logicalTime();
60
  const drift = target - el.currentTime;
61
- if (Math.abs(drift) > 0.3) {
62
- el.currentTime = Math.max(0, target);
63
- }
64
- if (state.isPlaying && el.paused) {
65
- el.play().catch(e => {
66
- log.warn('Autoplay blocked or error', e);
67
- });
68
- }
69
  if (!state.isPlaying && !el.paused) el.pause();
70
  // eslint-disable-next-line react-hooks/exhaustive-deps
71
  }, [state.isPlaying, state.anchor, state.anchorAt, mediaType]);
@@ -73,27 +67,39 @@ export default function Player({ socket, roomId, state }) {
73
  useEffect(() => {
74
  const a = audioRef.current;
75
  const v = videoRef.current;
76
- const onEnded = () => {};
77
  a?.addEventListener('ended', onEnded);
78
  v?.addEventListener('ended', onEnded);
79
  return () => {
80
  a?.removeEventListener('ended', onEnded);
81
  v?.removeEventListener('ended', onEnded);
82
  };
83
- }, []);
 
 
 
84
 
85
  return (
86
- <div>
87
- <div style={{ marginBottom: 8, display:'flex', alignItems:'center', justifyContent:'space-between', gap:8 }}>
88
- <div style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
89
- {state?.track ? safeTitle(state.track) : 'No track selected'}
 
 
 
 
 
 
90
  </div>
91
- <div className="tag">{mediaType === 'video' ? 'Video' : mediaType === 'audio' ? 'Audio' : 'Unknown'}</div>
92
  </div>
93
- {mediaType === 'video' ? (
94
- <video ref={videoRef} controls playsInline style={{ width:'100%', maxHeight: '56vh', background:'#000' }} crossOrigin="anonymous" />
 
 
 
95
  ) : (
96
- <audio ref={audioRef} controls style={{ width: '100%' }} crossOrigin="anonymous" />
97
  )}
98
  {err && <div style={{ marginTop:8, color:'var(--bad)' }}>{err}</div>}
99
  </div>
 
1
  import React, { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { detectMediaTypeFromUrl, getThumb, safeTitle } from './utils.js';
3
  import { log } from './logger.js';
4
  import { useToasts } from './Toasts.jsx';
5
+ import YouTubePlayer from './YouTubePlayer.jsx';
6
 
7
+ export default function Player({ socket, roomId, state, isHost }) {
8
  const { push } = useToasts();
9
  const audioRef = useRef(null);
10
  const videoRef = useRef(null);
 
13
  const mediaType = useMemo(() => {
14
  if (!state?.track?.url) return 'none';
15
  if (state?.track?.kind) return state.track.kind;
16
+ const detected = detectMediaTypeFromUrl(state.track.url);
17
+ return detected || 'audio';
18
  }, [state?.track?.url, state?.track?.kind]);
19
 
 
20
  const logicalTime = () => {
21
  if (!state) return 0;
22
  const { anchor, anchorAt, isPlaying } = state;
 
25
  return Math.max(0, (anchor || 0) + elapsed);
26
  };
27
 
 
28
  const loadInto = async (el) => {
29
  if (!el || !state?.track?.url) return;
30
  el.src = state.track.url;
 
32
  el.load();
33
  const target = logicalTime();
34
  el.currentTime = Number.isFinite(target) ? target : 0;
35
+ if (state.isPlaying) await el.play(); else el.pause();
 
 
 
 
36
  setErr(null);
37
  } catch (e) {
38
  log.error('Playback error for', state.track.url, e);
 
41
  }
42
  };
43
 
 
44
  useEffect(() => {
45
+ if (mediaType === 'audio') {
46
+ const el = audioRef.current;
47
+ if (el) loadInto(el);
48
+ } else if (mediaType === 'video') {
49
+ const el = videoRef.current;
50
+ if (el) loadInto(el);
51
+ }
52
  // eslint-disable-next-line react-hooks/exhaustive-deps
53
  }, [state?.track?.url, mediaType]);
54
 
 
55
  useEffect(() => {
56
+ if (mediaType === 'youtube') return;
57
  const el = mediaType === 'video' ? videoRef.current : audioRef.current;
58
  if (!el) return;
59
  const target = logicalTime();
60
  const drift = target - el.currentTime;
61
+ if (Math.abs(drift) > 0.3) el.currentTime = Math.max(0, target);
62
+ if (state.isPlaying && el.paused) el.play().catch(e => log.warn('Autoplay blocked or error', e));
 
 
 
 
 
 
63
  if (!state.isPlaying && !el.paused) el.pause();
64
  // eslint-disable-next-line react-hooks/exhaustive-deps
65
  }, [state.isPlaying, state.anchor, state.anchorAt, mediaType]);
 
67
  useEffect(() => {
68
  const a = audioRef.current;
69
  const v = videoRef.current;
70
+ const onEnded = () => { if (isHost) socket.emit('ended', { roomId }); };
71
  a?.addEventListener('ended', onEnded);
72
  v?.addEventListener('ended', onEnded);
73
  return () => {
74
  a?.removeEventListener('ended', onEnded);
75
  v?.removeEventListener('ended', onEnded);
76
  };
77
+ }, [socket, roomId, isHost]);
78
+
79
+ const title = state?.track ? safeTitle(state.track) : 'No track selected';
80
+ const thumb = state?.track?.thumb || getThumb(state?.track?.meta);
81
 
82
  return (
83
+ <div style={{ position:'relative' }}>
84
+ <div className="ambient"></div>
85
+
86
+ <div className="now-playing" style={{ marginBottom: 10 }}>
87
+ <div className="thumb" style={{ width:64, height:64, backgroundImage: thumb ? `url("${thumb}")` : undefined }} />
88
+ <div style={{ minWidth:0, flex:1 }}>
89
+ <div style={{ fontWeight:700, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{title}</div>
90
+ <div style={{ fontSize:12, color:'var(--muted)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
91
+ {state?.track?.meta?.artists?.join?.(', ') || state?.track?.meta?.artist || state?.track?.meta?.album || state?.track?.kind?.toUpperCase()}
92
+ </div>
93
  </div>
94
+ {!isHost && <div className="tag">Listener</div>}
95
  </div>
96
+
97
+ {mediaType === 'youtube' ? (
98
+ <YouTubePlayer socket={socket} roomId={roomId} state={state} isHost={isHost} />
99
+ ) : mediaType === 'video' ? (
100
+ <video ref={videoRef} controls={isHost} playsInline style={{ width:'100%', maxHeight: '56vh', background:'#000' }} crossOrigin="anonymous" />
101
  ) : (
102
+ <audio ref={audioRef} controls={isHost} style={{ width: '100%' }} crossOrigin="anonymous" />
103
  )}
104
  {err && <div style={{ marginTop:8, color:'var(--bad)' }}>{err}</div>}
105
  </div>