-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiff.patch
More file actions
199 lines (192 loc) · 8.72 KB
/
Copy pathdiff.patch
File metadata and controls
199 lines (192 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
commit 0430a0d6444efdb0833a4e5b7a72f849f9697474
Author: Flaxmbot <rdxdevbhai76@gmail.com>
Date: Sat Jun 6 09:16:39 2026 +0530
check
diff --git a/src/components/Player.jsx b/src/components/Player.jsx
index ba35577..59ee65d 100644
--- a/src/components/Player.jsx
+++ b/src/components/Player.jsx
@@ -246,6 +246,84 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
setFullScreen(false);
};
+ const fetchDynamicNextTrack = useCallback(async (seedId, preferredTracks = []) => {
+ if (!autoplayRef.current || !isOnline || prefetchingRef.current) return [];
+ prefetchingRef.current = true;
+ const lyricOffsetRef = useRef(lyricOffset);
+ useEffect(() => { lyricOffsetRef.current = lyricOffset; }, [lyricOffset]);
+ const [duration, setDuration] = useState(0);
+ const [vol, setVol] = useState(0.8);
+ const [muted, setMuted] = useState(false);
+ const [repeat, setRepeat] = useState(false);
+ const [shuffle, setShuffle] = useState(false);
+ const [loading, _setLoading] = useState(false);
+ const loadingRef = useRef(false);
+ const setLoading = useCallback((v) => {
+ _setLoading(v);
+ loadingRef.current = v;
+ if (onLoadingChange) onLoadingChange(v);
+ }, [onLoadingChange]);
+
+ const currentRef = useRef(0);
+ const durationRef = useRef(0);
+ useEffect(() => { durationRef.current = duration; }, [duration]);
+ const trackRef = useRef(null);
+ useEffect(() => { trackRef.current = track; }, [track]);
+
+ // Refs that always point to the latest next/prev callbacks.
+ // Native listeners (onEnded, onNext, onPrev) call these refs so they
+ // never hold a stale closure regardless of when the listener was registered.
+ const nextRef = useRef(null);
+ const prevRef = useRef(null);
+
+ // Track whether the JS side initiated the last play/pause command so we
+ // can ignore the echoed onIsPlayingChanged event from the native side.
+ // Without this, every setPlaying(true/false) call triggers a second
+ // setPlaying() from the native event, causing uneven button state.
+ const jsInitiatedPlaybackRef = useRef(false);
+
+ const [streamUrl, setStreamUrl] = useState('');
+ const [fullScreen, setFullScreen] = useState(false);
+ const [downloading, setDownloading] = useState(false);
+ const [downloadProgress, setDownloadProgress] = useState(0);
+ const [showLyrics, setShowLyrics] = useState(false);
+ const [loadingLyrics, setLoadingLyrics] = useState(false);
+ // Store lyrics in a ref ΓÇö updateDOMTime reads it directly without stale closures
+ // and without triggering re-renders on every timeupdate tick.
+ const lrcLinesRef = useRef([]);
+ const [lrcLines, _setLrcLines] = useState([]); // only used for rendering the lyrics view
+ const setLrcLines = useCallback((lines) => {
+ lrcLinesRef.current = lines;
+ _setLrcLines(lines);
+ }, []);
+ const [touchY, setTouchY] = useState(0);
+ const [touchX, setTouchX] = useState(0);
+ const [dragOffset, setDragOffset] = useState(0);
+ const [showPlaylistPicker, setShowPlaylistPicker] = useState(false);
+ const [newPlName, setNewPlName] = useState('');
+ const [dominantColor, setDominantColor] = useState('#080808');
+
+ // Modals state
+ const [showEqModal, setShowEqModal] = useState(false);
+ const [showSleepModal, setShowSleepModal] = useState(false);
+ const [activeEq, setActiveEq] = useState('flat');
+
+ const handleEqSelect = (preset) => {
+ // Disabled functionality, EQ coming soon
+ showToast('Equalizer is coming soon!');
+ };
+
+ // Keep refs in sync so callbacks always see latest values
+ useEffect(() => { queueRef.current = queue; }, [queue]);
+ useEffect(() => { qIdxRef.current = qIdx; }, [qIdx]);
+ useEffect(() => { downloadsRef.current = downloads; }, [downloads]);
+ useEffect(() => { recentRef.current = recent; }, [recent]);
+ useEffect(() => { autoplayRef.current = autoplay; }, [autoplay]);
+
+ const closeFullScreen = () => {
+ setFullScreen(false);
+ };
+
const fetchDynamicNextTrack = useCallback(async (seedId, preferredTracks = []) => {
if (!autoplayRef.current || !isOnline || prefetchingRef.current) return [];
prefetchingRef.current = true;
@@ -255,11 +333,10 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
const existingIds = new Set(q.map(t => t.id));
const freshTracks = (tracks || []).filter(r => r?.id && !existingIds.has(r.id)).slice(0, limit);
if (freshTracks.length > 0) {
- setQueue(prev => {
- const nextQueue = [...prev, ...freshTracks.filter(r => !prev.some(t => t.id === r.id))];
- queueRef.current = nextQueue;
- return nextQueue;
- });
+ const prev = queueRef.current || [];
+ const nextQueue = [...prev, ...freshTracks.filter(r => !prev.some(t => t.id === r.id))];
+ queueRef.current = nextQueue;
+ setQueue(nextQueue);
}
return freshTracks;
};
@@ -396,11 +473,13 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
return null;
}, [getPlaybackUrlForTrack]);
- const preloadQueueWindow = useCallback(async () => {
- if (!isOnline || queueRef.current.length === 0) return;
+ const preloadQueueWindow = useCallback(async (overrideQueue, overrideIdx) => {
+ if (!isOnline) return;
- let q = queueRef.current;
- const idx = qIdxRef.current;
+ let q = overrideQueue || queueRef.current;
+ if (q.length === 0) return;
+ const idx = overrideIdx !== undefined ? overrideIdx : qIdxRef.current;
+
if (autoplayRef.current && !shuffle && q.length - idx <= PRELOAD_NEXT_COUNT + 1) {
await fetchDynamicNextTrack(q[idx]?.id);
q = queueRef.current;
@@ -722,8 +801,11 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
return;
}
const nxt = shuffle ? Math.floor(Math.random() * q.length) : (idx + 1) % q.length;
+ qIdxRef.current = nxt;
+ trackRef.current = q[nxt];
setQIdx(nxt); setTrack(q[nxt]); setPlaying(true);
- }, [fetchDynamicNextTrack, repeat, shuffle]);
+ preloadQueueWindow(q, nxt);
+ }, [fetchDynamicNextTrack, repeat, shuffle, preloadQueueWindow]);
// Keep nextRef always pointing to the latest next callback so native
// listeners (onEnded, onNext) never hold a stale closure.
@@ -775,11 +857,14 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
} else {
const idx = qIdxRef.current;
const p = (idx - 1 + q.length) % q.length;
+ qIdxRef.current = p;
+ trackRef.current = q[p];
setQIdx(p); setTrack(q[p]); setPlaying(true);
+ preloadQueueWindow(q, p);
}
};
doPrev();
- }, []);
+ }, [preloadQueueWindow]);
// Keep prevRef always pointing to the latest prev callback.
useEffect(() => { prevRef.current = prev; }, [prev]);
@@ -900,8 +985,6 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
}
}));
listeners.push(PlugdPlayer.addListener('onEnded', () => {
- // Use ref so we always call the latest next() regardless of when
- // this listener was registered.
if (nextRef.current) nextRef.current();
}));
listeners.push(PlugdPlayer.addListener('onNext', () => {
@@ -911,17 +994,8 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
if (prevRef.current) prevRef.current(true);
}));
listeners.push(PlugdPlayer.addListener('onIsPlayingChanged', (res) => {
- // This is a genuine external change (e.g. audio focus lost, headphones
- // unplugged, notification pause) OR our own UI change.
- if (loadingRef.current && res.isPlaying === false) {
- // Ignore native pause events while loading, so we don't swallow
- // the user's play intent (fixes the "have to click 2-3 times" bug).
- return;
- }
- // Player.STATE_BUFFERING is 2
- if (res.isPlaying === false && res.playbackState === 2) {
- return;
- }
+ if (loadingRef.current && res.isPlaying === false) return;
+ if (res.isPlaying === false && res.playbackState === 2) return;
setPlaying(res.isPlaying);
}));
listeners.push(PlugdPlayer.addListener('onMediaItemTransition', (res) => {
@@ -930,8 +1004,11 @@ export default function Player({ track, playing, setPlaying, queue, setQueue, qI
const idx = q.findIndex(t => t.id === newId || t.id === newId.split('?')[0]);
if (idx !== -1 && idx !== qIdxRef.current) {
nativeTransitionIdRef.current = newId;
+ qIdxRef.current = idx;
+ trackRef.current = q[idx];
setQIdx(idx);
setTrack(q[idx]);
+ preloadQueueWindow(q, idx);
}
}));
return () => {