- Host/swap send refs only; pasted Jellyfin links become refs; the host
hands the relay its own app-obtained credential.
- Each viewer resolves with their own account (token in a header) or
via the relay, else a clear blocked card with Link account.
- Per-account progress reporting (Playing/Progress/Stopped), 401 marks
the account expired.
- Previous/next episode in the controls (< > / Shift+P/N), floating
Next episode pill near the end, auto-advance countdown (one
designated swapper), track carry-over per series.
- Swap panel uses the shared MediaPicker; browser location remembered
per server and opens on the current series.
- Fix: states are applied one at a time (a stale pause could land
after a newer play).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrations links several Jellyfin servers (Quick Connect or
password, app-obtained tokens only, status/reconnect/sign out). The
Host tab's Jellyfin source is a full browser: continue watching, next
up, latest per library, library grids with sort/filter/infinite
scroll, series pages with smart resume/next-up, details, grouped
search, inspector pane on wide screens, keyboard navigation.
MediaPicker selection is now a sealed PickedMedia. Hosting a Jellyfin
pick is interim (host's stream URL) until per-viewer playback lands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Media can be a Jellyfin ref {serverId, itemId, mediaSourceId, title,
subtitle, runtimeSecs}; the server never stores or broadcasts a URL
or token for it.
- Optional server-side Jellyfin credential (serve map / --jellyfin
file) enables GET /j/<sid>/<token>/<itemId>: range-faithful proxy of
the active item only, exact client header fields, upstream aborted on
disconnect, bytes counted. welcome.relayUrl / state.relay advertise it.
- Members report jf access (own account vs relay).
- /m now sends the real Content-Type (Relic overrode it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vendored Anime4K v4.0.1 GLSL (MIT), extracted to app support once per
version. Presets A/B/C/A+A/B+B/C+A in HQ and Fast per Anime4K's mpv
templates, plus custom files. Shader button in the video controls
(works in fullscreen), Ctrl+1..6 / Ctrl+0, live apply with read-back,
failures fall back to Off with a toast. Render mode detected from
mpv's GL log; warns when on the software path. Docs include the
Jellyfin client section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auth (Quick Connect incl. the Initiate 400 fix: Content-Length + full
client header; password), server identity and link parsing, browsing
(views, resume, next up, latest, paged items, search, seasons,
episodes, next episode), images, PlaybackInfo and stream URLs,
progress reporting and user data, and a track matcher for carrying
audio/subtitle choices across episodes. Fixture tests plus env-gated
live tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- User seeks are intercepted at the player (SyncPlayer), previewed
locally, sent once 300 ms after the drag ends; own echoes are matched
by seekSeq and never re-applied; the landed check can't reapply a
stale target. 1 s position poll removed.
- ping/pong clock offset (min-RTT ClockSync); positions use server time.
- Reports carry duration; buffer gate is min(4 s, remaining - 2 s);
new finished transport instead of a HELD stall at the end.
- cache/readahead 150 s, 256 MiB demuxer cache, pins read back.
- Resumed reports put a stale ready member back in the set; hello.resume
takes over the old member after an unclean drop (no ghost).
- Docs: roadmap rendering findings (HW path works; shaders verified).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Top bar merges window chrome, profile, tabs and page icons. Join: one
compact row plus recent-server tiles with live /health status and a
needs-update badge. Host: reusable MediaPicker (Jellyfin placeholder,
Link, File) filling the width, server settings in a narrow column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- serve(Map config, events) is the one server entrypoint: the app
hosts it via Isolate.spawn (crash surfaced on the Host card, logs
forwarded as [server]); bin/server.dart is the dedicated server,
compiled into the bundle as watch-together-server. --headless gone.
- Host identity via a per-hosting hostKey in hello, replacing the
loopback heuristic.
- Rename room -> server across code, files and wire (t:"state",
/health name); protocol bumped to 5.
- CI compiles the server binary; isolate smoke tests in server and
desktop suites. Docs updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README for the repo page; HOW-IT-WORKS overview; ARCHITECTURE,
PROTOCOL, DEVELOPMENT checked against the code; ROADMAP is backlog
only (isolate/dedicated server, sync fixes, rendering+shaders,
Windows build, Jellyfin epic). HANDOFF.md and mpv-http-contract.md
folded in and deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Player + VideoController per session; Reload video (Ctrl+R) and a
frozen-picture watchdog that reloads automatically.
- Shortcuts no longer fire while typing; empty swap disabled;
hwdec setting/env actually applied.
- Idle unready measured from real inactivity; cancellable reconnects
and setup; ready restored from server state; kicked/failed host
stops its server; graceful leave on window close; no URLs with
keys in display names.
- Welcome + connect screens replaced by one Home screen with Join and
Host cards, recent servers, Settings, Integrations and About.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Strip the tarball's top-level folder; stage inside the install root
(same filesystem) and swap with full rollback; keep file modes.
- Semver prerelease ordering (1.2.3-rc1 < 1.2.3).
- UpdateController: background check, download with progress, apply,
restart; required-update wording for newer protocols; Check now.
- update.sh/update.bat in the bundle; Windows apply path (untested).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reject non-finite numerics at protocol parse and in the engine
(a 1e400 seek became Infinity and broke every broadcast).
- Reject empty swaps; sanitize URL-looking media names.
- Blank password = open server; defaults no longer 'change-me'.
- Loopback peers (the in-process host) can't be kicked.
- /health no longer exposes media/transport; file URLs use the
host the client dialled instead of 127.0.0.1.
- Count served bytes as streamed, not as requested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
release.sh writes updates/manifest.json at release time (predictable
asset URL) — no CI commit-back, no push-token problem, no detached
checkout dance. release_publish.sh is create-release + upload only.
Manifest sha256 now optional; updater skips verification with a note
when absent (HTTPS + gzip CRC cover the realistic cases). Fixed the
repo-URL sed keeping a .git suffix (caught by a worktree dry run).
Updater tests: 8 (sha-less manifest flow added).
Reads the same stamped appVersion const the updater compares, so
the displayed version and the update logic can never disagree. Dev
runs show 0.0.0-dev.
patch/minor/major from latest tag (pubspec as-is for the first
release), bumps all pubspecs lockstep, commits, tags. Push with
--follow-tags and CI takes over. Docs updated to the flow.
release.yml (tag v* or manual dispatch): beta Flutter per local
convention, Linux build deps, package script tarball, artifact
upload, then release_publish.sh creates the Forgejo release,
uploads the asset, and commits updates/manifest.json back to
main. Workflows moved .github -> .gitea to match the forge setup.
Wishlist.md consumed into roadmap backlog: EOF stall + tiny buffer
bugs prioritized, recovery controls, link conveniences, time UI,
shortcuts, shaders, avatar serving.
Updater: watch-together --upgrade checks updates/manifest.json,
downloads the tarball, verifies sha256, applies over the live
install (extract-temp + delete-first move-over; rename-safe on
Linux while running). Zero Flutter imports, 7 tests incl.
stub-server flows. Version stamped via --dart-define=APP_VERSION
in package script. Warns (never silently jumps) on proto mismatch.
Roadmap: phases retired as tracker (history in git log), now flat
Built/Backlog/Epics/Deferred; Jellyfin next feature, transcode
never; updater expansion stays an epic. Endpoint publishing
(manifest + release tarball) is user-owned.
One command: flutter build linux --release, then bundles into
dist/watch-together-<version>-linux-x64.tar.gz with a single
top-level folder (executable, lib/, data/). Uploads via
DISCORD_WEBHOOK_URL env (never committed) as a Components V2 card:
version header, short git commit, stat codeblock, tarball as File
component. --noupload skips, --skip-build reuses the last bundle.
Verified: 0.1.0 builds, exec bit preserved, payload asserted
against a stub (flag 32768, container+file shape, attachment ref).
Private-server headroom over any pipeline PNG. The 8-min
auto-unready warning now mirrors the 10-min action guard: no toast
when self isn't ready, since there's nothing to lose.
64 KiB rejected normal 128px PNGs. 128 KiB fits any 128x128 PNG
(raw <= 64 KiB, worst-case base64 ~95 KiB chars) while still
bounding abuse. PNG kept for transparency. Pipeline now measures
the encoded base64 against RelayConstants (was checking the wrong
variable with its own 512 KiB limit) and drops dead crop code.
Server policy, still v4. Trade noted: avatars ride the 1 Hz room
broadcast, uplink scales with cap; hash-reference follow-up later.