create: choose the render model; length and price follow it

This commit is contained in:
gator 2026-09-14 02:54:56 +00:00
parent 0daf74cfac
commit dd92ed7aba

View file

@ -45,6 +45,12 @@
<span class="hint">This decides the set, the framing and how it is written, before a word exists.</span>
<select id="format"><option value="podcast">Podcast episode</option></select>
</div>
<div class="field">
<label for="model">Filmed with</label>
<span class="hint">Different engines shoot different shot lengths and cost different amounts. Leave it alone if you are not sure.</span>
<select id="model"></select>
<p class="foot" id="modelnote" style="margin:8px 0 0;color:var(--muted)"></p>
</div>
<div class="field">
<label for="scene">Override the setting <span style="color:var(--muted);font-weight:400">(optional)</span></label>
<span class="hint">Leave blank to use the format's own set. Real or not: a field of tall grass, floating on a surfboard.</span>
@ -64,11 +70,11 @@
<h2 class="sec" style="margin-top:34px">4 &middot; How long</h2>
<div class="field">
<div class="chips" id="len">
<span class="chip" data-v="2">16 seconds</span>
<span class="chip on" data-v="4">32 seconds</span>
<span class="chip" data-v="6">48 seconds</span>
<span class="chip" data-v="2">2 shots</span>
<span class="chip on" data-v="4">4 shots</span>
<span class="chip" data-v="6">6 shots</span>
</div>
<span class="hint" style="margin-top:10px;display:block">The piece is written as one continuous monologue, then cut into 8 second shots that flow into each other.</span>
<span class="hint" style="margin-top:10px;display:block" id="lenhint">The piece is written as one continuous monologue, then cut into shots that flow into each other.</span>
</div>
<div class="quote">
<div><div class="big" id="price">$8.85</div><div class="sub" id="plen">32 seconds &middot; 4 shots</div></div>
@ -136,11 +142,42 @@
navBar("create");
var email = requireSession();
var stance = "auto", segments = 4, havePets = false, FORMATS = [];
// The render engines the server will accept. Shot length and price both come
// from here rather than being assumed, because they differ per engine: Veo
// shoots 8s, Omni 1.1 shoots 10s.
var MODELS = [], MODEL = null;
function modelOf(id) {
for (var i = 0; i < MODELS.length; i++) if (MODELS[i].id === id) return MODELS[i];
return null;
}
function shotSeconds() { return (MODEL && MODEL.shot_seconds) || 8; }
(async function loadFormats() {
var r = await api("camera_presets", {});
if (!r.ok || !r.formats) return;
FORMATS = r.formats;
// Models. An older worker returns none, in which case the control stays
// hidden and the submission omits the field, so nothing breaks.
MODELS = r.models || [];
var msel = document.getElementById("model");
if (MODELS.length) {
msel.innerHTML = MODELS.map(function (m) {
return '<option value="' + esc(m.id) + '">' + esc(m.label) + '</option>';
}).join("");
msel.value = r.default_model || MODELS[0].id;
MODEL = modelOf(msel.value);
msel.addEventListener("change", function () {
MODEL = modelOf(msel.value);
paintModel();
paint();
});
paintModel();
paint();
} else {
msel.closest(".field").style.display = "none";
}
var sel = document.getElementById("format");
sel.innerHTML = FORMATS.map(function (f) {
return '<option value="' + esc(f.id) + '">' + esc(f.label) + '</option>';
@ -167,8 +204,28 @@ function chipGroup(id, cb) {
chipGroup("stance", function (v) { stance = v; });
chipGroup("len", function (v) { segments = parseInt(v, 10); paint(); });
// What the chosen engine costs us per shot, marked up the same way the server
// marks up. Mirrors quote_for(); if one changes, change both.
function paintModel() {
var el = document.getElementById("modelnote");
el.textContent = MODEL ? (MODEL.shot_seconds + "s per shot. " + (MODEL.note || "")) : "";
var chips = document.querySelectorAll("#len .chip");
for (var i = 0; i < chips.length; i++) {
var nshots = parseInt(chips[i].getAttribute("data-v"), 10);
chips[i].textContent = nshots + " shots · " + (nshots * shotSeconds()) + "s";
}
document.getElementById("lenhint").textContent =
"The piece is written as one continuous monologue, then cut into "
+ shotSeconds() + " second shots that flow into each other.";
}
function paint() {
var p = priceFor(segments);
if (MODEL && MODEL.usd_per_second) {
var cost = segments * MODEL.shot_seconds * MODEL.usd_per_second + 0.17;
p = { price: Math.round(cost * 5 * 100) / 100,
seconds: segments * MODEL.shot_seconds };
}
document.getElementById("price").textContent = "$" + p.price.toFixed(2);
document.getElementById("plen").textContent = p.seconds + " seconds · " + segments + " shots";
}
@ -364,6 +421,7 @@ document.getElementById("form").addEventListener("submit", async function (e) {
pet_name: opt ? opt.getAttribute("data-name") : "",
source_url: url, owner_notes: notes, scene: scene,
format: document.getElementById("format").value,
render_model: MODEL ? MODEL.id : "",
stance: stance, segments: segments, hold: true
});
if (!r.ok) {