diff --git a/script.html b/script.html index d02a815..27198b7 100644 --- a/script.html +++ b/script.html @@ -803,4 +803,197 @@ function tlBind() { if (drag.mode === "seek") { seekTo(timeAt(e.clientX)); return; } var dur = EDL.duration || 1, w = tl.clientWidth || 1; var d = ((e.clientX - drag.x0) / w) * dur; + var c = EDL.cuts[drag.i]; + if (drag.mode === "move") { + c.start = Math.max(0, Math.min(drag.start0 + d, dur - c.seconds)); + } else { + c.seconds = Math.max(0.8, Math.min(drag.secs0 + d, Math.min(20, dur - c.start))); + } + c.start = Math.round(c.start * 20) / 20; // quarter-frame is plenty + c.seconds = Math.round(c.seconds * 20) / 20; + c.moved = true; DIRTY = true; + tlDraw(); + }); + + function end(e) { + if (!drag) return; + drag = null; + try { tl.releasePointerCapture(e.pointerId); } catch (x) {} + } + tl.addEventListener("pointerup", end); + tl.addEventListener("pointercancel", end); +} + +// The preview never renders anything. The talking head plays with its audio +// throughout and the picture is covered while a cutaway is due, which is +// exactly what the finished cut does. +function previewBind(p) { + var v = document.getElementById("pvid"); + if (!v) return; + if (p.video_url) v.src = p.video_url; + var ovl = document.getElementById("povl"), + oi = document.getElementById("povlimg"), + ov = document.getElementById("povlvid"); + + function activeCut(t) { + var cs = (EDL && EDL.cuts) || []; + for (var i = 0; i < cs.length; i++) { + if (t >= cs[i].start && t < cs[i].start + cs[i].seconds) return cs[i]; + } + return null; + } + + function resetPlayButton() { + var b = document.getElementById("pplay"); + if (b) b.innerHTML = "▶ Preview the cut"; + } + // Playing to the end used to leave the button reading "Pause" for good. + v.addEventListener("ended", resetPlayButton); + v.addEventListener("pause", resetPlayButton); + v.addEventListener("play", function () { + var b = document.getElementById("pplay"); + if (b) b.innerHTML = "▮▮ Pause"; + }); + + var showing = null; + v.addEventListener("timeupdate", function () { + var head = document.getElementById("tlhead"); + if (head && EDL) head.style.left = ((v.currentTime / (EDL.duration || 1)) * 100) + "%"; + var tm = document.getElementById("ptime"); + if (tm && EDL) tm.textContent = v.currentTime.toFixed(1) + " / " + (EDL.duration || 0).toFixed(1) + "s"; + var c = activeCut(v.currentTime); + if (c === showing) return; + showing = c; + if (!c) { ovl.style.display = "none"; ov.pause(); return; } + ovl.style.display = "block"; + if (c.kind === "video") { + oi.style.display = "none"; ov.style.display = "block"; + if (ov.src !== c.src) ov.src = c.src; + try { ov.currentTime = c["in"] || 0; } catch (x) {} + ov.play().catch(function () {}); + } else { + ov.style.display = "none"; ov.pause(); + oi.style.display = "block"; oi.src = c.src; + } + }); + + document.getElementById("pplay").onclick = function () { + // Starting again from the end should start again, not sit there. + if (v.ended || v.currentTime >= (EDL.duration || 0) - 0.05) v.currentTime = 0; + if (v.paused) v.play(); else v.pause(); + }; +} + +async function loadEdl(p) { + var r = await api("preview_edl", { email: email, project_id: pid }); + if (!r.ok) return; + EDL = r.edl; DIRTY = false; + tlDraw(); tlBind(); previewBind(p); + + document.getElementById("edlsave").onclick = async function () { + var b = this; b.disabled = true; b.textContent = "Saving…"; + var s = await api("save_edl_cuts", { email: email, project_id: pid, + cuts: (EDL.cuts || []).map(function (c) { + return { shot: c.shot, start: c.start, seconds: c.seconds }; }) }); + b.textContent = "Save these positions"; + if (!s.ok) { b.disabled = false; showMsg("msg", s.error || "Those positions were refused."); return; } + hideMsg("msg"); + EDL = s.edl; DIRTY = false; tlDraw(); + showMsg("msg", s.status_text || "Saved.", "ok"); + }; + + document.getElementById("edlreset").onclick = async function () { + var s = await api("reset_edl_cuts", { email: email, project_id: pid }); + if (!s.ok) { showMsg("msg", s.error || "Could not reset."); return; } + EDL = s.edl; DIRTY = false; tlDraw(); + showMsg("msg", s.status_text || "Back to the suggested cut.", "ok"); + }; +} + +// Every film keeps the take it replaced. Without this, a re-film to fix one +// thing could quietly make another worse with no way back. +async function loadTakes() { + var el = document.getElementById("takes"); + if (!el) return; + var r = await api("list_versions", { email: email, project_id: pid }); + if (!r.ok || !(r.versions || []).length) { el.innerHTML = ""; return; } + el.innerHTML = '
' + esc(r.status_text || "") + ' ' + + 'Bringing one back keeps the one you are leaving.
' + + '