From 3f5bd70f24698f1b5e12f02c9c5ec5e29932ce51 Mon Sep 17 00:00:00 2001 From: gator Date: Sat, 19 Sep 2026 13:51:23 +0000 Subject: [PATCH] Remove the duplicated page tail left after the closing html tag A dollar sign before a quote is a substitution token to patch_git_file, so a price label spliced the rest of the page in after itself. trailer.html carried 33 KB of raw JavaScript after its closing tag, which rendered as garbled text in the page body; script.html carried 65 KB, commented out. The copy above the first closing tag was verified complete: it parses, and it defines every function and calls every action the removed text did. --- trailer.html | 704 --------------------------------------------------- 1 file changed, 704 deletions(-) diff --git a/trailer.html b/trailer.html index 558d271..a66df1d 100644 --- a/trailer.html +++ b/trailer.html @@ -1145,707 +1145,3 @@ - + (filmed * 0.02).toFixed(2) + ''); - } - - if (unfilmed && !undrawn) - out.push(''); - if (filmed) - out.push(''); - } - return out.join(""); - } - - // A cut may move shots as well as drop them, which makes it a different - // film rather than a shorter one. Worth saying out loud. - function reordered(ids) { - var a = (ids || []).map(String); - var b = a.slice().sort(); - return a.join("") !== b.join(""); - } - - // What the storyboard will actually cost to draw. Three sheets for every - // character who is seen or heard, one more per extra day they are dressed - // differently, and two frames per shot. - function boardImages(shots, cast) { - var seen = {}; - shots.forEach(function (s) { - (s.visible_characters || []).forEach(function (c) { seen[String(c)] = 1; }); - (s.audio || []).forEach(function (a) { - if (String(a.text || "").trim()) seen[String(a.character_id)] = 1; }); - }); - var present = cast.filter(function (c) { return seen[String(c.id)]; }); - - var days = {}; - shots.forEach(function (s) { - var d = String(s.day || "").trim(); - if (!d) return; - (s.visible_characters || []).forEach(function (c) { - (days[String(c)] = days[String(c)] || {})[d] = 1; }); - }); - var extra = 0; - present.forEach(function (c) { - var wd = c.wardrobe_by_day || {}; - var base = Object.keys(wd).sort()[0] || ""; - Object.keys(days[String(c.id)] || {}).forEach(function (d) { - if (d !== base && wd[d]) extra += 1; }); - }); - return present.length * 3 + extra + shots.length * 2; - } - - function mmss(n) { - return Math.floor(n / 60) + ":" + String(Math.round(n % 60)).padStart(2, "0"); - } - - // The header's runtime is the sum of what the writer typed. This is what the - // lines take when the cast actually says them, which is a different number - // and the only one worth cutting against. - function timingHtml() { - var t = PKG.timing; - if (!t || !t.measured_seconds) return ""; - var over = Number(t.over_by || 0); - var line = "Spoken by the cast this script runs " + mmss(t.measured_seconds) - + ", against a " + mmss(t.target_seconds) + " target"; - if (over > 5) line += " — " + Math.round(over) + " seconds over." - + " Revising the whole script will cut that much."; - else if (over < -15) line += " — " + Math.round(-over) + " seconds short."; - else line += " — that fits."; - var worst = (t.longest_lines || []).slice(0, 3).map(function (l) { - return "
  • " + Number(l.seconds || 0).toFixed(1) + "s · shot " - + esc(String(l.shot)) + " · “" - + esc(String(l.text || "").slice(0, 80)) + "”
  • "; }).join(""); - return '
    ' + line + "
    " - + (worst && over > 5 ? "" : "") + "
    "; - } - - // A job lives in the worker's memory and dies with it. Every deploy kills - // one, and the flag it set stays set, so anything older than this is treated - // as gone rather than leaving the page waiting on it for ever. - function busyCuts() { - if (String(PKG.cuts_state || "") !== "thinking") return false; - var t = Date.parse(PKG.cuts_started || ""); - if (!t) return false; - return (Date.now() - t) < 15 * 60 * 1000; - } - - // ---- the storyboard player ------------------------------------------- - // One call fetches the whole reel; after that nothing touches the network, - // which is what makes stop and jump-to instant. - var REEL = null, RAT = -1, RTIMER = null; - var reelAudio = new Audio(); - - function reelHtml() { - var st = String(PKG.reel_state || ""); - var shots = (PKG.shots || []); - if (!shots.length) return ""; - var running = st && st !== "ready" && !/^failed/.test(st); - - var out = '

    Watch the storyboard

    ' - + '

    The frames in order with the lines spoken over them — ' - + 'the trailer before it is filmed. Sequence problems are obvious here ' - + 'and nearly invisible in the script below.

    '; - - if (running) - out += '

    Speaking every shot… ' + esc(st) + '

    '; - else if (st === "ready") { - var spoken = shots.filter(function (s) { return s.reel_url; }).length; - var silent = shots.length - spoken; - out += '

    ' + spoken + ' shots have dialogue and are spoken' - + (silent ? '; ' + silent + ' are silent and hold on the frame' : '') - + '.

    '; - } - else if (/^failed/.test(st)) - out += '

    ' + esc(st) + '

    '; - else if (st) - out += '

    The last attempt stopped before it finished — ' - + 'the server restarted under it. Ask again.

    '; - - if (!REEL) { - out += '
    ' - + '
    '; - return out; - } - - var s = REEL[RAT] || REEL[0] || {}; - var lines = (s.lines || []).map(function (l) { - return '
    ' + esc(l.who || "") - + (l.lip_sync ? "" : " (v.o.)") + '' + esc(l.text || "") + '
    '; - }).join("") || '
    no dialogue in this shot
    '; - - out += '
    ' - + '
    opens on
    ' - + '
    ends on
    ' - + '
    ' + esc(s.id) + ' · ' - + esc(s.title || "") + '
    ' - + '
    ' + esc(s.setting || "") - + (s.scene ? ' · ' + esc(s.scene) : "") - + (s.day ? ' · ' + esc(s.day) : "") + '
    ' - + '
    ' + lines + '
    ' - + '
    ' - + '
    ' - + '' - + '' - + '' - + '' - + '' - + '' + (RAT + 1) + ' of ' + REEL.length + '' - + '
    '; - return out; - } - - function reelPaint() { - var host = document.getElementById("reelhost"); - if (host) host.innerHTML = reelHtml(); - } - - // The two frames split the shot's running time: the opening frame holds for - // the first half, the closing frame for the second. It is a storyboard, not - // an animation, but it reads as movement and it marks where the shot lands. - function reelShow(i) { - if (!REEL || i < 0 || i >= REEL.length) { reelStop(); return; } - RAT = i; - reelPaint(); - var s = REEL[i]; - var dur = Math.max(Number(s.seconds) || 0, Number(s.edit_seconds) || 0, 2.5); - if (s.audio_url) { reelAudio.src = s.audio_url; reelAudio.play().catch(function(){}); } - else reelAudio.pause(); - - var t0 = Date.now(), swapped = false; - clearInterval(RTIMER); - RTIMER = setInterval(function () { - var gone = (Date.now() - t0) / 1000; - var bar = document.getElementById("rprog"); - if (bar) bar.style.width = Math.min(100, 100 * gone / dur) + "%"; - if (!swapped && gone > dur / 2) { - swapped = true; - var a = document.getElementById("rfa"), b = document.getElementById("rfb"); - if (a && b && REEL[RAT].closing_url) { a.classList.remove("on"); b.classList.add("on"); } - } - if (gone >= dur) reelShow(RAT + 1); - }, 120); - } - - function reelStop() { - clearInterval(RTIMER); RTIMER = null; - reelAudio.pause(); - REEL = null; RAT = -1; - openTab(CUR); - } - - // A cut is an ordering, not a render. Playing one is free, so all of them - // can be looked at before anything is joined into a file. - function cutsHtml(meta) { - if (meta.writer !== RUN.lead_writer) return ""; - var shots = PKG.shots || []; - var filmed = shots.filter(function (s) { return s.clip_url; }).length; - var cuts = PKG.cuts || []; - var st = String(PKG.cuts_state || ""); - var out = '

    Shorter cuts of this same shoot

    ' - + '

    Nothing here is re-filmed. A cut is an order to play ' - + 'the shots in, so making another one costs nothing — which is why ' - + 'the question is not “what is the right length” but “which ' - + 'version do people watch to the end”.

    '; - - if (busyCuts()) out += '

    Working out what to drop…

    '; - else if (st === "thinking") out += '

    The last attempt stopped ' - + 'before it finished — the server restarted under it. Ask again.

    '; - else if (/^failed/.test(st)) out += '

    ' + esc(st) + '

    '; - - out += cuts.map(function (c) { - var stat = STATS[c.id]; - var play = (PLAYING === c.id) - ? '
    ' - + '
    loading…' - + '
    ' - : ''; - var tools = ''; - if (c.state === "made" && c.video_url) - tools += 'Published · in My videos'; - else if (c.state === "making") - tools += 'joining…'; - else if (filmed) - tools += ''; - - return '
    ' + esc(c.name || "") + '' - + '' + mmss(c.seconds || 0) + ' · ' - + (c.shot_ids || []).length + ' shots' - + (c.strategy ? '

    ' + esc(c.strategy) + '

    ' : '') - + (c.why ? '

    ' + esc(c.why) + '

    ' : '') - + (c.sacrifices ? '

    gives up: ' + esc(c.sacrifices) + '

    ' : '') - + (stat ? '

    ' + stat.views + ' viewer' - + (stat.views === 1 ? '' : 's') + ' · median ' + stat.median_percent - + '% watched · ' + stat.completion_rate + '% reached the end

    ' : '') - + '

    ' + esc((c.shot_ids || []).join(" ")) - + (reordered(c.shot_ids) ? ' · reordered' : '') + '

    ' - + '
    ' + tools + '
    ' + play + '
    '; - }).join(""); - - if (!cuts.length && st !== "thinking") - out += '

    No cuts proposed yet.

    '; - return out + '
    '; - } - - function reviewsHtml(meta) { - var mine = REVIEWS.filter(function (r) { - return r.subject === meta.writer && Number(r.iteration) === Number(meta.iteration); }); - if (!mine.length) return ""; - var gaps = mine.filter(function (r) { return r.reviewer === "__gaps__"; })[0]; - var readers = mine.filter(function (r) { return r.reviewer !== "__gaps__"; }); - var out = ""; - - if (gaps) { - var v = gaps.strengths && gaps.strengths.verdict; - var list = gaps.suggestions || []; - out += '

    What this trailer does not tell a viewer

    ' - + (v ? '

    ' + esc(v) + '

    ' : '') - + (list.length ? '
      ' + list.map(function (g) { - return '
    1. ' + esc(g.what || "") - + 'shot ' + esc(g.shot || "?") + ' → ' - + esc(g.fix || "") + '
    2. '; }).join("") + '
    ' : '') - + '
    '; - } - - out += readers.map(function (r) { - var s = r.strengths || {}, e = r.errors || {}; - if (typeof s !== "object") s = {}; - if (typeof e !== "object") e = {}; - function ul(items, cls) { - return (!items || !items.length) ? "" - : ''; - } - return '

    ' + esc(r.label) + ' watched it without reading the book' - + (r.score ? '' - + r.score + '/100' : '') + '

    ' - + (s.summary ? '
    ' + esc(s.summary) + '
    ' : '') - + '
    main character: ' + esc(s.who || "not clear to them") + '
    ' - + '
    what they want: ' + esc(s.wants || "not clear to them") + '
    ' - + '
    what is in the way: ' + esc(s.obstacle || "not clear to them") + '
    ' - + (s.would_watch ? '
    would they watch the film: ' - + esc(s.would_watch) + '
    ' : '') - + (s.unanswered && s.unanswered.length - ? '
    still asking:
    ' + ul(s.unanswered, "") : '') - + (e.missed && e.missed.length ? '
    they missed:
    ' + ul(e.missed, "") : '') - + (e.got_wrong && e.got_wrong.length - ? '
    the trailer misled them:
    ' + ul(e.got_wrong, "wrong") : '') - + '
    '; - }).join(""); - - return '
    ' + out + '
    '; - } - - function paneHtml(meta, draft) { - var p = PKG, shots = (p.shots || []).slice().sort(function (a, b) { - return String(a.id) < String(b.id) ? -1 : 1; }); - var cast = p.characters || [], voice = {}; - cast.forEach(function (c) { voice[c.id] = c; }); - var secs = shots.reduce(function (a, s) { return a + (Number(s.edit_seconds) || 0); }, 0); - - var head = '

    ' + esc(p.title || meta.label) + '

    ' - + (p.tagline ? '' + esc(p.tagline) + '' : '') + '
    ' - + (p.logline ? '

    ' + esc(p.logline) + '

    ' : '') - + '

    ' + esc(meta.label) - + (Number(meta.iteration) > 1 ? ' · revision ' + meta.iteration : '') - + ' · ' + shots.length + ' shots · ' - + Math.floor(secs / 60) + ':' + String(Math.round(secs % 60)).padStart(2, "0") - + ' · ' + cast.length + ' characters

    ' - + timingHtml(); - - var probs = draft.problems - ? '
    The checker has objections
    ' - : ""; - - var castHtml = '
    ' + cast.map(function (c) { - var sheets = (c.head_url || c.front_url || c.side_url) - ? '
    ' + [c.head_url, c.front_url, c.side_url].map(function (u) { - return u ? '' : ''; }).join("") + '
    ' : ''; - return '
    ' + sheets + '' + esc(c.name || c.id) + '' - + '' + esc(c.role || "") + (c.voice_id ? " · voiced" : " · silent") - + '
    ' + esc((c.appearance || "").slice(0, 150)) + '
    '; - }).join("") + '
    '; - - var body = insertHtml("start"); - shots.forEach(function (s) { body += shotHtml(s, voice) + insertHtml(s.id); }); - - return head + probs + '
    ' + barHtml(meta) + '
    ' - + '
    ' + reelHtml() + '
    ' - + spineHtml() + cutsHtml(meta) + reviewsHtml(meta) + castHtml + body; - } - - function insertHtml(after) { - if (EDITING === "new:" + after) { - return '
    ' - + '' - + '
    ' - + '
    '; - } - return '
    '; - } - - // The faults themselves, with the prompt change that would prevent each - - // which is the half that makes the check actionable rather than a verdict. - function faultsHtml(s) { - var vc = s.vision_check; - if (!vc || !(vc.faults || []).length) return ""; - return '
    ' - + (vc.summary ? '
    ' + esc(vc.summary) + '
    ' : '') - + vc.faults.map(function (f) { - return '
    ' + esc(f.kind || "") + '' - + (f.at != null ? ' at ' + Number(f.at).toFixed(1) + 's' : '') - + ' · ' + esc(f.what || "") - + (f.prompt_fix ? 'fix: ' + esc(f.prompt_fix) - + '' : '') + '
    '; - }).join("") + '
    '; - } - - function shotHtml(s, voice) { - if (s.writing) { - return '
    ' + esc(s.id) + '
    ' - + '

    ' + esc(s.title || "New scene") + '

    ' - + '

    The writer is turning your note into a shot…

    ' - + '

    ' + esc(String(s.rough || "").slice(0, 300)) + '

    '; - } - - var lines = (s.audio || []).map(function (a) { - var who = voice[a.character_id] || {}; - return '
    ' - + '' - + '
    ' + esc(who.name || a.character_id) + ' ' - + (a.lip_sync ? 'on camera' : 'voice over') - + '
    ' + esc(a.text || "") - + (a.delivery ? '
    ' + esc(a.delivery) + '' : '') - + '
    '; - }).join(""); - - var media = ""; - if (s.clip_url) { - media = '
    the filmed shot' - + (s.clip_seconds ? ' · ' + Number(s.clip_seconds).toFixed(1) + 's' : '') - + '
    ' - + (s.opening_url ? '
    filmed from this frame
    ' : '') - + '
    '; - } else if (s.opening_url || s.closing_url) { - media = '
    ' - + (s.opening_url ? '
    opens on
    ' : '') - + (s.closing_url ? '
    ends on
    ' : '') - + '
    '; - } - - var chk = s.audio_check || {}; - var chkHtml = ""; - if (chk.verdict && chk.verdict !== "no dialogue") { - var good = chk.verdict === "ok" || chk.verdict === "drifted"; - chkHtml = 'audio ' + esc(chk.verdict) - + (chk.coverage != null ? ' ' + Math.round(chk.coverage * 100) + '%' : '') - + (chk.missing ? ' — lost “' + esc(String(chk.missing).slice(0, 40)) + '”' : '') - + ''; - } - - // What the eyes found, if they have looked. Kinds: render (the picture is - // broken), continuity (the frames disagree), script (wrong shot, well made). - var vc = s.vision_check || null; - var eyeHtml = ""; - if (vc && vc.verdict) { - var bad = vc.verdict === "broken"; - var faults = vc.faults || []; - eyeHtml = 'picture ' + esc(vc.verdict) - + (faults.length ? ' · ' + faults.length + ' fault' - + (faults.length === 1 ? '' : 's') : '') - + (vc.people_on_screen != null ? ' · ' + vc.people_on_screen - + ' on screen' : '') + ''; - } - - var tools; - if (s.drawing) tools = 'drawing…'; - else if (s.filming) tools = 'filming…'; - else if (EDITING === s.id) { - tools = '
    ' - + '
    ' - + '
    '; - } else { - tools = '' - + '' - + (s.opening_url ? '' : '') - + chkHtml + eyeHtml - + ''; - } - - return '
    ' + esc(s.id) + '
    ' - + '

    ' + esc(s.title || "") + '

    ' - + '
    ' + esc(s.setting || "") + ' · ' - + Number(s.edit_seconds || 0).toFixed(1) + 's' - + (s.scene ? ' · ' + esc(s.scene) + '' : '') - + (s.day ? ' · ' + esc(s.day) + '' : '') - + '
    ' - + media - + '

    ' + esc(s.action || "") + '

    ' - + (s.camera ? '

    camera: ' + esc(s.camera) + '

    ' : '') - + (s.continuity ? '

    continuity: ' + esc(s.continuity) + '

    ' : '') - + lines - + faultsHtml(s) - + '
    ' + tools + '
    ' - + '' - + '
    '; - } - - async function act(name, payload) { - var r = await api(name, payload); - if (!r.ok) { showMsg("msg", r.error || "That did not work."); return null; } - hideMsg("msg"); - return r; - } - - document.getElementById("pane").addEventListener("click", async function (e) { - var hit = function (attr) { return e.target.closest("[" + attr + "]"); }; - var el, r; - - if ((el = hit("data-cancel"))) { EDITING = null; openTab(CUR); return; } - if ((el = hit("data-add"))) { EDITING = "new:" + el.getAttribute("data-add"); openTab(CUR); return; } - if ((el = hit("data-edit"))) { EDITING = el.getAttribute("data-edit"); openTab(CUR); return; } - - if ((el = hit("data-addgo"))) { - var rough = document.getElementById("newrough").value.trim(); - if (rough.length < 20) { showMsg("msg", "Say what happens in the scene."); return; } - el.disabled = true; - r = await act("add_shot", { draft_id: CUR, after: el.getAttribute("data-addgo"), rough: rough }); - EDITING = null; if (r) await refresh(); return; - } - if ((el = hit("data-rewrite"))) { - var rw = document.getElementById("rough").value.trim(); - if (rw.length < 20) { showMsg("msg", "Say what should change."); return; } - el.disabled = true; - r = await act("write_shot", { draft_id: CUR, shot_id: el.getAttribute("data-rewrite"), rough: rw }); - EDITING = null; if (r) await refresh(); return; - } - if ((el = hit("data-drawone"))) { - el.disabled = true; - r = await act("draw_one_shot", { draft_id: CUR, shot_id: el.getAttribute("data-drawone") }); - if (r) await refresh(); return; - } - if ((el = hit("data-filmone"))) { - el.disabled = true; - r = await act("film_one_shot_now", { draft_id: CUR, shot_id: el.getAttribute("data-filmone") }); - if (r) await refresh(); return; - } - if ((el = hit("data-delete"))) { - if (!confirm("Remove shot " + el.getAttribute("data-delete") + " from this trailer?")) return; - el.disabled = true; - r = await act("delete_shot", { draft_id: CUR, shot_id: el.getAttribute("data-delete") }); - if (r) await refresh(); return; - } - if ((el = hit("data-assemble"))) { - el.disabled = true; - r = await act("assemble_trailer", { draft_id: CUR }); - if (r) { showMsg("msg", "Joining " + r.shots + " shots. It appears in My videos.", "ok"); - setTimeout(function () { location.href = "/videos.html"; }, 2200); } - return; - } - if ((el = hit("data-lead"))) { - el.disabled = true; - r = await act("set_lead", { run_id: runId, draft_id: el.getAttribute("data-lead") }); - if (r) await refresh(); return; - } - if ((el = hit("data-draw"))) { - el.disabled = true; - r = await act("draw_storyboard", { draft_id: el.getAttribute("data-draw") }); - if (r) await refresh(); return; - } - if ((el = hit("data-reelgo"))) { - el.disabled = true; - var rl = await api("reel", { draft_id: CUR }); - if (!rl.ok) { showMsg("msg", rl.error || "Could not load the storyboard."); return; } - if (String(PKG.reel_state || "") !== "ready") { - el.textContent = "Speaking every shot…"; - REELWANT = true; - r = await act("prepare_reel", { draft_id: CUR }); - if (r) showMsg("msg", "Speaking " + r.shots + " shots. This is the same " - + "audio the film will use, so it is only paid for once. It starts " - + "playing on its own when it is ready.", "ok"); - else REELWANT = false; - await refresh(); - return; - } - hideMsg("msg"); - REEL = rl.shots || []; RAT = -1; - reelShow(0); - return; - } - if ((el = hit("data-reeltoggle"))) { - if (reelAudio.paused) { reelAudio.play().catch(function(){}); reelShow(RAT); } - else { reelAudio.pause(); clearInterval(RTIMER); RTIMER = null; reelPaint(); } - return; - } - if ((el = hit("data-reelstep"))) { - var d = Number(el.getAttribute("data-reelstep")) || 1; - reelShow(Math.max(0, Math.min((REEL || []).length - 1, RAT + d))); - return; - } - if ((el = hit("data-reelstop"))) { reelStop(); return; } - if ((el = hit("data-reeljump"))) { - var jid = el.getAttribute("data-reeljump"); - clearInterval(RTIMER); RTIMER = null; reelAudio.pause(); - var target = [...document.querySelectorAll(".shot")].find(function (x) { - var nu = x.querySelector(".num"); - return nu && nu.textContent.trim() === jid; }); - if (target) { - document.querySelectorAll(".shot.lit").forEach(function (x) { - x.classList.remove("lit"); }); - target.classList.add("lit"); - target.scrollIntoView({ behavior: "smooth", block: "center" }); - var ta = target.querySelector("textarea.note"); - if (ta) setTimeout(function () { ta.focus(); }, 500); - } - return; - } - if ((el = hit("data-cuts"))) { - el.disabled = true; el.textContent = "Working out what to drop…"; - r = await act("propose_cuts", { draft_id: el.getAttribute("data-cuts") }); - if (r) { showMsg("msg", "Asking for " + r.count + " different cuts.", "ok"); - setTimeout(refresh, 25000); } - return; - } - if ((el = hit("data-play"))) { - var cid = el.getAttribute("data-play"); - el.disabled = true; - var cc = await api("cut_clips", { draft_id: CUR, cut_id: cid }); - el.disabled = false; - if (!cc.ok) { showMsg("msg", cc.error || "That would not play."); return; } - QUEUE = (cc.shots || []).filter(function (s) { return s.clip_url; }); - if (!QUEUE.length) { - showMsg("msg", "None of the shots in this cut are filmed yet, so there " - + "is nothing to play. Film them first."); - return; - } - if (cc.filmed < cc.of) - showMsg("msg", "Playing the " + cc.filmed + " of " + cc.of - + " shots that are filmed. The rest are skipped."); - PLAYING = cid; QAT = 0; - await openTab(CUR); // the video element does not exist until this lands - playCut(); - return; - } - if ((el = hit("data-stop"))) { PLAYING = null; QUEUE = []; openTab(CUR); return; } - if ((el = hit("data-makecut"))) { - el.disabled = true; - r = await act("make_cut", { draft_id: CUR, cut_id: el.getAttribute("data-makecut") }); - if (r) { showMsg("msg", "Joining " + r.shots + " shots into this version. " - + "It appears in My videos with its own share page.", "ok"); - await refresh(); } - return; - } - if ((el = hit("data-measure"))) { - el.disabled = true; el.textContent = "Speaking every line…"; - r = await act("measure_script", { draft_id: el.getAttribute("data-measure") }); - if (r) { - showMsg("msg", "Speaking every line in the cast voices. A minute or two.", "ok"); - setTimeout(refresh, 20000); - } - return; - } - if ((el = hit("data-review"))) { - el.disabled = true; - r = await act("start_review", { run_id: runId }); if (r) await refresh(); return; - } - if ((el = hit("data-revise"))) { - el.disabled = true; - r = await act("start_revision", { run_id: runId }); if (r) await refresh(); return; - } - if ((el = hit("data-film"))) { - el.disabled = true; - r = await act("film_draft", { draft_id: el.getAttribute("data-film") }); - if (r) { showMsg("msg", "Filming " + r.shots + " shots, about $" + r.usd - + ". It appears in My videos as it goes.", "ok"); - setTimeout(function () { location.href = "/videos.html"; }, 2500); } - return; - } - if ((el = hit("data-retry"))) { - el.disabled = true; - r = await act("retry_writer", { draft_id: el.getAttribute("data-retry") }); - if (r) await refresh(); return; - } - - var btn = e.target.closest(".play"); - if (!btn) return; - var sid = btn.getAttribute("data-shot"); - btn.disabled = true; btn.innerHTML = "…"; - var a = await api("shot_audio", { draft_id: CUR, shot_id: sid }); - btn.disabled = false; btn.innerHTML = "▶"; - if (!a.ok) { showMsg("msg", a.error || "That would not play."); return; } - hideMsg("msg"); - if (!a.url) return; - document.querySelectorAll('[data-dur="' + sid + '"]').forEach(function (x) { - x.textContent = a.seconds.toFixed(1) + "s / " + Number(a.planned).toFixed(1) + "s"; - x.className = "dur" + (a.seconds > Number(a.planned) ? " over" : ""); - }); - audio.pause(); audio.src = a.url; audio.play(); - }); - - // Play the cut's clips one after another in a single element. This is the - // whole preview: no joining, no encoding, no upload, nothing to pay for. - function playCut() { - var v = document.getElementById("cutv"); - if (!v || !QUEUE.length) return; - var at = document.getElementById("cutat"); - function step() { - if (QAT >= QUEUE.length) { - if (at) at.textContent = "end of this cut"; - return; - } - var s = QUEUE[QAT]; - if (at) at.textContent = "shot " + s.id + " · " + (QAT + 1) - + " of " + QUEUE.length + (s.title ? " · " + s.title : ""); - v.src = s.clip_url; - v.play().catch(function () {}); - } - v.onended = function () { QAT += 1; step(); }; - v.onerror = function () { QAT += 1; step(); }; - step(); - } - - var noteTimers = {}; - document.getElementById("pane").addEventListener("input", function (e) { - var ta = e.target; - var sid = ta.getAttribute && ta.getAttribute("data-note"); - if (!sid) return; - ta.classList.remove("saved"); - clearTimeout(noteTimers[sid]); - noteTimers[sid] = setTimeout(async function () { - var r = await api("add_shot_note", { draft_id: CUR, shot_id: sid, note: ta.value }); - if (r.ok) { NOTES[sid] = ta.value; ta.classList.add("saved"); } - }, 700); - }); - - load(); -})(); - - -