diff --git a/script.html b/script.html index bebb78c..d02a815 100644 --- a/script.html +++ b/script.html @@ -593,4 +593,214 @@ function wire(locked) { [].forEach.call(document.querySelectorAll("[data-broll]"), function (b) { b.addEventListener("click", async function () { + hideMsg("msg"); + var idx = parseInt(b.getAttribute("data-broll"), 10); + b.disabled = true; b.textContent = "Making…"; + var r = await api("make_broll", { email: email, project_id: pid, index: idx }); + if (!r.ok) { b.disabled = false; b.textContent = "Make cutaway"; showMsg("msg", r.error || "Could not make that."); return; } + var waited = 0; + var t = setInterval(async function () { + waited += 5; + var g = await api("get_project", { email: email, project_id: pid }); + var seg = (g.ok && g.script && g.script.segments || []).filter(function (x) { + return parseInt(x.index, 10) === idx; })[0]; + if (seg && seg.broll_url) { clearInterval(t); load(); return; } + if (waited > 180) { + clearInterval(t); + b.disabled = false; b.textContent = "Make cutaway"; + showMsg("msg", "That cutaway is taking unusually long."); + } + }, 5000); + }); + }); + + [].forEach.call(document.querySelectorAll("[data-make]"), function (b) { + b.addEventListener("click", async function () { + hideMsg("msg"); + var idx = parseInt(b.getAttribute("data-make"), 10); + // Flush any pending edits in this card before drawing from them. + var card = b.closest(".shot"); + var sc = card.querySelector("[data-scene]"), cm = card.querySelector("[data-cam]"), + ph = card.querySelector("[data-photo]"); + await api("update_shot", { email: email, project_id: pid, index: idx, + scene: sc ? sc.value : undefined, camera: cm ? cm.value : undefined, + photo_id: ph ? ph.value : undefined }); + b.disabled = true; b.textContent = "Drawing…"; + var r = await api("make_shot_image", { email: email, project_id: pid, index: idx }); + if (!r.ok) { + b.disabled = false; b.textContent = "Try again"; + showMsg("msg", r.error || "Could not make that frame."); + return; + } + // Drawing takes about half a minute and runs on the server, so the call + // above returns straight away. Watch the shot until the frame appears. + var waited = 0; + var tick = setInterval(async function () { + waited += 4; + var g = await api("get_project", { email: email, project_id: pid }); + var seg = (g.ok && g.script && g.script.segments || []).filter(function (x) { + return parseInt(x.index, 10) === idx; })[0]; + if (seg && seg.draw_error) { + clearInterval(tick); + b.disabled = false; b.textContent = "Try again"; + showMsg("msg", "That frame did not draw: " + seg.draw_error); + return; + } + if (seg && seg.plate_url) { clearInterval(tick); load(); return; } + if (waited > 120) { + clearInterval(tick); + b.disabled = false; b.textContent = "Try again"; + showMsg("msg", "That frame is taking unusually long. Try again in a moment."); + } + }, 4000); + }); + }); + + bindBottom(); +} + +async function bindFilm(p) { + var bal = document.getElementById("balsec"); + if (bal) { + var b = await api("get_balance", { email: email }); + var have = (b.ok && b.balance_seconds) || 0; + var need = videoSecondsOf(p); + bal.textContent = have + "s left" + (have < need ? " — not enough" : ""); + if (have < need) bal.style.color = "var(--bad)"; + } + + // While it films, keep the page honest about where it has got to. + if (p.status === "queued_to_film" || p.status === "filming") { + var t = setInterval(async function () { + var g = await api("get_project", { email: email, project_id: pid }); + if (!g.ok) return; + var st = g.project.status; + var note = document.getElementById("filmnote"); + if (note) note.textContent = g.project.film_note || st; + if (st === "ready" || st === "film_failed") { clearInterval(t); load(); } + }, 8000); + } + + var fb = document.getElementById("film"); + if (fb) fb.onclick = async function () { + hideMsg("msg"); + fb.disabled = true; fb.textContent = "Starting…"; + var r = await api("request_render", { email: email, project_id: pid }); + if (!r.ok) { + fb.disabled = false; fb.textContent = "Film it"; + showMsg("msg", r.error || "Could not start filming."); + return; + } + showMsg("msg", r.status_text || "Filming.", "ok"); + load(); + }; +} + +// ---------------------------------------------------------------- the cut +// EDL is {duration, cuts:[{shot,start,seconds,kind,src,in,moved}], segments}. +// Only the cut positions are editable; the server composes the rest, so the +// page never has to be trusted about source urls or trim points. +var EDL = null, DIRTY = false; + +function tlDraw() { + var tl = document.getElementById("tl"); + if (!tl || !EDL) return; + var dur = EDL.duration || 1; + + // Above the bar: where each shot begins, with the frame it opens on. The end + // of a shot is always the start of the next one, so only starts are marked. + var strip = document.getElementById("tlshots"); + if (strip) { + var segs = ((DATA && DATA.script && DATA.script.segments) || []); + strip.innerHTML = segs.map(function (s, k) { + var at = k * 8, wide = (8 / dur) * 100; + return '