From 0b16bfbb2f0e67b1f06db7274dce6f2fd455f930 Mon Sep 17 00:00:00 2001 From: basboosa05 <166862890+basboosa05@users.noreply.github.com> Date: Sat, 2 May 2026 17:22:04 +0300 Subject: [PATCH 1/3] seek working - fixed the null array sent to the state endpoint - fixed the waveform of track that shows running when another one is playing --- src/components/playlist/PlaylistComponent.tsx | 94 ++++---- .../playlist/PlaylistStatsWaveform.tsx | 107 ++++++++- .../[trackSlug]/components/TrackWaveform.tsx | 223 +++++++++--------- src/pages/you/sets/PlaylistSlugPage.tsx | 46 +--- src/services/audioService.ts | 84 +++---- src/stores/player.store.ts | 54 ++--- 6 files changed, 340 insertions(+), 268 deletions(-) diff --git a/src/components/playlist/PlaylistComponent.tsx b/src/components/playlist/PlaylistComponent.tsx index 3f8e649c..66fd79b4 100644 --- a/src/components/playlist/PlaylistComponent.tsx +++ b/src/components/playlist/PlaylistComponent.tsx @@ -111,19 +111,29 @@ interface PlaylistWaveformProps { isActive: boolean; } +// ─── PlaylistWaveform ─────────────────────────────────────────────────────── +// FIXED: inactive tracks must NEVER touch audio src or the MediaElement backend. +// Active track re-initializes only when the track id actually changes. function PlaylistWaveform({ track, isActive }: PlaylistWaveformProps) { const containerRef = useRef(null); const wsRef = useRef(null); const timeRef = useRef(null); const durRef = useRef(null); + const lastTrackIdRef = useRef(null); useEffect(() => { - let ws: WaveSurfer | null = null; let isMounted = true; const initWaveform = async () => { if (!containerRef.current) return; - if (wsRef.current) { try { wsRef.current.destroy(); } catch { /* ok */ } wsRef.current = null; } + + // Destroy previous instance only if track actually changed + if (wsRef.current) { + try { wsRef.current.destroy(); } catch { /* ok */ } + wsRef.current = null; + } + containerRef.current.innerHTML = ""; + lastTrackIdRef.current = track.id; const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); @@ -134,88 +144,77 @@ function PlaylistWaveform({ track, isActive }: PlaylistWaveformProps) { track.waveformData?.length > 0 ? track.waveformData : await getTrackWaveform(track.id); - if (!isMounted) return; - - const hasPeaks = peaks && peaks.length > 0; + if (!isMounted || !containerRef.current) return; - let parsedDur = 0; - if (track.duration && typeof track.duration === "string") { - const parts = track.duration.split(":"); - if (parts.length === 2) { - parsedDur = parseInt(parts[0], 10) * 60 + parseInt(parts[1], 10); - } - } + const parsedDur = parseDur(track.duration ?? ""); if (isActive) { - ws = WaveSurfer.create({ - container: containerRef.current!, + // Active: share the global audio element — DO NOT provide url + const ws = WaveSurfer.create({ + container: containerRef.current, waveColor: g, progressColor: pg, barWidth: 2, barGap: 0.5, barRadius: 2, height: 80, - backend: "MediaElement", - media: audio, - peaks: hasPeaks ? [peaks] : undefined, + backend: "MediaElement" as const, + media: audio, // ← share, don't load new src + peaks: peaks.length > 0 ? [peaks] : undefined, duration: parsedDur > 0 ? parsedDur : undefined, }); + wsRef.current = ws; setGlobalWaveSurfer(ws, track.id); - setTrackLoadedLocally(track.id); + setTrackLoadedLocally(track.id); // tell audioService: src already set - ws.on("timeupdate", (currentTime: number) => { - if (timeRef.current) timeRef.current.textContent = fmt(currentTime); + ws.on("timeupdate", (t: number) => { + if (timeRef.current) timeRef.current.textContent = fmt(t); }); - - ws.on("interaction", (newTime: number) => { - seekAudio(newTime); - }); - - ws.on("decode", (dur: number) => { - if (durRef.current) durRef.current.textContent = fmt(dur); + ws.on("interaction", (t: number) => seekAudio(t)); + ws.on("decode", (d: number) => { + if (durRef.current) durRef.current.textContent = fmt(d); }); + ws.on("error", () => {}); } else { - // FIX 1: always set height (was missing in inactive branch) - // FIX 2: always provide url so WaveSurfer has an audio source to decode - // peaks alone give shape but no duration/interaction without a url - ws = WaveSurfer.create({ - container: containerRef.current!, + // Inactive: peaks-only, NO url, NO MediaElement — never touch global audio + const ws = WaveSurfer.create({ + container: containerRef.current, waveColor: g, progressColor: pg, barWidth: 2, barGap: 0.5, barRadius: 2, - height: 80, // ← FIX 1: was missing, caused 0px render + height: 80, interact: false, - peaks: hasPeaks ? [peaks] : undefined, + peaks: peaks.length > 0 ? [peaks] : undefined, duration: parsedDur > 0 ? parsedDur : undefined, - url: track.audioUrl, // ← FIX 2: was `!hasPeaks ? track.audioUrl : undefined` + // ← NO url here: loading audio for display-only tracks hijacks the + // global