Files
sync_replay_project/README.md
T
foxyzandClaude Opus 5.5 50bcc2e1a2
ci / core (push) Successful in 32s
ci / server (push) Successful in 44s
ci / updater-windows (push) Successful in 1m25s
CI: Windows build + release on yuuka-windows
tool/package_windows.ps1 builds watch-together-<ver>-windows-x64.zip
(same layout as the Linux tarball, libmpv bundled, update.bat and
watch-together-server.exe). The release workflow gets a windows job;
ci runs the updater tests on Windows. Publishing moves to
tool/release_publish.dart (dart: only, no python/bash on Windows), which
tolerates both jobs racing to create the release and replaces an asset
on re-runs. release.sh writes both asset URLs into the manifest.
Windows jobs check out with plain git (the host's cached
actions/checkout clone was corrupt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:27:38 +03:00

226 lines
11 KiB
Markdown

# watch-together
Watch a video with friends, in sync, with the quality of a real mpv player.
Pick from your Jellyfin library, paste any direct video link, or pick a file on your own disk;
everyone's play, pause and seek stay together.
Why not Jellyfin SyncPlay: it runs in the browser and its sync is poor.
This is a desktop app that drives mpv.
Name: `watch-together` is a placeholder and the final name is undecided
(`sync-relay` is the working name). It appears in file names and the
update manifest until that is settled.
- Desktop only (Linux and Windows), always mpv. No web clients.
- No transcoding. Everyone plays the original file.
- Friends trust model: no accounts, no roles. A blank password means an open server.
How it fits together: [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md).
## Install and update
From the Releases page of this repo:
- Linux: `watch-together-<version>-linux-x64.tar.gz`. Extract, run
`watch_together`. Needs mpv's library installed (`mpv` or `libmpv2`).
- Windows: `watch-together-<version>-windows-x64.zip`. Extract, run
`watch_together.exe`. mpv is bundled. New and not yet tried on a real
Windows desktop; reports welcome.
Update from the app: when a newer version exists, an update indicator
appears in Home's top bar and opens the update card. "Update and restart" downloads,
verifies, applies in place and restarts; the install folder must be writable
by you. Settings has Check now and an auto-check toggle. From a terminal:
`watch_together --upgrade`, or the shipped `update.sh` / `update.bat`. A
release with a newer protocol is a required update: everyone on a server
must run the same protocol version, and a mismatch is refused with a message
telling you to update.
## Host from the app
Home, Host tab. Pick what to watch (optional): Jellyfin (browse your linked
servers, see below), Link (paste an http(s) URL; Ctrl+V outside a field
pastes it in) or File (path or Browse). In the column beside it set a port and an optional password (blank =
anyone can join), then Host. Your name and avatar come from your profile
(the chip at the top left). The app starts a
server inside itself and joins it; you are the host and nobody can kick you.
Leaving or closing the app stops the server. Friends need to reach your machine: open
the TCP port on your router (or use a LAN or VPN address). Friends open nothing.
If you pick a local file, the server streams it to everyone from your disk.
Nothing is uploaded or stored anywhere.
## Dedicated server
For an always-on box (no display needed): the release tarball also contains
`watch-together-server`, a small standalone server without the app. Run it,
then everyone (you included) uses Join.
```
./watch-together-server --port 8080 --secret <password>
```
| Flag | Default | Meaning |
|---|---|---|
| `--port`, `-p` | 8080 | TCP port, all IPv4 interfaces |
| `--secret` | empty | password; empty = open server (a warning is printed) |
| `--name` | `server` | name reported by `/health` |
| `--media` | none | local file to serve as the starting video |
| `--advertise-host` | none | host put in file URLs instead of the one each viewer dialled |
| `--config` | none | YAML or JSON file with the same settings (`port`, `secret`, `name`, `mediaPath`, `advertiseHost`); replaces the flags |
It prints `READY port=... name=...` once listening and stops cleanly on
Ctrl+C or SIGTERM. A dedicated server has no host: anyone can kick anyone.
To run it as a service, unpack the tarball to `/opt/watch-together` and use
[deploy/watch-together.service](deploy/watch-together.service) (copy it to
`/etc/systemd/system/`, edit the flags, `systemctl enable --now watch-together`).
## Join
Home, Join tab. Enter the host's address, port, and the password if there
is one, then Enter or Join. Your name and avatar come from your profile (top
bar chip or Settings). Servers you got into appear as up to 6 Recent server
tiles with live status (online and how many are watching, or offline, plus
"needs update" when the server runs another protocol): click one to rejoin,
x to forget it. Ctrl+1 / Ctrl+2 switch between Join and Host.
## Settings
Profile, hardware decoding (auto, off or custom; the env var
`SYNC_RELAY_HWDEC` overrides it; applies on the next join or Reload video),
"Experimental: mpv-style controls" (below), updates, playing the next
Jellyfin episode automatically, video shaders (see below), and the
keyboard shortcuts (read-only).
Integrations links Jellyfin servers (below). About shows the version.
Keys in a session: Space = ready / unready, Tab = side panel (people, log,
chat, change video), Left/Right = seek 10 s, Shift+Right = skip 1:30,
PgUp/PgDn = previous / next chapter (as in mpv), F = fullscreen, Esc =
close panel or ask to leave, Ctrl+R = reload video, `<` / `>` or Shift+P /
Shift+N = previous / next Jellyfin episode, Ctrl+1..6 = Anime4K mode A, B,
C, A+A, B+B, C+A, Ctrl+0 = shaders off. Every seek, skip and chapter jump
moves everyone.
Controls: the seek bar marks chapter starts with a diamond; hovering it
shows the time and the chapter title. The chapters button lists them
(current one checked; click to jump). "1:30" skips ahead 1:30. Audio and
Subtitles buttons (with the track count) list tracks as "Japanese · E-AC3
2.0 · title"; subtitles have Off.
Experimental mpv-style controls (Settings → Player, off by default,
switches right away): video controls modeled on mpv's uosc theme. A thick
timeline across the bottom shows the played part, the cached part ahead
(hatched), chapter starts as diamonds and the time; hovering it shows the
chapter title and the time there. With the controls hidden it stays as a
thin line. Above it flat buttons: ≡ menu, Subtitles and Audio (with the
track count), the shader badge (the active Anime4K mode, e.g. "A HQ"),
ready, chapters, 1:30, previous / next episode, change video, people,
volume and fullscreen. Keys and skip buttons work the same.
Skipping: during the opening a "Skip OP" button floats in the corner (it
jumps to the end of the opening), during the ending "Skip ED" (to the end
of the ending only; what comes after is yours to skip). The opening and
ending come from Jellyfin's intro / outro segments when your Jellyfin has
them (needs a segment plugin, e.g. Intro Skipper), else from chapter names
(OP, Opening, Intro / ED, Ending, Credits, Outro). Without an opening, on
anything longer than 20 minutes, "Skip 1:30" floats there during the first
6 minutes.
## Ports
| What | Default | Notes |
|---|---|---|
| Sync WebSocket and `/health` | TCP 8080 | plain `ws://`, no TLS |
| Local file streaming | same port | path `/m/...`, needs a session token |
Only the host needs the port reachable.
## Jellyfin
Integrations > Jellyfin > Add server: type the address, then sign in with
Quick Connect (the app shows a code; approve it in Jellyfin under Profile >
Quick Connect) or your username and password. Several servers can be
linked; each shows its status, and a star picks the default. Sign out
revokes the app's sign-in on the server. If a sign-in expires the server
shows "Reconnect" instead of disappearing.
The Host tab's Jellyfin source is a library browser: Continue watching (with
time left), Next up and the latest per library on the start page; library
grids with sort (name, date added, release date, rating, random) and
Unwatched / Favorites filters; series pages with seasons, episode progress
and a "Next up" button; details with the file's tracks; search. Clicking a
movie or episode picks it; the button in the top right enlarges the browser
and moves port, password and Host into a bar below it. On wide windows a
side pane shows details of whatever is under the mouse.
Keys in the browser: arrows move, Enter picks or opens, I = details,
Esc / Backspace / Alt+Left = back (Esc first clears a search), / or Ctrl+F =
search.
Host from the library: pick a movie or episode and Host. Only the item
goes to the server (which Jellyfin server and which item), never a link or
your sign-in. Pasting a Jellyfin link (web page or stream link) does the
same when the app can reach that Jellyfin; otherwise it is shared as a
plain link.
Watching:
- Everyone with their own account on that Jellyfin server plays the file
directly with their own sign-in. Jellyfin tracks progress per account:
your watched state, Continue watching and resume point follow what you
saw (reported on start, every 10 s, on pause, resume, seek and track
change, and when it stops).
- Friends without an account watch through the host: the server fetches
the file with the host's sign-in and passes it on (only the current item,
only to people in the session). They show "via host" in the member list.
A dedicated server relays when started with `--jellyfin <file>` (see
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), Jellyfin relay).
- Without either, the app says "This needs a Jellyfin account on ..." with
a button to link one; it plays as soon as you are signed in.
Episodes: the ⏮ / ⏭ buttons next to the controls (or `<` / `>`,
Shift+P / Shift+N) put up the previous or next episode for everyone. From
the ending (when it is known, see Skipping above; else in the last two
minutes) a "Next episode" button floats in the corner. When an
episode ends, a card shows what is next with Play next; with "Play the
next episode automatically" (Settings, on by default) it counts down 10 s
first, and Cancel stops it on your screen (seeking back stops it for
everyone). Anyone with an account can put the next one up; friends
watching via the host cannot.
Audio and subtitles: the Audio and Subtitles buttons in the controls pick tracks.
Your choice is remembered per series (and as a default) and the closest
match (language, then title, then codec and channels) is selected on the
next episode. This is per person; nobody else is affected.
"Change video" in the session panel (Tab) opens the same picker as the Host
tab: the library starts on the series you are watching with the current
season, else where you left it. File is offered to the host only.
## Video quality and shaders
Each viewer can run mpv GLSL shaders on their own picture; nobody else is
affected. The app ships [Anime4K](https://github.com/bloc97/Anime4K) v4.0.1
(MIT) with the presets of its mpv guide: Mode A (1080p sources), B (720p),
C (480p), A+A, B+B, C+A, each as HQ (strong GPUs, GTX 1080 and up) or Fast
(the guide's low-end set). "Custom shaders" runs your own `.glsl` files.
Pick one with the ✨ button in the player's control bar (also in
fullscreen) or Ctrl+0..6; the change applies live and is saved, so the next
session starts with it. Settings → Video quality & shaders sets the default,
HQ or Fast (used by Ctrl+1..6) and the custom files. Shaders cost GPU time
every frame and need hardware video rendering: on media_kit's software
fallback they do nothing, and the app says so. A shader that is missing or
fails to load shows a toast and switches shaders off.
## Troubleshooting
- "host offline - check address and port": wrong address, or the port is not open.
- "wrong server password": password mismatch.
- "host runs a different version": update the app on both sides.
- Picture frozen but sound fine: Ctrl+R reloads the video; the app also does it automatically.
- Everyone waits at "waiting for X": X has not buffered 4 seconds yet or has not pressed Space.
- Windows: the build is new; if something is off there, say so.
For contributors: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md),
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/PROTOCOL.md](docs/PROTOCOL.md).