Commit graph

64 commits

Author SHA1 Message Date
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
fb1d9f229e Hear the script before paying to film it
Filming is $0.40 a shot. Speaking the same words is a few hundredths of a cent.
So the narrative — does it flow, is the joke in the right place, does it drag —
can now be judged for about one percent of the cost, using the opening stills
as placeholders and the pet's real voice.

It also replaces a guess with a measurement. Line length was estimated from a
word count and hoped to fit eight seconds; speaking it says exactly how long it
takes, and a line that runs over is flagged before anything is filmed rather
than after.
2026-09-07 23:27:57 +00:00
13a45d2e87 Fix the header claiming an unapproved script was approved
"locked" used to mean "the script is approved" and now means "not on the layout
stage", so the price card read Approved on every stage but one — including on a
script still waiting to be read. It should follow whether the words are still
open, which is what canEditScript says.
2026-09-07 23:25:29 +00:00
29a054f8c8 One stage at a time instead of the whole pipeline at once
The page showed the script, every shot's directing controls, your pictures,
filming, the joins and the cut simultaneously. That is four different jobs on
one screen, and the first of them - reading the words and getting them right -
is the one that matters most and was the easiest to skip past.

It is now four stages with a stepper: the words, the layout, filming, the cut.
Each shows only what belongs to it, and you can step back to look at an earlier
one at any time.

This also fixes something that was simply wrong: approving the script used to
hide every shot control, so the setting, camera and cutaways became uneditable
at exactly the moment you were meant to be arranging them. Directing now lives
in its own stage, after the words are settled and before anything is filmed.
2026-09-07 23:23:12 +00:00
b523474cbf Remove the duplicated tail left by the bad patch
The "$'" substitution token injected a second copy of everything after the
match point. The first copy was repaired into the real bindPost; this deletes
the leftover fragment and the duplicate that followed it, so the file ends at
its only </html> again.
2026-09-07 23:19:40 +00:00
6518c19b94 Repair the corrupted script block
The previous commit's replacement text contained the sequence "$'", which the
patch tool treats as a substitution token meaning "everything after the match".
It injected a duplicate copy of the rest of the file and left script.html with
a syntax error, so the page showed only "Loading...".

This closes the broken string and reopens bindPost so the duplicated tail that
follows becomes its legitimate body. A second patch removes the leftover.
2026-09-07 23:17:31 +00:00
862b864fce Show earlier takes, and let one be brought back
Filming again used to overwrite the video outright. Models drift between
generations, so re-filming to fix one thing can make another worse, and there
was no way back.

Every take is now kept and listed under the cut, newest first, with what it
cost and which voice it used. Restoring one keeps the take you are leaving, so
going back is reversible too.

This deliberately lands before the "remake this shot" button, because that is
what will make people re-film often enough to need it.
2026-09-07 23:15:09 +00:00
133e79da1b Put the editor behind a button, and stop the ruler clipping
The first and last ruler labels were centred on the track edges, so "0s" and
"32s" were half cut off.

More importantly the page had become busy: a finished video showed the cut, the
timeline, the joins and the shots all at once. The timeline is now behind an
"Open the video editor" button next to the video, so the default view is the
finished thing and the editing bay is somewhere you choose to go.
2026-09-07 23:10:01 +00:00
28831efa72 Seek by dragging the playhead or clicking the track; fix the stuck button
Playing to the end left the button reading "Pause" forever, because nothing
listened for the video ending. It now resets on end and on pause, shows the
current time against the total, and the track can be clicked or the playhead
dragged to reach a moment directly. Clicking a shot marker jumps to that shot.
2026-09-07 23:08:28 +00:00
930d0f4c5b Timeline: show what things are, and let the playhead be moved
Fixes and additions from watching it in use:

- the play button stayed on "Pause" after the video ran to the end
- there was no time readout and no way to reach a particular moment
- a cutaway was an anonymous blue rectangle, when the useful thing to show is
  the picture it will actually put on screen
- nothing marked where one shot ended and the next began, which is the whole
  reason the cutaways sit where they do

Shots are now marked above the bar with their opening frame, cutaways carry a
thumbnail of the image itself, and the playhead can be dragged or the track
clicked to move through the piece.
2026-09-07 23:07:56 +00:00
f7c8644b7f A timeline you can drag, and a preview that needs no render
The cut could be measured and executed but never seen or adjusted. This adds a
track view of where each cutaway sits and a player that switches sources at
each boundary, so the edit can be judged before spending anything on a render.

The audio is deliberately never touched: the talking head plays underneath
throughout and only the picture changes, which is what makes a cutaway hide a
join instead of interrupting the piece.

Dragging a block moves that cutaway; the handle on its right edge changes how
long it runs. Nothing is saved until Save, and Reset returns to the placement
the system chose.
2026-09-07 22:28:34 +00:00
d175b3b926 Poll for the placement suggestion instead of waiting on it
Matching pictures to the script is a model call and cannot answer inside the
few seconds the API gateway allows, so it returned "Function timed out". It now
starts in the background and this waits for it, the same way frames and joins
already do.
2026-09-07 21:25:52 +00:00
838f308ba9 Wire the collateral step
Step one creates the project on hold and reveals the collateral panel; step two
writes the script. A held project can be reopened with ?id= so a half-finished
setup is not lost.
2026-09-07 21:16:16 +00:00
28b98f8326 Gather pictures and footage before the script is written
The owner's pictures could only be attached after the script existed, so the
writer planned a piece in ignorance and the pictures were fitted to finished
words afterwards. That is backwards.

Making a video is now two steps. The project is created on hold, collateral is
attached, and only then does the writer run — knowing it has a photograph of
the actual product and eleven seconds of pool footage to write toward.

Footage is a link plus an in and out point, set by scrubbing the video itself.
The renderer already trimmed arbitrary sources with in/out, so this needed no
change to the cut at all, only somewhere to put the information.
2026-09-07 21:15:34 +00:00
99e2e7ce85 Add an Edit button beside Download on a finished video
A finished video was a dead end from this page: you could watch it or download
it, and the only way back into the shots, cutaways and cut was to know the
script URL. Edit goes to the detail screen, which is now where re-filming and
the cut live.
2026-09-07 21:14:47 +00:00
40b47c6c7f Say what each button does, and allow filming again after an edit
Three things were unclear or missing:

- "Make the frame" reads as though it might film the shot. It draws one still,
  the opening frame, and films nothing. Say so.
- Once a project was ready there was no way to film it again, so editing a
  shot afterwards led nowhere. The film card now appears for a finished video
  as "Film it again".
- Nothing explained that these per-shot buttons are optional. Filming draws
  any missing frame and makes any missing cutaway itself, so they are there to
  preview, not to complete.
2026-09-07 20:47:40 +00:00
708abeea41 Do not call a continuation shot's missing frame a fault
A shot that continues the previous one seeds from that shot's real last frame
and correctly has no opening still of its own. Flagging it as a lost likeness
was wrong and would send someone chasing a problem that is not there.

Also distinguish a frame drawn after the fact from the one actually used at
film time, so the audit trail does not imply a seed that was never sent.
2026-09-07 17:50:25 +00:00
bac9ecbcfb Say when a shot has no opening frame before filming starts
A video was filmed where shot 1 had a photo selected but no opening frame. The
seed was therefore empty and Veo was handed the written description alone, so
it invented a plausible bulldog: no collar, wrong eye colour. Nothing on this
page suggested anything was missing.

The worker now draws any missing frame before filming rather than proceeding
without a likeness, so this says that is going to happen and why it takes
longer.
2026-09-07 17:49:07 +00:00
09cf5c44c2 Pets: click a thumbnail to see the photo full size
The thumbnails are 110px squares cropped square, which is too small to check
the thing they exist to record — the collar, the eye colour, the markings that
have to survive into the video.
2026-09-07 17:47:00 +00:00
0771c990f4 Admin: show the reference photo beside the frame it produced
A video came out with the wrong dog — no collar, hazel eyes instead of black —
and there was no way to tell from this page whether a reference photograph had
been used at all. It had not: the shot had a photo selected but no opening
frame, so Veo was given the written description only and invented a plausible
bulldog.

Show both images side by side, and say loudly when there is no opening frame,
because that is the exact signature of a likeness that silently went missing.
2026-09-07 17:46:43 +00:00
79ba141ada Admin: show the voice used and split video cost from audio cost
Cost was a single number, which now hides two very different things: about
$0.40 a shot of video generation against roughly $0.015 of voice conversion.
Per shot, show which voice was actually dubbed in, so a conversion that failed
and silently fell back to Veo's own audio is visible rather than invisible.
2026-09-07 15:00:54 +00:00
b1cf3ef6f6 Say why the script failed instead of just that it did
The reason was already being stored on the project, but the page showed a bare
"could not finish" and a Start again button. A dead link is the most common
cause and it is the reader's to fix, so it needs saying out loud.
2026-09-07 14:54:35 +00:00
a3af200b14 Pet profile: pick a real voice instead of describing one
The old field asked owners to describe a voice in prose, and that prose went
straight into the Veo prompt. Nobody can reliably describe a voice, and the
description matched no actual voice, so nothing held the character steady
between episodes.

Now the owner picks an ElevenLabs voice and can listen to it first. That single
choice drives the Veo prompt, the writer's sense of the character, the word
budget per shot, and the voice the finished clip is redubbed into. The prose
box stays as an optional note that reaches the writer only.
2026-09-07 14:50:48 +00:00
9e10b6ed58 Pet profile: pick a real voice instead of describing one
The old field asked owners to describe a voice in prose, and that prose went
straight into the Veo prompt. Nobody can reliably describe a voice, and the
description matched no actual voice, so there was nothing holding the character
steady between episodes.

Now the owner picks an ElevenLabs voice and can listen to it first. That single
choice drives the Veo prompt, the writer's sense of the character, the word
budget for each shot, and the voice the finished clip is redubbed into. The
prose box stays as an optional note that reaches the writer only.
2026-09-07 14:49:38 +00:00
af95cf1802 Admin opens on the admin role, with the key only as a bootstrap 2026-09-07 12:39:57 +00:00
a2095eb806 Show the time as well as the date, using the shared formatter 2026-09-07 12:39:45 +00:00
ca5c25ef14 Store the session token issued at sign in 2026-09-07 12:39:36 +00:00
c4bc712972 Session tokens, server-driven menu, and times on My videos 2026-09-07 12:38:56 +00:00
2b611917be Poll for the seam report now that measuring runs in the background 2026-09-07 01:17:42 +00:00
78689c7014 Add Film it to the UI so nothing needs an operator 2026-09-07 01:02:08 +00:00
c4491ac8f1 Add a format dropdown that pins the set, framing and brief 2026-09-07 01:01:02 +00:00
6fccc3eb3b Add pasteable owner photos with descriptions and AI placement 2026-09-07 00:43:52 +00:00
c0a7f46094 Add B-roll cutaways, seam measurement and final cut to the shot editor 2026-09-07 00:09:05 +00:00
dba954ad17 Add admin page showing every project with the exact prompt submitted 2026-09-06 20:02:54 +00:00
62c4f344c7 Add a voice field to the pet profile so the character stays consistent 2026-09-06 18:55:22 +00:00
760f335256 Per-shot source toggle: photo or continue from the previous shot 2026-09-06 15:35:04 +00:00
61210c6640 Poll for the shot image, since drawing is now asynchronous 2026-09-06 15:29:00 +00:00
8a3324be31 Visual shot editor: start image, scene and camera move editable per shot 2026-09-06 15:24:34 +00:00
34150bb264 Add script review page with feedback loop and approval 2026-09-05 19:29:34 +00:00