Skip to content

SeanLF/what-to-watch

Repository files navigation

what-to-watch

A two-person Trakt.tv sync that tracks which watches were together vs solo. Every other Trakt-aware tool I could find treats accounts as independent — none model the couple-watching case. This one does.

The primary person's history pulls automatically from Trakt. After each sync, items go into a queue; an interactive bin/wtw review lets you mark each one as watched-together or solo. Joint watches mirror the episode count to the partner's row. Partner-only watches (things they saw without you) are entered manually — by direct SQLite insert, or via the /add-watch Claude Code slash command in .claude/commands/ if you use Claude Code.

Why

Standard Trakt clients have one history per account. Couples watching shows together end up with one person's account "owning" the data and the other person's history full of gaps — or two accounts duplicating each other. This tool keeps one canonical Trakt account but lets you attribute joint watches to a second person manually, with a queue-based UX so you can defer the attribution decision until after sync.

Also doubles as a small demo of:

  • Per-event idempotency (Debezium-style watermark + dedupe table) — re-running sync never double-counts, even after partial failures
  • Transaction-wrapped writes so crashes never leave half-resolved state
  • Hard-fail-on-error pagination (the silent-truncate variant once cost 173 lost events before it was fixed)

Setup

Ruby 3.2+ (uses endless method definitions and numbered block params). The repo's .ruby-version is latest so mise/asdf will install whatever they consider current; that's fine for any 3.x release.

# 1. Get the code
git clone <repo-url>
cd what-to-watch

# 2. Create a Trakt OAuth app at https://trakt.tv/oauth/applications
#    (any name works; "redirect URI" doesn't matter for this read-only use)

# 3. Configure
cp .env.example .env           # fill in TRAKT_CLIENT_ID, TRAKT_USERNAME, PRIMARY_USER, PARTNER_USER
cp config.yml.example config.yml  # adjust filters per person if you want

# 4. Run — bin/wtw self-bootstraps `bundle install`
bin/wtw export                 # initial full pull
bin/wtw update                 # incremental from now on
bin/wtw review                 # interactively attribute pending items
bin/wtw stats                  # JSON summary

How attribution works

  1. bin/wtw update pulls Trakt history events since the last watermark.
  2. New events upsert into watches for the primary user.
  3. Each touched item (movie or show) is enqueued in pending_partner_reviews.
  4. bin/wtw review opens a tty-prompt multi-select (arrow keys + spacebar). Items you check are mirrored to the partner's row; items you leave unchecked are marked as solo.
  5. Mirroring uses the primary's authoritative episode count (with MAX to preserve any solo-ahead count the partner might have) and never downgrades a completed partner row.

Schema

SQLite. See db/schema.sql for the full DDL.

Key tables:

  • watches — current state per (person, type, trakt_id). episodes_watched is mutated via upsert_watch (MAX semantics) or add_episodes (additive).
  • trakt_history_events — per-event idempotency. PK on Trakt's event_id.
  • pending_partner_reviews — queue drained by bin/wtw review.
  • Views: v_stats, v_genre_profile, v_overlap, v_recent, v_incomplete, v_exclusive.

Query views directly:

sqlite3 -json db/trakt.db "SELECT * FROM v_overlap ORDER BY rating DESC LIMIT 10"

Architecture notes

See docs/redesign-notes.md for the deferred event-sourced ledger refactor (replace the mutating watches.episodes_watched with a projection over an append-only watch_events table). The current code is correct, but the ledger shape would eliminate the dual-write footgun that caused the bugs this rewrite fixed.

Limitations

  • No OAuth. Reads use the public profile + client-id header only. Anything under Trakt's /sync/* endpoints (e.g. last_activities for a cheap pre-flight) needs Bearer auth and isn't wired up.
  • Manual entry for the partner. No browser extension, no auto-detect. You decide attribution at review time. If both people have Trakt accounts and want auto-detected joint-watch attribution, see docs/redesign-notes.md for the design sketch — good first contribution.
  • Re-watches over-count via the history endpoint. Trakt's /users/:u/watched/shows returns unique episode counts, while /users/:u/history returns play events. The full export pulls the former and is the authoritative source; incremental update pulls the latter and can over-count if you re-watch episodes. See docs/redesign-notes.md.

Working with LLM coding assistants

CLAUDE.md is the canonical project-context file. AGENTS.md and GEMINI.md symlink to it, so Codex, Gemini CLI, Antigravity, and any other harness that follows the "look for an instructions file named after me" convention will pick up the same content. If your harness uses yet another name, just symlink it: ln -s CLAUDE.md <whatever>.md.

The .claude/commands/*.md files are Claude Code slash commands (/recommend, /add-watch, etc.) but the content is plain markdown prose describing workflows — any LLM can read them directly via @, file attachment, or copy-paste. Move or symlink them into your harness's recipe/skill directory if it has one.

License

MIT — see LICENSE.

About

Two-person Trakt sync with a watched-together attribution queue.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages