diff --git a/trailer.html b/trailer.html
index 1fa4c3a..380abb5 100644
--- a/trailer.html
+++ b/trailer.html
@@ -1084,7 +1084,7 @@
+ fold("views", "Master pictures for each set-up", "", viewsHtml(draft))
+ fold("cast", "The cast", cast.length + " characters - faces, looks, voices", castHtml)
+ fold("props", "Things that must look the same in every shot", ((PKG.props || []).length || "no") + " tracked", propsHtml())
- + body;
+ + rangeHtml() + body;
}
// FOLDS. The cast, the props, the story notes and the master pictures are
@@ -1746,6 +1746,26 @@
+ '';
}
+ // A link of many clips can take longer than the proxy's 5 s; it is made
+ // anyway, and asking again returns the same link at once.
+ async function shareRetry(payload) {
+ var r1 = await api("share_clips", payload);
+ if (!r1.ok && /timed out/i.test(r1.error || "")) r1 = await api("share_clips", payload);
+ if (!r1.ok) { showMsg("msg", r1.error || "That did not work."); return null; }
+ hideMsg("msg");
+ return r1;
+ }
+ var RANGE = { from: "", to: "" };
+ document.addEventListener("input", function (e) {
+ if (e.target && e.target.id === "rgfrom") RANGE.from = e.target.value;
+ if (e.target && e.target.id === "rgto") RANGE.to = e.target.value;
+ });
+ function rangeHtml() {
+ return '
Watch a run of shots from shot '
+ + ' to '
+ + ' '
+ + '
';
+ }
async function act(name, payload) {
var r = await api(name, payload);
if (!r.ok) { showMsg("msg", r.error || "That did not work."); return null; }
@@ -1821,10 +1841,27 @@
else el.disabled = false;
return;
}
+ if ((el = hit("data-rangeplay"))) {
+ // A RUN OF SHOTS: every filmed shot from one number to another, off
+ // the cutting room floor, as one link that plays them in order.
+ var rf = String((document.getElementById("rgfrom") || {}).value || "").trim(), rt = String((document.getElementById("rgto") || {}).value || "").trim();
+ var pad = function (x) { return /^\d+$/.test(x) ? ("000" + x).slice(-3) : x; };
+ rf = pad(rf); rt = pad(rt);
+ var run = (PKG.shots || []).slice().sort(function (a, b) { return String(a.id).localeCompare(String(b.id)); })
+ .filter(function (x) { var id = String(x.id); return x.clip_url && !x.floor && (!rf || id >= rf) && (!rt || id <= rt || id.indexOf(rt) === 0); })
+ .map(function (x) { return x.id; });
+ if (run.length < 2) { showMsg("msg", "There are fewer than two filmed shots in that range."); return; }
+ if (run.length > 80) { showMsg("msg", "That is " + run.length + " shots; a link holds at most 80. Choose a shorter range."); return; }
+ el.disabled = true;
+ r = await shareRetry({ draft_id: CUR, shot_ids: run, label: "Shots " + run[0] + " to " + run[run.length - 1] });
+ el.disabled = false;
+ if (r) { var lk = document.getElementById("rglink"); if (lk) lk.innerHTML = '' + esc(r.url) + ''; showMsg("msg", r.clips + " shots, played in order: " + r.url, "ok"); }
+ return;
+ }
if ((el = hit("data-sharescene"))) {
var sids = el.getAttribute("data-sharescene").split(",");
el.disabled = true;
- r = await act("share_clips", { draft_id: CUR, shot_ids: sids, label: el.getAttribute("data-scenelabel") });
+ r = await shareRetry({ draft_id: CUR, shot_ids: sids, label: el.getAttribute("data-scenelabel") });
if (r) { SHARES["scene:" + sids.join(",")] = r.url; showMsg("msg", r.clips + " clips, played in order: " + r.url, "ok"); openTab(CUR); }
else el.disabled = false;
return;