One-click character swap for videos: an open-source, local Higgsfield Genjutsu alternative built on ComfyUI and MiniMax H3, with FLUX.3 Klein relighting.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Organization repository · verify with a repository file
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
An online cinema for Mizo films: pay per film (TVOD), opening nights where
everyone watches together, snacks delivered before the show, and a public box
office chart. HLS streaming, accounts, continue watching, My List, Razorpay.
Architecture
apps/
api/ Express 5 + Prisma (PostgreSQL) — auth, catalog, tickets, screenings, snacks, payments
web/ Next.js (App Router) + Tailwind + hls.js — the viewer site
media-worker/ Cloudflare Worker on media.remphamly.com/films/* — checks signed film links
packages/
shared/ Zod schemas and TypeScript types shared by api and web
Browser ──► Next.js (web :3000) ──/api/* rewrite──► Express API (:4000) ──► PostgreSQL
│ │
└──── HLS manifest + segments ◄──── CDN / origin ◄───┘ (signed URL from POST /playback)
Sessions: JWT in an httpOnly cookie. The web app proxies /api/* to the API so the cookie is first-party.
Tickets, not subscriptions: each title is bought on its own. A ticket's window (rentalHours,
48h by default) starts on first play. STANDARD = 1 screen; TOGETHER = 3 screens at once and +24h,
for families watching from different places. Extra tickets in one order become gift codes.
Entitlement: the catalog never exposes stream URLs. POST /playback checks for a playable ticket and
claims a screen for the device (PlaybackSession, kept alive by heartbeats), then returns a time-limited
manifest URL (HMAC-signed when CDN_SIGNING_KEY is set).
Opening nights (Screening): one start time and a virtual seat map. A title with an upcoming opening
night is only sold for that night. While live, grants carry live.offsetSec so every viewer is in sync
(players must not allow seeking). Afterwards those tickets work on demand, with a fresh window.
Orders: one checkout for tickets and/or snacks. Pending orders hold seats for ORDER_HOLD_MIN
minutes. Payment is Razorpay (Orders API + Checkout; the webhook is the source of truth). Without
RAZORPAY_KEY_ID (non-production only) dev payments confirm orders instantly.
Profiles: up to 5 per account (a household shares tickets; each profile has its own history,
My List, ratings and recommendations). Kids profiles only see G/PG/TV-Y/TV-PG titles — in the catalog,
search, recommendations and playback. The current profile comes from the rlb_profile cookie
(PUT /profiles/current) or an x-profile-id header.
Discovery: an in-memory search index (typo-tolerant trigram matching, Mizo diacritics ignored,
titles/alt names/cast/directors/genres/tags, "did you mean", suggestions). Recommendations blend
content similarity, co-watching ("households who watched X also watched Y") and popularity; the home
feed builds Continue watching, Top 10, Because you watched…, Recommended for you, Trending and
genre rows ordered by the profile's taste. Thumbs down/up/love feed it.
Our cinema: RemPhamLy Cinema's showtimes (entered by staff) with per-day BookMyShow links —
BookMyShow has no public API, so booking stays there.
Staff screens (/admin, ADMIN role): add and edit films and series (artwork upload, episodes, prices,
draft / scheduled / published), opening nights, kitchens and menus, plus showtimes, the snack queue and sales.
Scheduled titles are published by the API within a minute of their time (lib/publishing.ts).
Snacks: partner kitchens and menus; snack orders go to a staff queue (/admin/snack-orders) that
moves them from PLACED → ACCEPTED → OUT_FOR_DELIVERY → DELIVERED.
Getting started
Requires Node 20.19+ and PostgreSQL 16 — Docker, or no install at all with Prisma's local server
(see .env.example for the matching URLs):
npm install
npm run db:up # Postgres via docker compose, or:
# (cd apps/api && npx prisma dev -d --name ott -P 51214 --shadow-db-port 51215)
cp apps/api/.env.example apps/api/.env # then set JWT_SECRET
npm run db:migrate # apply migrations
npm run db:seed # demo catalog
npm run dev # api on :4000, web on :3000
Open http://localhost:3000, sign up, open a title, get a ticket (instant with dev payments) and press Play.
Make yourself staff with npm run make-admin -w @ott/api -- [email protected].
The demo catalog streams public HLS test assets (Blender Foundation open movies, CC-BY) and uses
picsum.photos placeholder artwork.
Tests
createdb -O ott ott_test # once; or set TEST_DATABASE_URL
npm test # resets + seeds the test DB, then runs API integration tests
# With the local Prisma server, tests use their own schema on it instead:
# TEST_DATABASE_URL="postgres://postgres:postgres@localhost:51214/template1?sslmode=disable&schema=test" npm test
Razorpay
Set RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET (test mode keys to start).
Add a webhook to https://<api>/payments/razorpay/webhook for payment.captured and order.paid,
and set RAZORPAY_WEBHOOK_SECRET to its secret.