// Optional "Remove background music" for URL/YouTube imports. Button appears only when the // server reports features.vocal_isolation. Original stays loadable via the toggle (before/after). function setupVocalIsolation({ btn, toggle, msg, getId, getDur, apply }) { const b = $(btn), t = $(toggle), m = $(msg); let pair = null, showing = 'clean'; const say = text => { m.textContent = text; status(text); }; fetch('/api/version').then(r => r.json()).then(v => { if (v?.features?.vocal_isolation) b.hidden = false; }).catch(() => {}); const label = () => { t.textContent = showing === 'clean' ? 'Play original' : 'Play without music'; }; b.addEventListener('click', async () => { const id = getId(); if (!id) { say('Load audio first, then remove the background music.'); return; } b.disabled = true; t.hidden = true; say('Removing background music. This can take a few minutes.'); try { const r = await fetch('/api/isolate-vocals', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ id }) }); const d = await r.json().catch(() => ({})); if (!r.ok) throw new Error(d.detail || r.statusText); pair = { orig:id, origDur:getDur(), clean:d.id, cleanDur:d.duration }; showing = 'clean'; apply(d.id, d.duration); t.hidden = false; label(); say('Background music removed. Use the button next to it to compare with the original.'); } catch (e) { say('Could not remove background music: ' + e.message); toast('Vocal isolation failed: ' + e.message, 'error'); } finally { b.disabled = false; } }); t.addEventListener('click', () => { if (!pair) return; showing = showing === 'clean' ? 'orig' : 'clean'; if (showing === 'clean') apply(pair.clean, pair.cleanDur); else apply(pair.orig, pair.origDur); label(); say(showing === 'clean' ? 'Playing the version without background music.' : 'Playing the original audio.'); }); }