diff --git a/create.html b/create.html index 6ddb751..24e9fc2 100644 --- a/create.html +++ b/create.html @@ -4,7 +4,7 @@ New video — PayToLaugh - + @@ -12,7 +12,7 @@

Make a new video

-

Pick the pet, paste whatever you want them to react to, and tell the director what you actually think. The opinion is yours; the delivery is theirs.

+

Pick the pet, paste whatever you want them to react to, and tell the director what you actually think. The opinion is yours; the delivery is theirs. You can direct every shot afterwards.

@@ -38,6 +38,13 @@ Say what you think and how you want it played. Two sentences is plenty. + +

3 · Where it happens

+
+ + A starting point for every shot, which you can change shot by shot later. Real or not: a podcast booth, a field of tall grass, floating on a surfboard. + +
@@ -48,27 +55,28 @@
-

3 · How long

+

4 · How long

16 seconds 32 seconds 48 seconds
+ The piece is written as one continuous monologue, then cut into 8 second shots that flow into each other.
$8.85
32 seconds · 4 shots
-
You approve the script before anything is filmed.
+
You direct and approve before anything is filmed.
- +

- What happens next. Submitting records your request and puts it in the - queue. Nothing is charged and nothing is filmed yet. Payment and automatic - production get switched on once proper sign-in is in place. + What happens next. The writer starts straight away. You will be able to + read it, change the setting and camera on every shot, ask for rewrites, and approve it. + Nothing is charged and nothing is filmed yet.

@@ -129,21 +137,23 @@ document.getElementById("form").addEventListener("submit", async function (e) { var btn = document.getElementById("go"); var url = document.getElementById("url").value.trim(); var notes = document.getElementById("notes").value.trim(); + var scene = document.getElementById("scene").value.trim(); var opt = petSel.options[petSel.selectedIndex]; if (!url) { showMsg("msg", "Paste a link to whatever you want them to react to."); return; } if (notes.length < 10) { showMsg("msg", "Tell the director what you think, in a sentence or two."); return; } - btn.disabled = true; btn.textContent = "Submitting…"; + btn.disabled = true; btn.textContent = "Writing…"; var r = await api("create_project", { email: email, pet_id: petSel.value, pet_name: opt ? opt.getAttribute("data-name") : "", - source_url: url, owner_notes: notes, stance: stance, segments: segments + source_url: url, owner_notes: notes, scene: scene, + stance: stance, segments: segments }); - btn.disabled = false; btn.textContent = "Submit for scripting"; + btn.disabled = false; btn.textContent = "Write the script"; if (!r.ok) { showMsg("msg", r.error || "That did not go through. Please try again."); return; } - showMsg("msg", "Submitted. Taking you to your videos…", "ok"); - setTimeout(function () { location.href = "/videos.html"; }, 650); + showMsg("msg", "Writing it now…", "ok"); + setTimeout(function () { location.href = "/script.html?id=" + encodeURIComponent(r.project.id); }, 500); }); diff --git a/script.html b/script.html index 36cf21b..66adca3 100644 --- a/script.html +++ b/script.html @@ -3,17 +3,28 @@ -Review the script — PayToLaugh - +Direct the shots — PayToLaugh + @@ -21,7 +32,7 @@

The script

-

Read it through. If it does not sound like your pet, say so and we will rewrite it.

+

Read it through, then direct it. Every shot can have its own setting and camera move.

@@ -37,31 +48,60 @@ navBar("videos"); var email = requireSession(); var pid = new URLSearchParams(location.search).get("id"); -var poll = null; +var poll = null, PRESETS = [], PHOTOS = [], DATA = null; if (!pid) { document.getElementById("out").innerHTML = '
No script chosen. Back to my videos.
'; } -function segHtml(s) { - return '
' - + '
Shot ' + (s.index || "") + '' - + '' + esc(s.camera || "") + '
' - + '

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

' - + '
' + (s.word_count || 0) + ' words · about ' + (s.est_seconds || 0) + 's' - + (s.note ? ' · ' + esc(s.note) : '') + '
' +function photoOptions(sel) { + if (!PHOTOS.length) return ''; + return PHOTOS.map(function (p) { + var label = p.kind.charAt(0).toUpperCase() + p.kind.slice(1); + return ''; + }).join(""); +} + +function shotHtml(s, locked) { + var img = s.plate_url + ? 'Shot ' + s.index + ' opening frame' + : '
No opening frame yet.
Set the scene, then make one.
'; + + var controls = locked ? '' : + '' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + '
'; + + return '
' + + '
' + img + '
' + + '
' + + 'Shot ' + (s.index || "") + '' + + (s.continues ? 'continues the last shot' : '') + + '

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

' + + '

' + (s.word_count || 0) + ' words · about ' + (s.est_seconds || 0) + 's

' + + '
' + controls + '
' + + '
'; } function render(r) { + DATA = r; var p = r.project, s = r.script, out = document.getElementById("out"); var used = parseInt(p.revisions || 0, 10); var left = (r.max_revisions || 3) - used; if (p.status === "scripting") { out.innerHTML = '

' - + 'Writing the script now. This usually takes under a minute — the page will update itself.

'; + + 'Writing now. This usually takes under a minute — the page updates itself.

'; if (!poll) poll = setInterval(load, 5000); return; } @@ -72,42 +112,88 @@ function render(r) { + 'Start again'; return; } - if (!s || !s.segments) { - out.innerHTML = '
No script on this project yet.
'; - return; - } + if (!s || !s.segments) { out.innerHTML = '
No script yet.
'; return; } document.getElementById("title").textContent = s.title || "The script"; document.getElementById("sub").textContent = s.premise || ""; - var approved = p.status === "approved"; + var locked = p.status === "approved"; var warn = (s._warnings && s._warnings.length) - ? '
Worth a look: ' + s._warnings.map(esc).join(" · ") + '
' - : ""; + ? '
Worth a look: ' + s._warnings.map(esc).join(" · ") + '
' : ""; var head = '
' + '
$' + Number(p.price_usd || 0).toFixed(2) + '
' + '
' + ((p.segments || 4) * 8) + ' seconds · ' + (p.segments || 4) + ' shots
' - + '
' + (approved ? "Approved" : left + " rewrite" + (left === 1 ? "" : "s") + " left") + '
' - + '
'; + + '
' + (locked ? "Approved" : left + " rewrite" + (left === 1 ? "" : "s") + " left") + '
'; - var actions = approved - ? '

Approved. Filming is switched on once payment is live. ' - + 'Nothing has been charged.

' + var full = s.full_text + ? '

The whole piece

' + esc(s.full_text) + '
' : ""; + + var cams = '' + PRESETS.map(function (c) { + return ''; + + var actions = locked + ? '

Approved. Filming is switched on once payment is live. Nothing has been charged.

' : '
' - + '

Not quite right?

' - + '
' - + '' - + 'Be specific. “Too polite, he would be smugger about it” beats “make it funnier”.' - + '' - + '
' + + '

Change the words

' + + '
' + + 'Be specific. “Too polite, he would be smugger” beats “make it funnier”. ' + + 'Setting and camera you can just edit directly above.' + + '
' + '' - + '' + + (left > 0 ? "Rewrite the words" : "No rewrites left") + '' + + '' + '
'; - out.innerHTML = head + warn + s.segments.map(segHtml).join("") + actions; + out.innerHTML = cams + head + warn + full + + '

The shots

' + + s.segments.map(function (x) { return shotHtml(x, locked); }).join("") + + actions; + wire(locked); +} + +function wire(locked) { + if (locked) { bindBottom(); return; } + + // Save edits when the field loses focus, so typing is never interrupted. + [].forEach.call(document.querySelectorAll("[data-scene],[data-cam],[data-photo]"), function (el) { + var evt = el.tagName === "SELECT" ? "change" : "blur"; + el.addEventListener(evt, async function () { + var idx = el.getAttribute("data-scene") || el.getAttribute("data-cam") || el.getAttribute("data-photo"); + var body = { email: email, project_id: pid, index: parseInt(idx, 10) }; + if (el.hasAttribute("data-scene")) body.scene = el.value; + if (el.hasAttribute("data-cam")) body.camera = el.value; + if (el.hasAttribute("data-photo")) body.photo_id = el.value; + var r = await api("update_shot", body); + if (!r.ok) showMsg("msg", r.error || "That change did not save."); + else hideMsg("msg"); + }); + }); + + [].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 }); + b.disabled = false; + if (!r.ok) { b.textContent = "Try again"; showMsg("msg", r.error || "Could not make that frame."); return; } + load(); + }); + }); + + bindBottom(); +} + +function bindBottom() { var rv = document.getElementById("revise"); if (rv) rv.onclick = async function () { hideMsg("msg"); @@ -115,17 +201,16 @@ function render(r) { if (fb.length < 5) { showMsg("msg", "Tell us what you would like changed."); return; } rv.disabled = true; rv.textContent = "Sending…"; var res = await api("revise_script", { email: email, project_id: pid, feedback: fb }); - if (!res.ok) { rv.disabled = false; rv.textContent = "Rewrite it"; showMsg("msg", res.error || "That did not go through."); return; } + if (!res.ok) { rv.disabled = false; rv.textContent = "Rewrite the words"; showMsg("msg", res.error || "That did not go through."); return; } showMsg("msg", "Rewriting now…", "ok"); load(); }; - var ap = document.getElementById("approve"); if (ap) ap.onclick = async function () { hideMsg("msg"); ap.disabled = true; ap.textContent = "Approving…"; var res = await api("approve_script", { email: email, project_id: pid }); - if (!res.ok) { ap.disabled = false; ap.textContent = "Approve this script"; showMsg("msg", res.error || "That did not go through."); return; } + if (!res.ok) { ap.disabled = false; ap.textContent = "Approve and lock it"; showMsg("msg", res.error || "That did not go through."); return; } showMsg("msg", res.status_text || "Approved.", "ok"); load(); }; @@ -133,8 +218,16 @@ function render(r) { async function load() { if (!pid || !email) return; + if (!PRESETS.length) { + var c = await api("camera_presets", {}); + if (c.ok) PRESETS = c.presets || []; + } var r = await api("get_project", { email: email, project_id: pid }); if (!r.ok) { showMsg("msg", r.error || "Could not load that script."); return; } + if (!PHOTOS.length && r.project && r.project.pet_id) { + var ph = await api("list_photos", { email: email, pet_id: r.project.pet_id }); + if (ph.ok) PHOTOS = ph.photos || []; + } render(r); } load();