Sign inStart creating

The Theater

Implementation: src/server/services/theater.ts. UI: /theater/**.

Rows

homeRows({ userId, kidsMode }) returns, in order: Continue Watching (first — it is what people came back for), Trending, New Releases, Top Rated, Editor's Picks, Creator Spotlight, Coming Soon, and "Because you watched…".

Kids mode filters to G/PG. Children's is a curated-only genre creators cannot self-select — enforced in the publish form and in CURATED_ONLY_GENRES.

Recommendations

similarTo is content-based over signals we own, with transparent weights:

SignalWeight
Same universe (series sibling or shared parent project)60
Shared style pack22 each
Same creator18
Genre overlap12 each
Popularity prior≤ 8

This is genuine content-based filtering, and "Same universe" / "Similar visual style" are real rows rather than relabelled popularity. It is not the pgvector semantic engine Part 8 targets — that is a marked swap point: replace the scoring loop with a nearest-neighbour query, keep the signature.

Search

searchCatalog parses structured intent out of natural language, then keyword-matches the remainder.

Extracted: runtime (20-minute, under 10 minutes, feature-length), genres (canonical list + aliases like scifi, scary, cartoon), and maturity (family, adult).

The example from the spec — "20-minute dark sci-fi thriller with a female lead and a twist ending" — resolves to runtime ≈14–26 min, genres Sci-Fi + Thriller, keywords dark, female, lead, twist.

The UI shows how the query was interpreted. That is the difference between a search box that feels smart and one that feels broken, and it costs nothing.

Method is reported as structured_keyword, not dressed up as semantic. Filters beat embeddings for runtime and rating anyway, so the structured extraction stays useful after a vector swap.

Title pages

Synopsis, trailer slot, AI cast cards linked back to the Universe, creator, runtime, rating, reviews, comments, licensing terms shown as first-class facts, and "Same universe" / "Similar visual style" rows.

Player

components/theater/Player.tsx handles three cases and is explicit about which one you are in:

  1. Real video master → HTML5 <video>, resumes from watch history.
  2. Procedural render (mock adapter) → animated preview on a simulated transport, with a note saying it is a style-accurate preview, not generated footage.
  3. No render → says so plainly. Progress still records, so Continue Watching works end to end.

Case 3 is the one worth getting right: a dead player with no explanation reads as a broken product.

Engagement

recordProgress (completion at 90%), incrementView, rate (recomputes the aggregate), comments with threading, follows, and My List as a toggle.

Film Festival

Monthly editions, category awards, audience voting. One vote per account per category, not per entry — otherwise a voter backs every nominee and the ranking means nothing. Voting for the same entry twice withdraws the vote.

Not built

  • Adaptive HLS/DASH — needs a transcoding pipeline and Mux/Cloudflare Stream. Today the player serves the asset directly.
  • Watch parties, remix marketplace transactions, monetization payouts.
  • Creator-editable caption tracks. Captions generate from dialogue (buildCaptions → SRT) but there is no editor.