Skip to content

Latest commit

 

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Watch Bracket

Watch Bracket is a self-hosted, real-time party game for turning “what should we watch?” into a shared decision. V1 implements Milestones 0 through 9: NAS deployment and onboarding, durable realtime rooms, browser and Chromecast displays, private nominations, the complete Double-Take tournament, TMDB/Plex/Tautulli/Seerr-compatible integrations, winner actions, a one-shot “I object!” podium re-vote, household memory, replay, animated presentation, accessibility, and production hardening.

See it in action

Start a movie night Watch the room fill live
Watch Bracket home screen Watch Bracket room lobby
Pin two private picks Vote poster-first
Watch Bracket private nomination screen Watch Bracket poster voting screen
Pair the television display Crown a winner on the podium
Watch Bracket shared display Watch Bracket animated winner podium
Challenge a close call Watch the podium go into overtime
Watch Bracket Gold and Silver objection ballot Watch Bracket shared-display objection overtime

Docker NAS quick start

Install Docker Engine with Docker Compose, then:

git clone https://github.com/HadenHiles/WatchBracket.git
cd WatchBracket
cp .env.example .env.production
cp .env.integration.example .env.integration.production
docker network create watchbracket_edge

Edit the two private environment files and replace every replace-me value. Set PUBLIC_APP_URL, PUBLIC_ALIAS_URL, the administrator email, and any media-server credentials for your installation. Both files are ignored by Git. The default compose.yml automatically loads .env.production, so routine Compose commands do not need --env-file or -f options.

For winner buttons that work outside your home network, set the internal and public addresses separately:

PLEX_BASE_URL=http://plex:32400
SEERR_BASE_URL=http://jellyseerr:5055
SEERR_PUBLIC_URL=https://jellyseerr.example.com

Plex winner actions use canonical app.plex.tv deep links so supported devices can hand them to the Plex app. SEERR_PUBLIC_URL is the safe, credential-free Jellyseerr destination shown to players. Tokens and API keys remain private in .env.integration.production.

Participant Plex sign-in survives page reloads for the room session. The Plex token is encrypted in PostgreSQL; browsers retain only the normal room cookie and a non-sensitive connected-account label for seamless UI restoration.

Start everything—including PostgreSQL migrations—with:

docker compose up -d --build
docker compose ps

Connect your existing reverse proxy or Cloudflare Tunnel to watchbracket_edge, open PUBLIC_APP_URL, and use Server settings to complete the guided setup. Creating and joining rooms does not require an account.

After the one-time environment and reverse-proxy setup, routine restarts are simply docker compose up -d. Source upgrades should use git pull --ff-only followed by docker compose up -d --build.

TMDB search and wildcard generation run exclusively through the private integration service. Development environments retain the deterministic local catalog as an explicit offline fallback; production never silently fills a bracket with fallback titles that bypass room filters. Browser automation is separately identified by an explicit request marker (with common crawler user agents as a fallback) and always receives a checked-in real-title metadata snapshot plus locally generated artwork, even when it targets a production-like stack. This prevents Playwright, screenshot tools, and crawlers from consuming TMDB quota. Google Cast launching requires a registered Custom Web Receiver application ID and a registered physical test device; see docs/cast/MILESTONE-2.md.

Development prerequisites

  • Node.js 22.9 or newer
  • pnpm 10.34 or newer (pnpm 11 requires Node.js 22.13+)
  • Docker Engine with Docker Compose v2 for the full stack

Copy .env.example to a private environment file only when running services outside Compose. Never commit the resulting file.

Start for development

The one-command container path includes PostgreSQL migrations:

pnpm install --frozen-lockfile
pnpm compose:dev

Open http://localhost:3000. The development bootstrap account is host@example.com / correct-horse-battery-staple; it is intentionally limited to the loopback-only development Compose file.

For process-level development, start PostgreSQL, export the variables in .env.example, then run:

pnpm db:migrate
pnpm dev

Verification

pnpm lint
pnpm typecheck
pnpm test
TEST_DATABASE_URL=postgres://... pnpm test:integration
E2E_BASE_URL=http://127.0.0.1:3000 pnpm test:e2e
pnpm build
pnpm compose:prod:config

Integration tests require a real, migrated PostgreSQL database and fail clearly rather than replacing it with memory-backed persistence.

Architecture

  • apps/web: Next.js App Router mobile controller and browser display
  • apps/game-api: Fastify, Socket.IO, authoritative room state, and expiration scheduler
  • apps/integration-service: private Fastify boundary for narrow, typed TMDB, Plex, Tautulli, and Seerr-compatible operations
  • packages/mock-catalog: deterministic provider-free catalog used by tests and development fallback only
  • packages/tournament-engine: pure deterministic 8-, 12-, and 16-title Double-Take rules
  • apps/cast-receiver: Vite-built Custom Web Receiver and deterministic receiver test mode
  • packages/db: Drizzle schema and migration; PostgreSQL is durable truth
  • packages/realtime-protocol and packages/display-protocol: versioned Zod contracts

Browsers access one public origin through Caddy. Only the game API reaches PostgreSQL. Display sessions are room-scoped, read-only, independently reconnectable, and revocable.

The Cast sender passes only a single-use launch token over the custom namespace. The receiver exchanges it once and then connects directly to the game API using an in-memory display bearer token. Set CAST_RECEIVER_APP_ID at web build time after completing Google Cast registration.

See the NAS quick start, deployment guide, UX audit, backup/restore runbook, security model, roadmap, and complete product specification.

Implementation notes are tracked per phase in Milestone 6, Milestone 7, Milestone 8, and Milestone 9.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages