(jobId, stems, duration, thumbnail, mixUrl = null, title = "", peaksPromise = null, hasVideo = false)
| 765 | } |
| 766 | |
| 767 | export function wireUpAudio(jobId, stems, duration, thumbnail, mixUrl = null, title = "", peaksPromise = null, hasVideo = false) { |
| 768 | const app = document.querySelector(".app"); |
| 769 | app?.classList.remove("is-import"); |
| 770 | app?.classList.remove("no-track"); |
| 771 | setWaveformLoading(true); |
| 772 | stopVuLoop(); |
| 773 | stopStemVuLoop(); |
| 774 | if (multitrack) { |
| 775 | multitrack.destroy(); |
| 776 | setMultitrack(null); |
| 777 | } |
| 778 | playBtn.classList.remove("playing"); |
| 779 | stopBtn.classList.remove("stopped"); |
| 780 | visualRenderToken += 1; |
| 781 | const token = visualRenderToken; |
| 782 | window.setTimeout(() => { |
| 783 | const el = document.getElementById("waveLoadingOverlay"); |
| 784 | if (token === visualRenderToken && el && !el.classList.contains("hidden")) { |
| 785 | el.classList.add("stalled"); |
| 786 | } |
| 787 | }, 20000); |
| 788 | window.setTimeout(() => { |
| 789 | if (token === visualRenderToken) setWaveformLoading(false); |
| 790 | }, 60000); |
| 791 | setCurrentJobId(jobId); |
| 792 | setTotalDuration(duration || 0); |
| 793 | refreshMixerVisuals(); |
| 794 | const mixReady = loadMixIntoState(jobId, stems.map((s) => s.name)) |
| 795 | .then(() => { if (currentJobId === jobId) refreshMixerVisuals(); }) |
| 796 | .catch((e) => { console.warn("[player] mix state load failed:", e); }); |
| 797 | setLaneControlsEnabled(true); |
| 798 | setLoopEnabled(false); |
| 799 | setLoopStart(0); |
| 800 | setLoopEnd(0); |
| 801 | loopBtn.classList.remove("active"); |
| 802 | loopRegionEl.classList.add("hidden"); |
| 803 | |
| 804 | // User-selected stems only. Backend produced all 6, but the import- |
| 805 | // page toggles tell us which ones the user actually wanted to see. |
| 806 | // Filter early so multitrack, decoded-visuals, energy baseline, and |
| 807 | // mini-waves all operate on the trimmed set. The synthetic "original" |
| 808 | // track always passes the filter -- the user wants the full song |
| 809 | // available alongside the isolated stems for A/B comparison. (When |
| 810 | // the user selected all 6 stems, the backend doesn't produce |
| 811 | // original.wav, so it's simply not in `stems` and the mixer/sidebar |
| 812 | // rows for it stay hidden.) |
| 813 | stems = stems.filter((s) => s.name === "original" || selectedStems.has(s.name)); |
| 814 | _currentStems = stems; |
| 815 | _mixUrl = mixUrl || null; |
| 816 | _currentTitle = title || ""; |
| 817 | _currentHasVideo = !!hasVideo; |
| 818 | document.getElementById("footer-export-wrap")?.classList.toggle("has-video", !!hasVideo); |
| 819 | applyStemSelectionFilter(new Set(stems.map((s) => s.name))); |
| 820 | updateFooterTrack({ thumbnail, stemCount: stems.filter((s) => s.name !== "original").length }); |
| 821 | |
| 822 | // Reset footer waveform state — will be re-populated below after peaks fetch. |
| 823 | _footerWavePeaks = null; |
| 824 | setFooterWaveDrawFn(null); |
no test coverage detected