Storyboard: one long take of several shots on Seedance (to 30 s); H3 Max Turbo in the camera list
This commit is contained in:
parent
b4543b9c13
commit
ebafaf6be1
1 changed files with 55 additions and 2 deletions
57
trailer.html
57
trailer.html
|
|
@ -451,6 +451,40 @@
|
||||||
"fal-seedance-2.5-480p": { name: "Seedance 2.5 from fal, standard picture", rate: 0.2205, min: 4, max: 30 },
|
"fal-seedance-2.5-480p": { name: "Seedance 2.5 from fal, standard picture", rate: 0.2205, min: 4, max: 30 },
|
||||||
"fal-seedance-2.5-720p": { name: "Seedance 2.5 from fal, sharper picture", rate: 0.4730, min: 4, max: 30 } };
|
"fal-seedance-2.5-720p": { name: "Seedance 2.5 from fal, sharper picture", rate: 0.4730, min: 4, max: 30 } };
|
||||||
CAMERAS["minimax-h3"] = CAMERAS[""];
|
CAMERAS["minimax-h3"] = CAMERAS[""];
|
||||||
|
CAMERAS["h3-max-turbo"] = { name: "the tuned camera (H3 Max Turbo)", rate: 0.04, min: 5, max: 15 };
|
||||||
|
var LONG_CAMERAS = ["hf-seedance-2.5-480p", "hf-seedance-2.5-720p", "fal-seedance-2.5-480p", "fal-seedance-2.5-720p"];
|
||||||
|
// The shots a long take would hold, and how many seconds they run together.
|
||||||
|
function longRun(fromId, toId) {
|
||||||
|
var all = (PKG.shots || []).filter(function (x) { return !x.floor; })
|
||||||
|
.sort(function (a, b) { return String(a.id) < String(b.id) ? -1 : 1; });
|
||||||
|
var ids = all.map(function (x) { return String(x.id); });
|
||||||
|
var a = ids.indexOf(String(fromId)), b = ids.indexOf(String(toId));
|
||||||
|
if (a < 0 || b <= a) return null;
|
||||||
|
var run = all.slice(a, b + 1), secs = 0;
|
||||||
|
run.forEach(function (x) { secs += Math.max(2, Math.round(Number(x.edit_seconds) || 4)); });
|
||||||
|
return { shots: run.length, seconds: secs };
|
||||||
|
}
|
||||||
|
function longTakeHtml(s) {
|
||||||
|
var lt = s.long_take || {};
|
||||||
|
var h = '<div style="margin-top:8px;border-top:1px dashed var(--line,#ccc);padding-top:6px">'
|
||||||
|
+ '<label><b>One long take</b> — this shot and the ones after it filmed as a single take of up to 30 seconds, '
|
||||||
|
+ 'the picture cutting from person to person inside it. The long-take camera speaks the lines itself, in voices like our actors’. '
|
||||||
|
+ 'It is kept here beside the shots’ own clips and replaces none of them.</label>'
|
||||||
|
+ 'from shot ' + esc(s.id) + ' through shot <input type="text" data-ltto="' + esc(s.id) + '" value="' + esc(lt.to || "") + '" style="width:56px;font:inherit;font-size:12px"> on '
|
||||||
|
+ '<select data-lteng="' + esc(s.id) + '">' + LONG_CAMERAS.map(function (k) {
|
||||||
|
return '<option value="' + k + '"' + ((lt.engine || LONG_CAMERAS[0]) === k ? ' selected' : '') + '>' + CAMERAS[k].name + ' — $' + CAMERAS[k].rate.toFixed(2) + ' a second</option>'; }).join("") + '</select> '
|
||||||
|
+ '<button type="button" class="btn small" data-longtake="' + esc(s.id) + '">' + (lt.clip_url ? 'Film the long take again' : 'Film the long take') + '</button>';
|
||||||
|
if (lt.state === "filming") h += '<p class="tiny">The long take is being filmed (started ' + esc(String(lt.since || "").replace("T", " ").slice(0, 16)) + '). It takes several minutes; reload the page to see it.</p>';
|
||||||
|
if (lt.error) h += '<p class="tiny" style="color:#b4531f">The long take could not be made: ' + esc(lt.error) + '</p>';
|
||||||
|
if (lt.unchanged) h += '<p class="tiny">Nothing that reaches the camera has changed since this long take was filmed, so it was not bought again.</p>';
|
||||||
|
if (lt.clip_url) h += '<div class="frames"><figure><video src="' + esc(lt.clip_url) + '" controls preload="metadata"></video><figcaption>one long take, shots '
|
||||||
|
+ esc((lt.shots || []).join(", ")) + ' · ' + Number(lt.clip_seconds || 0).toFixed(1) + 's'
|
||||||
|
+ madeOn({ filmed_on: lt.engine, film_usd: lt.usd, film_wait: lt.wait })
|
||||||
|
+ ((lt.check || {}).verdict ? ' · words: ' + esc(lt.check.verdict) : '')
|
||||||
|
+ ((lt.check || {}).heard ? '<br>heard: “' + esc(lt.check.heard) + '”' : '')
|
||||||
|
+ '</figcaption></figure></div>';
|
||||||
|
return h + '</div>';
|
||||||
|
}
|
||||||
function cameraOf(x) { return CAMERAS[(x && x.engine) || ""] || CAMERAS[""]; }
|
function cameraOf(x) { return CAMERAS[(x && x.engine) || ""] || CAMERAS[""]; }
|
||||||
function filmCost(x) { var c = cameraOf(x);
|
function filmCost(x) { var c = cameraOf(x);
|
||||||
return Math.min(c.max, Math.max(c.min, Math.ceil((Number(x.edit_seconds) || 5) + 1.5))) * c.rate; }
|
return Math.min(c.max, Math.max(c.min, Math.ceil((Number(x.edit_seconds) || 5) + 1.5))) * c.rate; }
|
||||||
|
|
@ -1317,12 +1351,13 @@
|
||||||
+ '</select></label>')
|
+ '</select></label>')
|
||||||
+ '<label style="margin-top:6px">Which camera films this shot '
|
+ '<label style="margin-top:6px">Which camera films this shot '
|
||||||
+ '<select data-engine="' + esc(s.id) + '">'
|
+ '<select data-engine="' + esc(s.id) + '">'
|
||||||
+ ["", "hf-seedance-2.5-480p", "hf-seedance-2.5-720p", "fal-seedance-2.5-480p", "fal-seedance-2.5-720p"].map(function (k) {
|
+ ["", "h3-max-turbo", "hf-seedance-2.5-480p", "hf-seedance-2.5-720p", "fal-seedance-2.5-480p", "fal-seedance-2.5-720p"].map(function (k) {
|
||||||
var c = CAMERAS[k];
|
var c = CAMERAS[k];
|
||||||
return '<option value="' + k + '"' + ((s.engine || "") === k ? ' selected' : '') + '>'
|
return '<option value="' + k + '"' + ((s.engine || "") === k ? ' selected' : '') + '>'
|
||||||
+ (k ? 'The long-take camera: ' + c.name : 'The usual camera')
|
+ (k === "h3-max-turbo" ? 'The tuned camera (H3 Max Turbo)' : k ? 'The long-take camera: ' + c.name : 'The usual camera')
|
||||||
+ ' — a shot up to ' + c.max + ' seconds, about $' + c.rate.toFixed(2) + ' a second</option>'; }).join("")
|
+ ' — a shot up to ' + c.max + ' seconds, about $' + c.rate.toFixed(2) + ' a second</option>'; }).join("")
|
||||||
+ '</select></label>'
|
+ '</select></label>'
|
||||||
|
+ longTakeHtml(s)
|
||||||
+ '</div>';
|
+ '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2356,6 +2391,24 @@
|
||||||
}, 700);
|
}, 700);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("pane").addEventListener("click", async function (e) {
|
||||||
|
var b = e.target.closest && e.target.closest("[data-longtake]");
|
||||||
|
if (!b) return;
|
||||||
|
var sid = b.getAttribute("data-longtake");
|
||||||
|
var to = ((document.querySelector('[data-ltto="' + sid + '"]') || {}).value || "").trim();
|
||||||
|
var eng = (document.querySelector('[data-lteng="' + sid + '"]') || {}).value || LONG_CAMERAS[0];
|
||||||
|
var run = longRun(sid, to);
|
||||||
|
if (!run) { showMsg("msg", "Give the last shot of the long take — a later shot of this script, by its number."); return; }
|
||||||
|
if (run.seconds > 30) { showMsg("msg", "Shots " + sid + " to " + to + " run " + run.seconds + " seconds together; one take holds 30. End it on an earlier shot."); return; }
|
||||||
|
var had = ((PKG.shots || []).filter(function (x) { return String(x.id) === sid; })[0] || {}).long_take || {};
|
||||||
|
if (!confirm(run.shots + " shots as one take of " + run.seconds + " seconds on " + CAMERAS[eng].name
|
||||||
|
+ ": about $" + (run.seconds * CAMERAS[eng].rate).toFixed(2) + ". Film it?")) return;
|
||||||
|
var r = await api("long_take", { draft_id: CUR, shot_id: sid, to_shot: to, engine: eng, force: !!had.clip_url && had.to === to && had.engine === eng });
|
||||||
|
await refresh();
|
||||||
|
if (r.ok) showMsg("msg", "The long take is being filmed: " + r.shots + " shots, " + r.seconds + " seconds, about $" + Number(r.usd).toFixed(2) + ". It takes several minutes; reload the page to see it.", "ok");
|
||||||
|
else showMsg("msg", r.error || "The long take could not be started.");
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById("pane").addEventListener("change", async function (e) {
|
document.getElementById("pane").addEventListener("change", async function (e) {
|
||||||
var el = e.target;
|
var el = e.target;
|
||||||
if (el.getAttribute && el.getAttribute("data-pagepick")) {
|
if (el.getAttribute && el.getAttribute("data-pagepick")) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue