()
| 542 | } |
| 543 | |
| 544 | function extractScreen() { |
| 545 | const fileName = state.extractFile?.name; |
| 546 | return `<div class="screen scrl"> |
| 547 | <div class="pad"> |
| 548 | <div class="h1">Extract stems</div> |
| 549 | <div class="sub">Paste a link or upload audio to split into stems.</div> |
| 550 | <div class="paste">${ICON.link}<input id="ext-url" class="ext-input" type="url" inputmode="url" autocomplete="off" autocapitalize="off" spellcheck="false" placeholder="Paste YouTube or audio URL" value="${esc(state.extractUrl || "")}"></div> |
| 551 | <button class="upload" data-action="pick-file">${ICON.upload}${fileName ? esc(fileName) : "Upload audio file"}</button> |
| 552 | <input id="ext-file" type="file" accept="audio/*,video/mp4,.mp3,.wav,.flac,.m4a,.ogg" style="display:none"> |
| 553 | <div class="eyebrow">STEMS TO EXTRACT</div> |
| 554 | <div class="chips">${EXTRACT_STEMS.map((s) => { |
| 555 | const on = !!state.selected[s.id]; |
| 556 | const onStyle = on ? `border-color:${s.color};background:${s.color}1c;` : ""; |
| 557 | return `<button class="chip-btn ${on ? "on" : ""}" style="${onStyle}" data-action="chip" data-id="${s.id}"><div class="dot" style="background:${s.color}"></div><span class="nm">${s.name}</span>${on ? ICON.check : ""}</button>`; |
| 558 | }).join("")}</div> |
| 559 | <button class="cta" style="margin-top:22px" data-action="split">${ICON.scissors}Split stems</button> |
| 560 | ${extractProgressCard()} |
| 561 | </div> |
| 562 | </div>`; |
| 563 | } |
| 564 | |
| 565 | // Submit an extraction (URL or file) and follow it to completion. |
| 566 | async function startExtraction() { |
no test coverage detected