Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ Built for the [Backblaze Generative Media Hackathon](https://backblaze-generativ
h264 720p reel on B2, including a real generation on the live box's upload
path — see `demo/STATE.md` + `deploy/DEPLOYED.md`.
- **Firebase mirror:** https://upgradegr-cinemory.web.app — the identical app.
- **Demo video:** [`demo/cinemory-demo.mp4`](demo/cinemory-demo.mp4) (2:34, of
which 68.2s is live screen capture of this deployed app doing the real thing).
- **Demo video:** [`demo/cinemory-demo.mp4`](demo/cinemory-demo.mp4) (2:50, of
which 95.9s — 56.5% — is live screen capture of this deployed app doing the
real thing, shot as one unbroken five-photo run).
YouTube link: *TODO(owner): paste the URL after upload.*

**Check that the live app really is this commit.** The image is stamped at
Expand Down
75 changes: 73 additions & 2 deletions demo/STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,72 @@ _Last updated: 2026-08-03. Deadline: 2026-08-03 5:00pm EDT. $10k. Greece-eligibl
> were true on that date and are very likely superseded. If two entries
> disagree, the higher one wins.

## 2026-08-03: demo narration rewritten, voice swapped by measurement (canonical)
## 2026-08-03: demo reshot against the shipped product (canonical)

> The cut below was accurate when it was made and had gone stale in a day. Four
> things shipped after it and the video contradicted all of them: the photo cap
> is **5** (and the UI says the cap belongs to the demo, not the reel maker),
> the wait is **derived from the photo count**, generation runs
> **concurrently**, and the **job id is in the URL** so a reload no longer loses
> the run. The old narration's "about five minutes a photo" described a
> sequential pipeline that no longer exists, which by this point was not a
> stale detail but a misleading one.
>
> **Reshot, one unbroken take, a full five-photo run.** The video is now
> **2:50 (169.70s)**, under the 180s cap, and **95.9s of it (56.5%)** is live
> screen capture, up from 68.2s (44%). Ten beats, six of them footage. The take
> is a real job against the live service at the commit `/health` was reporting
> (`9fe2f24`): five photos in, the cap note held on screen, the occasion picked,
> all five model calls rolling at once, **the tab reloaded mid-job** and the same
> live reel picked back up from the link, the reel playing, Provenance verified
> **12/12**.
>
> **What the concurrency actually bought, measured on the run in the video:**
> `GET /reels/jobs/vKvu63FDtWK3b0Y-wBVsVufi` reports **1508.6s of provider work
> completed in 445.7s of wall clock** across 5 concurrent Kling calls, a 3.4x
> compression. Saved verbatim as `demo/video-assets/usage.txt`, and the beat-04
> narration quotes that run rather than an estimate. The same endpoint is the
> per-job usage accounting the storage beat refers to.
>
> **The reload beat, and what it can honestly show.** Playwright records the
> page viewport only, so `#reel/<job_id>` in the address bar can never be in
> frame. Rather than fake an overlay, the beat is built on the app's own resume
> copy, which is real in-page evidence that the reload recovered a running job:
> "Picking this reel back up. It carried on without the page open." plus
> "Already in progress" where the photo count would be. The URL is recorded in
> the take's marks file and stated in the burned-in caption.
>
> **A narration claim was cut because the picture could not back it.** The first
> pass had beat 3 say "read the line beneath" about the derived estimate, but
> that line sits below the fold behind the caption band and is not in frame in
> the take. The claim moved to beat 4, where "5 photos usually take about 6
> minutes." is plainly legible on the rolling screen for the whole beat, and
> beat 3 went back to being about the occasions.
>
> **Also fixed here, because it would have silently undone a live change:**
> `deploy/deploy-cloudrun.sh` still pinned `--memory 512Mi`. A 5-photo
> concurrent run OOM'd live earlier ("Memory limit of 512 MiB exceeded with 527
> MiB used") and the service was raised to 2Gi by hand, so the next deploy from
> that script would have put the OOM straight back. The script now pins 2Gi with
> the reason written next to it.
>
> Still ElevenLabs (**Rachel**, `21m00Tcm4TlvDq8ikWAM`, unchanged), still
> narration only and **no music** (single AAC track). `scripts/check_video.py`
> passes at 169.70s with every cue matched to its beat by order, timing and
> text. Zero em-dashes in captions, narration, SRT and beat script. Every
> footage beat is shorter than its source clip, so nothing freezes on a cloned
> frame:
>
> | Beat | Beat length | Source clip | Slack |
> |---|---|---|---|
> | 02-photos | 19.27s | 19.77s | 0.50s |
> | 03-occasion | 10.33s | 10.80s | 0.47s |
> | 04-rolling | 19.13s | 19.60s | 0.47s |
> | 05-link | 16.07s | 16.57s | 0.50s |
> | 06-reel | 10.43s | 10.93s | 0.50s |
> | 07-verify | 20.67s | 21.17s | 0.50s |

## 2026-08-03: demo narration rewritten, voice swapped by measurement (superseded)

> The picture was right and the words were wrong. The owner's verdict on the
> previous cut was that it sounded **"military"**: a run of short declaratives,
Expand Down Expand Up @@ -69,7 +134,13 @@ _Last updated: 2026-08-03. Deadline: 2026-08-03 5:00pm EDT. $10k. Greece-eligibl
> to its beat by order, timing and text). Captions carry **zero em-dashes**, as
> do the narration, the SRT and the beat script.

## 2026-08-03: demo video rebuilt around live screen capture; unique reel names
## 2026-08-03: demo video rebuilt around live screen capture; unique reel names (superseded)

> **Superseded by the top entry.** The video described below was reshot on the
> same day. In particular the "9/9 checks passed" receipt quoted here was from
> the two-photo take; the current five-photo video shows **12/12**, and the
> narration deliberately states no count so it cannot go stale again.


> The previous cut was a slide deck: eight still images with a Ken Burns move,
> **not one second of the product running**. Two of those eight beats (32 of
Expand Down
16 changes: 9 additions & 7 deletions demo/SUBMISSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
serves the identical app. `cinemory.ai` is **not yet mapped** — pending DNS;
use the run.app URL.)*
- **Demo video:** recorded + committed —
[`demo/cinemory-demo.mp4`](cinemory-demo.mp4) (2:34, inside Devpost's 3-min
[`demo/cinemory-demo.mp4`](cinemory-demo.mp4) (2:50, inside Devpost's 3-min
cap; ElevenLabs voice-over, no music, A/V-sync-gated in CI via the
`demo-video` job). Five of its nine beats are **live screen capture of the
deployed app** (68.2s, 44% of the runtime): photos in, occasion picked, the
real Kling job polled, the reel playing, Provenance verified 9/9. The
narration covers all four judged criteria by the clock: utility **47.7s**,
production readiness **54.1s**, Backblaze B2 **24.1s**, Genblaze **24.0s**.
`demo-video` job). Six of its ten beats are **live screen capture of the
deployed app** (95.9s, 56.5% of the runtime), cut from one unbroken take of a
full five-photo run: photos in, occasion picked, all five model calls rolling
at once, the tab reloaded mid-job and the reel picked back up from the link,
the reel playing, Provenance verified 12/12. The narration covers all four
judged criteria by the clock: utility **52.8s**, Genblaze **19.1s**,
production readiness **59.1s**, Backblaze B2 **38.6s**.
`TODO(owner): paste YouTube URL` — Devpost requires a publicly hosted
YouTube/Vimeo/Youku link; the repo mp4 alone does not satisfy that rule.
- **Deadline:** 2026-08-03 5:00pm EDT. Per the rules the app must stay freely
Expand Down Expand Up @@ -322,7 +324,7 @@ python -m cinemory.cli --name demo --chapters 3 --per-chapter 2 --bridges
- Readiness gate: automatable **100.0% (17/17) PASS**; full **85.6%** (3
user-gated live items, of which two — live redeploy, live B2 objects — are
now factually done; see above).
- Demo video recorded + committed (`demo/cinemory-demo.mp4`, 2:34) — ElevenLabs
- Demo video recorded + committed (`demo/cinemory-demo.mp4`, 2:50) — ElevenLabs
voice-over, guarded by an A/V-sync CI gate (the `demo-video` job) so audio,
video and captions cannot drift out of sync.
- **Devpost draft filled** — submission 1108702
Expand Down
84 changes: 47 additions & 37 deletions demo/build-video.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,79 +154,89 @@ def shot(name: str) -> str:
Beat(
"02-photos",
[shot("02-photos.mp4")],
"Drop photos in. The order you choose becomes the edit",
"This is the real app, live on Cloud Run. I drop in two photos from an "
"anniversary dinner. No account, no watermark. The photos go to a "
"private Backblaze bucket that is never made public.",
"Up to five photos. A limit of this demo, not of the reel maker",
"This is the real app, live on Cloud Run, and nothing is staged. I drop "
"in five photos from an anniversary dinner. No account, no watermark. "
"Five is the cap so a reel finishes while you watch, and the app says "
"that limit belongs to the demo, not the reel maker.",
live=True,
),
Beat(
"03-occasion",
[shot("03-occasion.mp4")],
"Six occasions. Each sets the music, pacing and titles",
"I choose the occasion. Each one brings its own music, its own "
"pacing, its own titles. Anniversary is warm strings. There is one for "
"award nights and company events.",
"Then I pick the occasion. Each one carries its own music, its own "
"pacing, its own titles, so an anniversary sounds nothing like an "
"award night.",
live=True,
),
Beat(
"04-rolling",
[shot("04-rolling.mp4")],
"A real model call through Genblaze. Five minutes per photo",
"Now the work. Each photo becomes a Genblaze pipeline step, and Genblaze "
"sends it to Kling on GMI Cloud. That is a real model "
"call, about five minutes a photo, so it runs as a background job the "
"page polls.",
"5 photos, about 6 minutes. All five calls run at once",
"The app tells you the wait up front, from the photo count. And here is "
"what changed: all five photos go through Genblaze to Kling on GMI "
"Cloud at once, not one behind another. This run packed twenty-five "
"minutes of model work into seven and a half minutes.",
live=True,
),
Beat(
"05-reel",
[shot("05-reel.mp4")],
"05-link",
[shot("05-link.mp4")],
"The job id is in the link. Reload, and the reel is still there",
"And you are not stuck on this page while it runs. The job id goes into "
"the link the moment the job is submitted, so I can reload the tab and "
"the reel is still here, still being made. That is what makes it "
"usable.",
live=True,
),
Beat(
"06-reel",
[shot("06-reel.mp4")],
"The reel plays. The people in it are model generated",
"And there it is. Two photographs, now moving, cut to the music. "
"Everyone you see was made by a model, so nobody real is in this demo.",
"And there it is. Five photographs, now moving, cut to the music the "
"occasion chose. Everyone you see was made by a model, so nobody real "
"appears in this demo.",
live=True,
),
Beat(
"06-verify",
[shot("06-verify.mp4")],
"07-verify",
[shot("07-verify.mp4")],
"Press Verify. Your own browser recomputes the SHA-256",
"Here is the part that matters. The provenance panel lists every step, "
"the model, the prompt and a hash for every asset. I press Verify, and "
"the browser recomputes the SHA-256. All nine checks pass, and "
"you can run them yourself.",
"Here is the part that matters most. The provenance panel lists every "
"step, the model, the prompt, and a hash for every asset. I press "
"Verify, and my own browser recomputes the SHA-256 against the sealed "
"manifest. Every check passes, and you can run the same check "
"yourself.",
live=True,
),
Beat(
"07-honest",
"08-honest",
[card("cinemory-04-b2-objects.png")],
"Every artifact in B2, content addressed and indexed",
"All of it lands in Backblaze B2. Every photo, every clip, the finished "
"reel and the sealed manifest, each stored under its own content hash, "
"and every clip names the photo it came from. Genblaze seals its own "
"provenance beside ours in the same bucket, and an append-only index "
"catalogues every object, so a bucket of files becomes a library you "
"can query.",
"reel and the sealed manifest, each under its own content hash. An "
"append-only index catalogues every object, and each job reports what "
"it burned, so a bucket becomes a library.",
),
Beat(
"08-stack",
"09-stack",
[card("cinemory-06-architecture.png")],
"One core, three ports. Live adapters, offline fakes in CI",
"This is built to last. One core, three ports, so the live Genblaze and "
"B2 adapters swap for offline fakes and the whole pipeline runs in "
"tests with no credentials. Genblaze is contract tested against the "
"real SDK, so a change there breaks the build instead of the demo. More "
"than six hundred tests, and security scans on every push.",
"tests with no credentials. Six hundred tests, contract tested against "
"the real SDK, and security scans on every push.",
),
Beat(
"09-close",
"10-close",
[card("cinemory-03-live-health.png")],
"Live now · open source · github.com/upgradedev/cinemory",
"Every push to main deploys itself, and it only passes if the live "
"service reports the commit it just built. No screenshot required. And "
"when a model is down, Cinemory says so out loud instead of faking a "
"result. It began as one gift, for one anniversary. It is live now, it "
"is open source, and it is waiting for your photos.",
"service reports the commit it just built. When the model fails, "
"Cinemory names the reason in plain words instead of leaving "
"you watching a spinner. It began as one gift, for one anniversary. It "
"is live now, open source, and waiting for your photos.",
),
]

Expand Down
Loading
Loading