Skip to content

hk21x/decide

Repository files navigation

Contributors Forks Stargazers Issues MIT License


Logo

decide

Swipe. Decide. Watch. — a self-hosted swipe-to-match film picker for your Plex library.

Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Configuration
  5. Deployment Notes
  6. Roadmap
  7. Development
  8. Contributing
  9. License
  10. Contact
  11. Acknowledgments

About The Project

decide running on a phone — swiping a film card

Movie night has a deadline, and "what shall we watch?" shouldn't eat half of it. decide turns the choice into a game: two to four people swipe through the same small deck of films from your Plex library; when everyone says "Tonight" to the same one, it's a match — and a ticket stub prints.

  • Small decks, not endless scrolling. Filter by mood, runtime, rating, certificate — or build the deck from a Plex collection; swipe 20–50 cards, not 3,000.
  • Together or apart. Everyone swipes the same frozen deck in the same order, live on the sofa or hours apart on the train. Matches land the moment you agree.
  • It actually decides. Too many matches? The Final Round runs them head-to-head until one film is crowned — then open it in Plex, or send it straight to the TV.
  • Nights worth keeping. Every session ends with a taste-compatibility score ("You and Dee agreed on 7 of 30 — 23% tonight"), and stubs you keep live on in the album long after sessions expire.
  • Private by design. Your Plex token never leaves the server — every Plex call, including artwork, is proxied by the backend. Once synced, it works on the LAN with no internet at all.
  • Feels like an app. Installable PWA with offline swiping that syncs back exactly once when the connection returns. Dark and light themes.

decide is an independent project that works with Plex Media Server. It is not affiliated with or endorsed by Plex, Inc.

(back to top)

Built With

  • React
  • TypeScript
  • Vite
  • Tailwind CSS
  • Framer Motion
  • FastAPI
  • Python
  • SQLite
  • Docker

(back to top)

Getting Started

decide runs as a single Docker container alongside your Plex Media Server — one image, one port, one SQLite file.

Prerequisites

  • Docker (or any OCI runtime) on a machine that can reach your Plex server — a Pi, NAS or home server on the same LAN is ideal
  • A Plex Media Server with at least one movie library

Installation

  1. Create a compose.yaml:

    services:
      decide:
        image: ghcr.io/hk21x/decide:latest
        container_name: decide
        ports:
          - "8080:8080"
        volumes:
          - ./data:/data
        environment:
          - PUID=1000
          - PGID=1000
        restart: unless-stopped
  2. Start it:

    docker compose up -d
  3. Open http://<host>:8080 and follow the setup wizard: sign in with Plex (a 4-character code at plex.tv/link — decide never asks for your password), pick your server and film libraries, and let the first sync run.

Unraid: a Community Applications template ships in hk21x/unraid-templates — once accepted you'll find decide in the Apps tab; until then, the template XML works with Unraid's "Add Container → Template" as well.

(back to top)

Usage

  1. Start a session — pick a mood (or fine-tune genres), a runtime cap, a minimum rating, a certificate ceiling for family nights, or a Plex collection to deal from. A live counter shows how many films match.
  2. Share the code — a six-character join code, a copyable link, and a QR for whoever's on the sofa next to you.
  3. Swipe — right means "I'd watch this tonight". Tap ⓘ for the synopsis, cast and backdrop. Undo within five seconds if your thumb betrayed you.
  4. Match — unanimous right-swipes print a ticket stub in the shared shortlist, along with how compatible your taste turned out to be tonight.
  5. Crown one — the Final Round puts the matches head-to-head; pass the phone, argue, tap winners until one film is crowned for everyone. Open it in Plex, or play it on any Plex client the server can see.
  6. Keep the stub — kept stubs (and crowned winners) go to the 🎟 album, a ticket wallet of past movie nights that outlives the sessions.

On iPhone or Android, open the site and Add to Home Screen — decide installs as an app, and swipes made offline sync back when you reconnect. There's a light theme in Settings if the auditorium look isn't yours.

(back to top)

Configuration

Everything is optional — the wizard covers the lot. Env vars exist for declarative setups:

Variable Default Purpose
PORT 8080 Listen port
DATA_DIR /data SQLite database + artwork cache
PUID / PGID 1000 Run as this user/group
ART_CACHE_MB 500 Artwork disk-cache cap
PLEX_URL Skip the wizard: server address
PLEX_TOKEN Skip the wizard: auth token
PLEX_SECTIONS Comma-separated movie section ids
SECRET_KEY auto-generated Cookie-signing key override

(back to top)

Deployment Notes

decide uses a WebSocket at /api/sessions/*/live — make sure upgrades pass through your reverse proxy.

Caddy (WebSockets work out of the box):

decide.example.com {
    reverse_proxy 127.0.0.1:8080
}

nginx:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Remote access is deliberately out of scope — bring whatever already works for the rest of your self-hosted stack. Tailscale is the easiest route if you have nothing yet.

If your Plex server runs in Docker and LAN clients appear as remote (source-IP NAT), that's a Plex networking quirk, not a decide one — see the Plex forum thread on Docker and local network discovery.

(back to top)

Roadmap

  • Final Round — head-to-head bracket that crowns tonight's film
  • Taste-compatibility stats
  • The stub album — kept tickets that outlive sessions
  • Decks from Plex collections
  • Play the crowned film on a Plex client (TV, etc.)
  • Jellyfin support (media access already sits behind a MediaSource protocol — a JellyfinSource drops in without touching the rest)
  • Per-person Plex sign-in, so "unwatched" means your unwatched
  • Save the shortlist to a Plex playlist

See the open issues for the full list of proposed features and known issues.

Deliberately not planned: video playback (that's Plex's job), TV shows, more than four people, recommendations, or accounts.

(back to top)

Development

No build step magic — a Python backend and a Vite frontend.

Backend (Python 3.12, FastAPI, SQLite — managed with uv):

cd backend
uv sync
DATA_DIR=.dev-data uv run uvicorn decide.main:app --reload --port 8080
uv run pytest

Frontend (React 18 + Vite + TypeScript + Tailwind):

cd frontend
npm install
npm run dev   # proxies /api to :8080

Verified Plex endpoint behaviour is documented in docs/plex-notes.md.

(back to top)

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the MIT License. See LICENSE for more information.

(back to top)

Contact

Harry — @hk21x

Project Link: https://github.com/hk21x/decide

(back to top)

Acknowledgments

(back to top)

About

A self-hosted swipe-to-match film picker that works with your Plex library. Two to four people swipe through the same small deck of films; when everyone says "Tonight" to the same one, it's a match - and a ticket stub prints.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages