- 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>
89 lines
4.4 KiB
Markdown
89 lines
4.4 KiB
Markdown
# Roadmap
|
|
|
|
Backlog and epics, in order. Done work lives in git history. Check an item
|
|
off by deleting it in the same commit as the code. Wire changes also edit
|
|
[PROTOCOL.md](PROTOCOL.md).
|
|
|
|
## Backlog
|
|
|
|
1. **Sync fixes.**
|
|
- Jumpy seek. Root cause: the media_kit seek bar calls `player.seek`
|
|
continuously while dragging; the coordinator only notices through its
|
|
1 s position poll (`_detectUserSeek`), so it fights the server echo and
|
|
the seek-landed retry (`_checkSeekLanded`).
|
|
- End-of-file stall. The 4 s buffered gate cannot be met in the last 4 s of
|
|
a file, the server never learns the duration, and there is no finished
|
|
state. Add duration reporting and a finished state.
|
|
- Tiny read-ahead. Our own pins (`cache-secs=10`,
|
|
`demuxer-readahead-secs=10` in `playback_controller.dart`) cap it; want
|
|
about two minutes for large files.
|
|
- Remote clock offset is not corrected: clients assume their clock equals
|
|
the server's.
|
|
- Stale-ready. A ready member who went stale (no reports for 15 s) should
|
|
return to the ready set when reports resume; the client re-sends ready as
|
|
a workaround (remove it then).
|
|
- Ghost member after an unclean drop until `dropAfter`.
|
|
|
|
2. **Rendering, then shaders.** media_kit falls back to software rendering
|
|
on the dev machine. Suspect Impeller on Linux against media_kit's GL
|
|
texture path; investigate first, since shaders need hardware rendering and
|
|
it may also cause the video freeze. Then shaders: per-viewer local setting
|
|
using mpv `glsl-shaders`, shipping Anime4K presets (bloc97, MIT).
|
|
|
|
3. **Windows build + CI.** There is no Windows runner. Try
|
|
`ubuntu2610/flutter_build` (LLVM-MinGW + Wine `gen_snapshot`) on the Linux
|
|
runner, fallback a GitHub Actions mirror. The manifest needs per-platform
|
|
URLs and `release_publish.sh` must handle a second platform. The updater's
|
|
Windows apply path exists but is untested. The tarball's
|
|
`watch-together-server` needs a Windows `dart compile exe` too. Desktop
|
|
tests are not in CI either.
|
|
|
|
4. **Jellyfin** (epic, below).
|
|
|
|
5. **Smaller items**
|
|
- Link conveniences: Ctrl+V pastes into the media field; a link swap in
|
|
session updates the remembered media; show the open link with an
|
|
open-in-browser button.
|
|
- Time UI: remaining time, scrubber preview.
|
|
- mpv / mpv.net shortcut parity.
|
|
- Sounds: replace `NoopSfxService` (cues already fire). Candidates:
|
|
Kenney Interface Sounds (CC0), ObsydianX Pack 1 (CC0), akx/Notifications
|
|
(CC0); playback via flutter_soloud. Keep seek/pause/resume near silent.
|
|
- Chaos suite: host drop 5 s / 30 s, viewer stall, 20-seek storm, long
|
|
outage with cache exhaustion (`tool/shaper.dart --kill-after`); assert no
|
|
OOM, no stuck session, drift recovers.
|
|
- Chat history pane (today: activity log only).
|
|
- Avatar caching: serve avatar bytes once by hash instead of base64 in
|
|
every `state` broadcast.
|
|
- The `state` broadcast carries the full media URL, which can include an
|
|
`api_key`.
|
|
- TLS: document a reverse proxy in front of the server.
|
|
- Playlist: `queue-add`, `queue-skip`, auto-advance like a swap.
|
|
- Sidecar subtitles.
|
|
|
|
## Epic: Jellyfin
|
|
|
|
- Each viewer logs in to Jellyfin themselves (Quick Connect or password).
|
|
- Several Jellyfin servers can be linked, keyed by `ServerId`.
|
|
- The sync server shares only `{ServerId, ItemId}`. Pasted Jellyfin links are
|
|
recognised and reduced to that pair.
|
|
- Viewers with an account direct-play `/Videos/{id}/stream?static=true`
|
|
with their own token and report Playing / Progress / Stopped, so watched
|
|
state and Continue Watching update per account.
|
|
- Guests without an account play through the sync server, which fetches with a
|
|
credential that stays server-side (the host's own or a configured one).
|
|
- Next episode: the successor in `/Shows/{seriesId}/Episodes` (NextUp means
|
|
first unwatched, not next-after-X); audio and subtitle track matching by language, then title,
|
|
then codec/channels.
|
|
- Library browser; anyone can put up items.
|
|
|
|
Spike facts (Jellyfin 10.11.11): `/Videos/{id}/stream?static=true&mediaSourceId=`
|
|
serves the original with byte ranges (206). PlaybackInfo gives `PlaySessionId`
|
|
and DirectPlay. All audio and subtitle tracks are embedded in the mkv. Quick
|
|
Connect is enabled but `/QuickConnect/Initiate` returned 400: investigate
|
|
(likely missing client/device fields in the auth header).
|
|
|
|
## Deferred
|
|
|
|
P2P, Android. Never: transcoding, web clients.
|