Commit graph

102 commits

Author SHA1 Message Date
2467b779b0 Show a freshly computed price, not the one stamped at creation
price_usd is written onto the project row when it is created, so changing the
markup constant left every existing project quoting the old number - the page
still said 40.85 after the server had recomputed it to 8.13.

get_project already returns a fresh quote. Use it, and fall back to the stored
value only for a response that predates it.
2026-09-15 02:15:07 +00:00
10c1541dbc Clamp the view to what the stepper allows, not to the stage
Making step 3 clickable was not enough: render clamped VIEW back down to the
project's stage on every draw, so the click set VIEW = 3 and the redraw
immediately put it back to 2. The Filming panel stayed unreachable.
2026-09-15 02:06:51 +00:00
4292624eda Wire the Remove cutaway button
Clears the cutaway from the shot so it is not cut into the film. The subject
stays, so Make cutaway can try again with a different draw.
2026-09-15 02:05:20 +00:00
58ccf6fc6d Layout view: stop crying wolf on short shots, allow removing a cutaway, make filming reachable
- "off the clock" fired on ANY shot not within 1.2s of the clip length, so a
  7.0s line in a 10s shot was flagged alongside a genuine 11.4s overrun. Under
  is fine - the engine finishes early and waits. Only an overrun gets cut off
  mid-word, and only that is now flagged, as "runs long".
- A cutaway could be made or remade but never removed, and the subject is the
  writer's guess so it is often wrong. There is now a Remove button.
- The Filming step was disabled until the project was already filming, so the
  only way to start was a button at the bottom of the Layout page. Approved now
  reaches step 3.
- "Cutting to the clock ... rewrites how the lines are divided" is no longer
  true - it measures and no longer moves boundaries, because Setup shots does
  the division locally. Copy corrected.
- "speaking the words costs about a hundredth of a cent" was wrong by three
  orders of magnitude; a measured run is about 16 cents.
2026-09-15 02:05:07 +00:00
70fa5ae9bb Split Setup shots from Script audio; name the voice; drop the rewrite cap
The one button did two unrelated jobs and showed the result of neither.

- Setup shots is now local division only: Python at the engine's own measured
  speaking rate, ~500ms, free, no LLM and no TTS. Answers "divide with local
  code, because each voice has a different cadence".
- Script audio is a separate button that reads it aloud AND SHOWS THE PLAYER.
  The audio was always being made and uploaded, then rendered into #alignout,
  which only exists on the Layout view - so pressing the button on the Words
  view produced fifteen seconds of nothing.
- The voice is now named. With an Omni engine the film ships Omni's own native
  voice, so the ElevenLabs read-through is a stand-in and says so.
- Rewrites are no longer capped at three. The real cost from OpenRouter is
  shown beside the buttons, added to the bill at cost with no markup.
- "Approve and lock it" now explains what it does.
2026-09-14 23:26:01 +00:00
076621f809 Setup shots: quote the real TTS cost, not "about a cent"
A measured run of the 80s NIMBY script came back at $0.1589 for 946 characters
- the ElevenLabs rate of $22 per 131,000 - so the copy understated it by more
than tenfold. Compute it from the words actually in the box instead.
2026-09-14 23:00:30 +00:00
30fde992dc Review page: the narrative first, the controls under it, the shots cut on request
The whole piece is now editable at the top of the review page, the rewrite
controls sit directly under it rather than below eight shot cards, and cutting
the narrative into shots is an explicit Setup shots button.

- narrative editable via set_narrative; shots marked stale until re-cut
- writer dropdown (Grok 4.6/4.5, Claude Opus 5/Sonnet 5, GPT-5.6/5.5, Gemini 3.1)
- align no longer runs unasked on page load, which spoke the script and moved
  every shot boundary for a cent nobody asked to spend
- align's fit check reads shot length from the model instead of assuming 8s,
  which reported every Omni shot 2s short
2026-09-14 22:55:33 +00:00
f19c3ea88b create: disable shot counts an engine cannot produce 2026-09-14 19:10:56 +00:00
a5174a3546 remove the dollar-token probe file 2026-09-14 13:36:00 +00:00
bf4cc12b2f script: rebuild part 6 of 6 - corruption repaired 2026-09-14 13:35:32 +00:00
dd1ad70fb7 script: rebuild part 5 of 6 2026-09-14 13:34:44 +00:00
7ce21f6621 script: rebuild part 4 of 6 2026-09-14 13:33:51 +00:00
0dabc60006 script: rebuild part 3 of 6 2026-09-14 13:32:57 +00:00
ab1027e6db script: rebuild part 2 of 6 2026-09-14 13:32:01 +00:00
9250a559c3 script: rebuild part 1 of 3 (repairing $' corruption) 2026-09-14 13:29:39 +00:00
b3c2128a44 scratch: probe before-match token 2026-09-14 13:26:46 +00:00
ad79f668bf scratch: confirm backtick token exposes text before the match 2026-09-14 13:26:03 +00:00
b1af9ad85b scratch: probe dollar-token behaviour in replacements 2026-09-14 13:24:17 +00:00
46b35556f9 scratch: determine how patch replacement handles dollar tokens 2026-09-14 13:24:12 +00:00
d8746810cc script: price and duration follow the project's render model, not a fixed 8s 2026-09-14 13:14:13 +00:00
dd92ed7aba create: choose the render model; length and price follow it 2026-09-14 02:54:56 +00:00
0daf74cfac admin: name the engine that filmed each shot, not always Veo 2026-09-14 02:54:09 +00:00
aed4812b7e admin: redub is opt-in now, so the switch reads that way round 2026-09-10 14:00:41 +00:00
58c8e92c3a pets: the voice description Veo is given is now selectable and editable 2026-09-10 13:13:20 +00:00
89b78e5ae2 admin: show the filmed date beside the created date 2026-09-10 12:52:10 +00:00
17fe951397 videos: show when it was filmed, not only when it was submitted 2026-09-10 12:52:03 +00:00
ddf6056d43 admin: copy the prompt, and list the renditions derived from the master 2026-09-10 12:22:16 +00:00
30da437300 admin: show the delivery direction, the request actually sent, and a redub switch 2026-09-10 01:15:49 +00:00
f6f1335fff Use the rendered share address here too
The link in the message under the picture now comes from share_link, which
returns the worker-rendered page rather than a guessed path on this site.
2026-09-09 02:21:45 +00:00
6b7ef08fcc Share links come from the worker now, not from a published file
Publishing wrote an HTML file into this repository and triggered a full site
deploy on every share. The page is now rendered per request on its own
hostname, so there is nothing to publish: share_link just marks the video
shared and hands back the address.
2026-09-09 02:21:33 +00:00
7407a945ac remove routing probe 2026-09-09 02:02:00 +00:00
abb070d845 remove routing probe 2026-09-09 02:01:59 +00:00
60ce736a06 remove routing probe 2026-09-09 02:01:57 +00:00
77d76f1d7f Share the page that has preview tags, not the JavaScript route
Preview crawlers do not run JavaScript, so the /v/<code> route — which is the
home page plus a client-side redirect — gives them nothing. The published
/v/<code>.html page carries real og:image, og:video and a real video element in
the served source. Five characters longer, and the difference between a card
someone taps and a bare grey link.
2026-09-09 02:01:08 +00:00
25ad188e99 Share page for Astro Formally Protests Cleaning Robots 2026-09-09 02:00:05 +00:00
3690278211 probe 2026-09-09 01:57:00 +00:00
5a409e943f Second routing probe: an explicit .html file per code
Directory indexes fall back to the root page, but files with an extension are
served directly. If this resolves, per-video pages with real Open Graph tags in
the head are possible on a static host, at the cost of five characters.
2026-09-09 01:55:42 +00:00
8a5f3a7b1b Routing probe: does a per-code directory index serve at /v/<code>?
Throwaway file to establish whether static per-video pages with baked-in
Open Graph tags can live at the short URL, or whether the host will keep
falling back to the root index.
2026-09-09 01:55:10 +00:00
755e831891 Share the short link here too, and show the view count
The message under the picture now carries the tracked link rather than the raw
file, and the card reports how many times it has been opened.
2026-09-09 01:14:55 +00:00
9051af0f3d Share the short link, not the file
A link straight to the file cannot be counted — the recipient fetches bytes
from the file host and we never hear about it. The short link runs through our
own domain, so every open is a page load, and it is 33 characters against 74.
2026-09-09 01:14:44 +00:00
a82e95d9cc Send /v/<code> to the watch page
The host serves this file for any extensionless path it does not recognise, so
/v/<code> lands here rather than at v/index.html. Four lines at the top of the
head hand it on before anything renders.

Deliberately the first thing in the head and wrapped in a try, so a share link
resolves before any paint and a failure here can never take the homepage down
with it.
2026-09-09 01:14:31 +00:00
789c4fef55 A watch page at /v/<code>, so shares run through our own domain
A link straight to the file is untrackable — the recipient fetches bytes from
the file host and we never hear about it. Routing through paytolaugh.com makes
every open a page load that can be counted, and gives somewhere to put a real
player and a preview card.

The code is the first eight hex characters of the project id. Those are already
unique across every project and the space is 4.3 billion, so there is no
mapping table to build or keep in sync — the server resolves by prefix and
checks for ambiguity. 33 characters against 74 for the raw file.

Sharing stays deliberate: the page only resolves a project whose owner has
pressed Share.
2026-09-09 01:13:35 +00:00
6dcbfbd035 Wire the share button up
The button was rendered but nothing listened for it.
2026-09-09 01:08:46 +00:00
9801f1a8d1 Share button on the videos list
Sharing from here is the common case — you are looking at the thing you want to
send. Uses the native share sheet where there is one, otherwise copies, and the
link carries a filename so it ends in .mp4 for clients that read the URL rather
than the headers.
2026-09-09 01:08:23 +00:00
e0db55e0db Share the video, with a link that ends in .mp4
The file URL ends in /public with no filename, which is fine for anything that
reads headers — it already serves Content-Type video/mp4 and a
Content-Disposition of final.mp4 — but clients that sniff the extension out of
the URL string get it wrong. A filename query is accepted and ignored by the
file service, so the link can end in .mp4 at a cost of a dozen characters.

The button uses the native share sheet where there is one, which on a phone is
the shortest path into a text message, and falls back to copying.
2026-09-09 01:08:09 +00:00
b94b0557c2 A still worth texting, with the link that goes under it
Sending the video itself by MMS puts it through carrier transcoding and it
arrives looking far worse than it left. So the picture carries the hook and a
link carries the video at full quality.

The still is 720x1280 and lands around 90KB at near-maximum quality — carrier
limits are typically 600KB and up, so there is no reason to compress hard. The
frame is chosen by sampling several moments and keeping the one that compresses
largest, which in practice means eyes open and in focus rather than mid-blink.
The play button sits at two thirds height so it never covers the face.
2026-09-09 01:03:03 +00:00
4d553585d6 Cut the shots where the clock says, not where a word count guesses
"I am not" and "extraordinarily unimpressed" are both three words and nowhere
near the same length, so any word budget is a guess about something we can
measure. Three attempts at tuning one were wrong in both directions.

Speaking the monologue once returns the start and end of every character, so
word boundaries are exact. This cuts each shot at the word whose end sits
nearest the boundary, preferring a sentence ending when one is close. On the
first real test it produced a 20 word shot landing at 7.8s and cutting on a
full stop, where the budget had said 18.
2026-09-08 14:46:15 +00:00
c26efbbea7 Correct the previz for the difference between spoken and filmed pace
The run-through is spoken by ElevenLabs, but the finished audio is Veo's
performance re-voiced, and Veo enunciates more slowly - it was asked to, to
make the mouth movement readable. Measured on a real clip: 17 words ran 7.02s
filmed (about 2.4 words a second) against roughly 3.0 words a second spoken.

So the raw previz number understates the filmed length by about a quarter, and
the dead-air warning was correspondingly overstated - it claimed 2.4 seconds of
silence per shot where the filmed figure is nearer one. Show both numbers and
name the factor rather than quietly applying it.
2026-09-08 13:05:14 +00:00
447368e355 Put the big rough voices at the top of the list
The catalogue was 21 polished professional voices — podcast hosts and
narrators. None of them sounds like ninety pounds of jowly bulldog, which is
the actual complaint. Seven deep, gravelly, weathered voices are added, and
they go first because a talking animal usually wants weight rather than polish.

Without a group of their own they would not have appeared at all: the dropdown
only built male, female and neutral.
2026-09-08 03:18:22 +00:00
d9167ede59 Warn about dead air, not just overrun
The first real measurement was the opposite of what was expected: four lines
spoke in 21.7s against 32s of video, so every eight second shot carries about
2.4 seconds of silence. That is almost certainly where the sound effects and
echo come from, since the model fills a gap it was given nothing to do with.

So flag short as loudly as long, and say what it means.
2026-09-07 23:28:25 +00:00