Files
sync_replay_project/docs/ROADMAP.md
T
foxyzandClaude Opus 5.5 ea607c1a52
ci / core (push) Successful in 27s
ci / server (push) Successful in 1m4s
ci / updater-windows (push) Successful in 5m50s
Stabilisation quick fixes: orderly close, visible version and update band, chat pop-ups on, Windows shaders and hwdec, render logs
- Close: hide the window, dispose every player, thumbnail job and the
  sound engine, leave, then close normally; Linux runner _exit (the
  NVIDIA EGL exit handler crashed 10/10 closes, 0/41 now); Windows tears
  down inside the message loop, then TerminateProcess; watchdogs.
- Version under the Settings tab; an update band instead of a dot,
  never over a video; checks at start and every 4 hours.
- Chat pop-ups default to All again.
- Shader read-back compared as a list (old mpv joins with ','), so
  Windows stops turning shaders off; Windows hwdec auto-copy.
- Render diagnostics (`mpv: render` lines); RAM-only demuxer cache pin
  silences the file-cache warning; a volume readout test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:01:44 +03:00

13 KiB

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.

Stabilisation (feature freeze)

No new systems or features until the app is stable on a proper architecture. The next release is the restructure (1.3.0, or 2.0.0). Problems are numbered; delete a line when its fix ships.

Decisions (agreed):

  • D1 Presence: In (watching, full), Away (sat out: still in the server, chat and chosen notifications, system notifications in the background, not counted for the gate, ready counter or watched tracking), Gone (disconnected, held in a grace period).
  • D2 A joiner during playback holds it until everyone watching is ready, like the start gate.
  • D3 Watched: at least as good as Jellyfin's own; a peek, skipping around or a screenshot visit never counts. Basis: time present while it played.
  • D4 Toasts above the control bar; toasts for joins/leaves, queue adds (title, picture, who), swaps, problems; the rest in the activity log. Chat pop-ups and their sound always on by default; only the unread badge stays off the picture.
  • D5 A reconnect resumes the same member within a grace period (~10 min).
  • D6 The person passing a video on drops: others keep playing; on reconnect the relay continues from where it was.
  • D7 One controls model, two skins (done in 1.2.1-dev.1); uosc credited.
  • D8 Always the latest version: automatic checks, a clear prompt (no background download), never during playback.
  • D9 Watching alone is a full standalone player: no social sounds or prompts; social parts appear when others are there; no solo mode.
  • D11 Login-only sites: error texts say which cookies were used.

Problems:

  • P1 A reconnect makes you a new member: the relay, ready, sitting out and your queue items are lost (log: Hoff m23 → m24 had to put ep 06 up again).
  • P2 Joining a relay whose source left serves broken leftovers; mpv loops on failed seeks; nobody is told the source is gone.
  • P3 A joiner during playback lands mid-scene (see D2).
  • P4 Reconnects time out repeatedly on a bad link; no clear "reconnecting" state.
  • P6 Sat-out people count in the ready counter.
  • P7 Sat-out people get tracked as watched on Jellyfin.
  • P8 Sat-out people get the normal notifications.
  • P9 The watched rule (position coverage) misses real watches (D3).
  • P11 Toasts cover the video controls.
  • P12 Queue additions don't show as toasts.
  • P15 Commands reach a player mid-switch (seek 90.0, event:5 errors).
  • P17 Preload, warm relay and the fresh player per video are layered on the player instead of being one media pipeline.
  • P18 Bilibili "no video formats" with browser cookies set; the hint wording misleads (D11).
  • P22 Friends on different versions (D8). (P20 version visible and P21 update band: fixed in 1.2.1-dev.3; check the band in real use.)
  • P23 Solo mode you enter and leave; going hosted loses the queue (D9).
  • P24 The invite address list is unhelpful (shows the proxy's 198.18.x).
  • P25 ssh to Gitea blocked through the proxy (pushes go over https).
  • P27 Failing tests: shell "Ctrl+V outside a field", relay e2e "a guest puts up a file", "links: through me".
  • P28 Log noise: "Failed to create file cache"; an idle relay channel for Jellyfin items everyone has an account for.
  • P29 Relay start delay after a seek into an unbuffered part.
  • P30 Shaders don't work on Windows. Fix in 1.2.1-dev.3 (list read-back check; Windows hwdec auto-copy): a Windows user must confirm shaders stay on and the EGL-surface error is gone.
  • P31 Software rendering on a friend's Linux. 1.2.1-dev.3 logs it (mpv: render lines); needs that friend's log, plus watch-together 2>&1 | grep VideoOutput from a terminal if software.
  • P32 The app freezes on exit, mostly reported on Windows. 1.2.1-dev.3: orderly close, Linux _exit (0 crashes in 41 closes vs 10/10 before), Windows normal close + TerminateProcess + watchdogs, untested on Windows; if it still hangs: Event Viewer 1000/1002, a Task Manager dump, the close: log lines.
  • P33 No volume readout on wheel: likely fixed by the controls rework (1.2.1-dev.1; a test covers both skins); confirm in real use.
  • P34 Local anime folders are opened as plain files (no banner, names, next episode, tracking); test library: the user's /mnt/torrents/Torrents/Binux/ (several shows and a movie).

Restructure (in order; reviewed against the code). Truth lives in three places: the server (members, presence, gate, transport, queue incl. the automatic continuation and countdown, relay state), the device (client id, library and folders, match corrections, watch history, settings, notification prefs, what this app is sourcing) and the integrations (Jellyfin played/resume, AniList list). 0. Safety net and seams, no visible change: fix P27; an AppEvent type; platform ports wrapping today's calls (window/shell, open URL, notifications, yt-dlp process, folder access, player host); Connection extracted from the coordinator; the proto 16 spec written.

  1. Connection + Identity + Presence, proto 16 in full (one bump, with a min/max version range and a stable refusal frame so later changes are additive): a lasting client id; a socket close no longer removes the member (explicit bye); presence joining/in/away/gone with grace; PresencePolicy in core used by server and app alike; bounded D2 hold; relay sourceGone and source resume; queue by lasting; members[].app. Fixes P1 P3 P4 P6, server half of P2; D1 D2 D5 D6.
  2. Status (one computed level) + Notifier (pure policy over events: toasts above the controls, SoundEngine, system notifications, activity log). Fixes P8 P11 P12 P21 P33, visible half of P2; D4, D1, D9 alone policy ("alone" = no other connected member, In or Away).
  3. Your server: one hub, listeners attached (loopback; LAN on Invite), nothing restarts; Invite as a value (link/deep link, filtered addresses, version gate). Fixes P22 P23 P24; D9.
  4. Library + Media identity: L1 local grouping (pure Dart, offline: kinds episode/movie/extra, folder hints, gaps, merge across folders) and L2 match (AniList/TVDB ids, art, names, manual pin, cache); one MediaRef carried in swap/queue media. Fixes P34 and Files' next episode.
  5. What's next (decides which/when, never opens; continuation and countdown as server state, shared cancel) + Watch tracking (time In, device history, Jellyfin now, AniList later). Fixes P7 P9; D3.
  6. Media pipeline + player host port: plan → resolve → open → ready, commands queued during a switch, source-drop recovery, resolver and source chosen by capability. Fixes P2 (client) P15 P17 P18 P28 P29; P30 P31 P32 as a player-host sub-step; D6 client half, D11. Not in the restructure: P25 (ops).

Open decisions (raised by the review):

  • Queue survival: does your own server keep the queue across an app restart (conflicts with the "No server storage" decision)?
  • Transport for Android later: plain ws:// needs a cleartext exception on Android 9+; wss:// behind a proxy changes the Invite format.
  • Auto-Away: suggest Away after a long minimise (client intent), or only by hand?
  • Hold and grace lengths: D2 hold bound (e.g. 15 s), grace ~10 min; a source in grace keeps its relay buffer.
  • Platform decision: "Desktop + mpv only" becomes "mpv only, desktop first" if Android goes ahead.
  • D10 was dropped on purpose (preload/warm relay/fresh player are folded into step 6, not a separate decision).

Backlog

  1. Auto-skip OP/ED (undecided): who decides and how (shared for the server, personal, per show) is open. Constraint: a skip is a seek and seeks are shared, and only real ranges (segments, OP/ED chapters) should ever be skipped.

  2. Files: next episode from Files (by parsed episode, with the countdown, and under Up next's "Continues with"). Later: tracking for file series.

  3. yt-dlp follow-ups (see ARCHITECTURE "yt-dlp"): check that the bundled Windows libmpv has the ytdl hook (Lua) and plays edl:// URLs through media_kit; if not, resolve in the app and open the relay tracks' formats. YouTube: yt-dlp wants a JS runtime (deno) for full extraction. Formats that need yt-dlp's cookies.

    • Live streams through the relay (today refused: the sync model needs a fixed duration and seeking).
    • tool/relay_e2e.dart: a two-track case (bytes and pieces) over a shaped source link. Pieces on a slow source: a cold piece can take longer than media_kit's 5 s network-timeout; if it does, fetch the first pieces before setting relay.live.
    • Pieces, unverified so far: the 403/410 re-resolve against a real expiring HLS (signed segment URLs), HLS discontinuities / ad splicing, Windows' bundled ffmpeg with extensionless piece URLs (p/<i>; fine with ffmpeg 9 on Linux).
    • Thumbnails and scrubbing previews of a relayed media with tracks should open the video track's URL alone, not the EDL.
  4. AniList account: sign in (OAuth via a localhost callback), "My list" filter on the Schedule from the user's AniList lists. This is the Schedule's personal tracking; AnimeFlow has no per-user follow.

  5. Ops: Check shaders on Windows; the relay e2e "source drops uncleanly and resumes" fails about half the time under the full suite.

  6. Windows: first real use. CI builds and publishes watch-together-<ver>-windows-x64.zip (libmpv bundled) and runs the updater tests on NTFS; a friend has joined from it. Still unchecked: fullscreen controls, hosting (firewall prompt), in-app update from one release to the next. The rest of the desktop tests are not in CI.

  7. Relay follow-ups (see ARCHITECTURE "Relay"). First real use: owner's PC -> VPS -> friend, Jellyfin and a file. Then:

    • Source takeover: when the source leaves, another member with an account on that Jellyfin (or the same file: name, size, hash) takes over instead of the buffer running dry.
    • "Bob can't open this link — relay it?": offer the relay to whoever put a direct link up when a viewer's player fails to open it.
    • The host's own file as Up next's first item: it plays from /m once up, so others open it cold (the warm relay covers every other relayed item, see ARCHITECTURE "Relay"). The hosting app could hand the server the item's path early and /m serve it under a next/ name until the swap.
    • Research (before building anything): can stream URLs a member's app resolved with yt-dlp (its cookies, its address) be fetched by the server from another address? Per site: YouTube (its googlevideo URLs carry the resolving IP), Bilibili, X, generic HLS/DASH, others; and YouTube resolved by the server itself from a VPS, with and without cookies. Decides whether a server-driven fetch of member-resolved links (the member need not upload or stay) is worth building.
  8. Jellyfin (epic, below).

  9. Smaller items

    • Video output robustness: if media_kit's hardware path regresses to software (see ARCHITECTURE, Media: render mode), vendor a media_kit_video fix that builds the EGL display from GDK's native display. The video freeze after GPU idle is not explained by this; next suspect is media_kit's context switching on the raster thread with NVIDIA.
    • Link conveniences: 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.
    • 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).
    • Images in chat (paste a screenshot; server memory only): possible, not wanted yet.
    • HTTP: move the app's clients (Jellyfin, AniList, AnimeFlow, updater) to dio from fox_utils when touching them, for retries/interceptors.
    • TLS: document a reverse proxy in front of the server.
    • Up next follow-ups (see ARCHITECTURE "Up next"): a member whose reconnect lands as a new member loses its "through me" links and files in the queue (skipped as gone); the key could follow the member. A whole season or a pick list at once ("Add the rest of the season").
    • Sidecar subtitles.

Epic: Jellyfin

What is left (the rest is in ARCHITECTURE.md, "Jellyfin in a session"):

  • Auto-advance cancel for everyone (the next episode and Up next): today Cancel is per screen (the designated swapper's Cancel stops the swap; a seek stops all). A shared cancel needs a wire message.
  • Sidecar (external) subtitle streams of Jellyfin items: only embedded tracks are mapped to mpv today.

Deferred

P2P, Android. Never: transcoding, web clients.