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>
This commit is contained in:
1 parent
e537f9d5f8
commit
ea607c1a52
29 files changed
+873
-168
No files matched your search
+75
-14
@@ -193,7 +193,7 @@ Loosely modeled on Syncplay, with a different readiness design.
|
||||
the server state and reconnect every 1.5 s until cancelled (an epoch counter makes
|
||||
a cancelled attempt stale). After a reconnect the ready flag is
|
||||
restored from the server's state. Cancel during setup aborts the join.
|
||||
Closing the window leaves gracefully (2 s timeout).
|
||||
Closing the window leaves gracefully (see "App close" under Desktop UI).
|
||||
- **Idle** (`IdleTracker`): measured from the later of the last input and the
|
||||
moment playback stopped. While HELD and self ready: warning at 8 min,
|
||||
auto-unready at 10 min, and never less than 2 min after the warning.
|
||||
@@ -294,21 +294,29 @@ page yt-dlp knows", network errors → "Couldn't reach the site", anything
|
||||
else as is). It rides in `report.error` (see "Player error" in the sync
|
||||
model) until the media opens (a new duration) or the server's `mediaId`
|
||||
moves on; the pill shows "Can't play this: ..." with Retry (Reload
|
||||
video). The cache is in RAM:
|
||||
media_kit sets `cache-on-disk` without a `cache-dir`, and mpv falls back. mpv
|
||||
video). The cache is in RAM: `cache-on-disk=no` is pinned (media_kit
|
||||
sets it on without a `cache-dir`, so mpv fell back to RAM anyway, warning
|
||||
"Failed to create file cache." on every open). mpv
|
||||
ignores invalid values silently, so every pin is read back and a mismatch is
|
||||
logged and kept in `pinMismatches`. Measured against the `/m` endpoint
|
||||
(`readahead_e2e_test`): a paused player buffers ~147 s ahead (10 s before).
|
||||
media_kit has no public `setProperty`/`getProperty`; the code calls them
|
||||
through a guarded dynamic call on `player.platform`. Hardware decoding comes from Settings (auto, off, custom)
|
||||
and is passed to the `VideoController`; env `SYNC_RELAY_HWDEC` (e.g. `no`)
|
||||
overrides it. It is read per player, so a change applies on the next media,
|
||||
overrides it. Auto is mpv `auto` on Linux and `auto-copy` on Windows
|
||||
(`autoHwdec`, also for a blank custom value): media_kit renders through
|
||||
ANGLE there, where `auto` picks the dxva2 interop, which cannot create its
|
||||
EGL surface ("Failed to create EGL surface"); `auto-copy` decodes on the
|
||||
GPU (d3d11va-copy, dxva2-copy, ... whichever works) and copies frames
|
||||
back, needing no interop. It is read per player, so a change applies on the next media,
|
||||
join or Reload video.
|
||||
|
||||
Shaders (per viewer, local only, no wire change): mpv `glsl-shaders`, set
|
||||
through the same dynamic `setProperty` and read back
|
||||
(`PlaybackController.applyShaders`). The value is the platform path list
|
||||
(`:` joined, `;` on Windows). One saved choice in `AppSettings`
|
||||
(`:` joined, `;` on Windows). The read-back is compared as a list
|
||||
(`shaderListMatches`): older mpv (0.37 and before, the bundled Windows
|
||||
libmpv among them) prints it `,` joined. One saved choice in `AppSettings`
|
||||
(`shaderPreset`, `shaderQuality`, `customShaders`): applied on every player
|
||||
creation (join, new media, Reload video) and live when changed from the session (the
|
||||
controls' ⚙ → Upscaling shader menu, Ctrl+0..6, both also save it). The Anime4K
|
||||
@@ -330,7 +338,17 @@ mpv logs at `v` while the controller is created: OpenGL init logs
|
||||
(`RenderMode.gpu`; "suspected software renderer" means llvmpipe,
|
||||
`softwareGl`); none within 1.5 s of the controller being ready means the
|
||||
software path (`software`, where shaders do nothing; the shader toast, the
|
||||
menu and Settings say so). Otherwise the session player logs at `warn`
|
||||
menu and Settings say so). Into the app log (`mpv`, INFO, all starting
|
||||
`render`): once per app start on Linux `render env:` (`XDG_SESSION_TYPE`,
|
||||
`WAYLAND_DISPLAY`, `DISPLAY`, `GDK_BACKEND`, `FLUTTER_LINUX_RENDERER`,
|
||||
`LIBGL_ALWAYS_SOFTWARE`, `__EGL_VENDOR_LIBRARY_FILENAMES`, `-` when
|
||||
unset); from that `v` window only mpv's GL init lines (`GL_VENDOR`,
|
||||
`GL_RENDERER`, `GL_VERSION`, `GL_SHADING_LANGUAGE_VERSION`, `Detected
|
||||
...`); the mode decided at the end of it; and per player, at its first
|
||||
frame, `hwdec-current` and the `hwdec` asked for (the first one per app
|
||||
start also `mpv-version` and `ffmpeg-version`). Why media_kit fell back to
|
||||
software it only prints on stderr (`media_kit: VideoOutput: ...`), which
|
||||
the app does not capture. Otherwise the session player logs at `warn`
|
||||
(`PlaybackController.logLevel`, switched with `mpv_request_log_messages`
|
||||
on media_kit's handle): media_kit drains mpv's log on the UI thread, one
|
||||
message at a time, so a chatty level, or ffmpeg logging every retry of a
|
||||
@@ -1105,7 +1123,8 @@ panel) and chat messages (Copy message, and a link in it).
|
||||
|
||||
- **Party strip** (`party_strip.dart`, 60 px): your avatar (profile card; 42 px like the party's),
|
||||
the Library / Schedule / Settings tabs with a growing underline (an
|
||||
`HScroll` when narrow), the update indicator, then offline a grey dot,
|
||||
`HScroll` when narrow; the version in small text under Settings,
|
||||
`shortVersion`, the full one in its tooltip), then offline a grey dot,
|
||||
"Offline", Join a friend and Host (connect cards; icon-less "Join" and
|
||||
"Host" under 1100 px), solo a "Solo · only you" chip, Invite friends
|
||||
(the Host card, titled "Invite friends", "Start hosting") and Stop (no
|
||||
@@ -1270,7 +1289,7 @@ panel) and chat messages (Copy message, and a link in it).
|
||||
switched off in `soundsOff`. The device opens 2 s after startup
|
||||
(`warmUp`), so neither startup nor the first sound waits on it; if it
|
||||
can't open, the engine logs `no sounds: audio init failed` once and drops
|
||||
every play. The close path disposes the engine (deinit, 500 ms bound)
|
||||
every play. App close disposes the engine (deinit, 500 ms bound)
|
||||
before leaving the server. Settings → Notifications & sounds has the volume, the
|
||||
overall sound set (`soundPreset`), and per sound a preview, its own set
|
||||
(`soundPresetFor`, else the overall one) and a switch; a preview always
|
||||
@@ -1376,8 +1395,10 @@ panel) and chat messages (Copy message, and a link in it).
|
||||
own have none); each
|
||||
slides in, stays 5 s, then slides out and collapses its gap. A click on
|
||||
a chat toast opens the chat; the others let clicks through. Chat toasts
|
||||
follow `AppSettings.chatPopups` (Settings → Notifications: All, Only
|
||||
when paused (default), Off); the message is in the chat either way.
|
||||
follow `AppSettings.chatPopups` (Settings → Notifications: All
|
||||
(default), Only when paused, Off); the message is in the chat either
|
||||
way, and its sound plays whatever this setting (its own switch in
|
||||
Sounds).
|
||||
|
||||
Settings persist in two Hive boxes: `connect_form` (last form, recent
|
||||
servers and links, the profile picture and colour; saved even when a join
|
||||
@@ -1534,6 +1555,36 @@ Window chrome is frameless (`window_manager`): the party strip is the drag
|
||||
area and holds the min/max/close buttons (`widgets/window_buttons.dart`);
|
||||
fullscreen has no drag area and shows the buttons in the player's top bar.
|
||||
|
||||
App close (`platform/app_shutdown.dart`, `shutDownApp`; the close button,
|
||||
Alt+F4, and Update and restart): the window position is saved and the
|
||||
window hides at once. Then every mpv player goes, before the engine does
|
||||
(a live one calls its wakeup callback into a dead isolate): the session
|
||||
player, the preload and the player still retiring
|
||||
(`PlaybackController.disposeAll`, 1.5 s each, via
|
||||
`SessionCoordinator.releasePlayers`, which keeps the position for the
|
||||
Jellyfin stop report), the Files thumbnail jobs (`cancelAllThumbnails`)
|
||||
and the sound engine (500 ms), side by side; on Windows 300 ms more,
|
||||
since media_kit_video frees a video output on a thread of its own after
|
||||
dispose returns, and an output still up when the engine stops blocks the
|
||||
plugin's teardown (`~VideoOutput` waits on a texture callback that never
|
||||
comes). Then `SessionState.leave`
|
||||
(Jellyfin stop report, last diagnostics, the goodbye, so others see
|
||||
"left") and the hosted server's stop, 3 s in all; an update restart then
|
||||
closes Hive and starts the new binary. Last the window closes the normal
|
||||
way (`setPreventClose(false)` + `close`), so the engine and plugins are
|
||||
torn down inside the platform's main loop, and the runners end the
|
||||
process without `exit()`'s handlers: Linux `_exit` after
|
||||
`g_application_run` (NVIDIA's EGL exit handler segfaults inside `exit()`),
|
||||
Windows `TerminateProcess` after the message loop (no DLL detach
|
||||
handlers, which can hang on threads already gone). Every step is bounded
|
||||
and logs (`close: ...`) when it is slow or fails. Backstops, so no process
|
||||
stays behind a hidden window: 3 s after asking the window to close the
|
||||
app kills itself (`Process.killPid`, no exit handlers; it only gets there
|
||||
if the close did not go through); the runners end the process 15 s after
|
||||
the first close request (Linux: a `delete-event` handler connected before
|
||||
window_manager's; Windows: the first `WM_CLOSE`), and Windows also 3 s
|
||||
after the window is destroyed, while the engine and plugins go.
|
||||
|
||||
## Logging
|
||||
|
||||
`packages/core/lib/src/logging.dart`, one setup for the app and the
|
||||
@@ -1690,10 +1741,20 @@ defines for every build and run) with "dev run". CLI: `watch-together-server --u
|
||||
the shipped `update.sh` / `update.bat` run it), `--check-update` prints only;
|
||||
it ends with "restart the server to run <version>". `watch_together --upgrade`
|
||||
does the same from the app binary. In the app, `UpdateController`
|
||||
(`apps/desktop/lib/src/update/`) runs the same code for the strip's update
|
||||
indicator, which opens the update card, and restarts the app ("Update and
|
||||
restart", closing Hive first).
|
||||
Settings has Check now and an auto-check toggle. The manifest is `updates/manifest.json` in
|
||||
(`apps/desktop/lib/src/update/`) runs the same code for the update bar
|
||||
(`UpdateBar`, `screens/home/update_indicator.dart`): a band across the
|
||||
window under the strip while an update is available: "Starsync X.Y.Z is
|
||||
ready", the notes, Later (hidden until the next check or start) and
|
||||
"Update and restart" (download, apply, then the app close above with Hive
|
||||
closed and the new binary started), its progress in the band. A release
|
||||
that is behind also says which version it runs; a copy that is not a
|
||||
packaged install says to replace the folder by hand. The shell keeps the
|
||||
band off the full player while a video is on (not finished); it shows on
|
||||
the tabs, the mini player's views and once the video ends. A required
|
||||
update has no Later. Nothing downloads before the click. The app checks at
|
||||
start and every 4 hours while running (`updateCheckEvery`), both with the
|
||||
auto-check setting on; a re-check never touches an install under way.
|
||||
Settings has Check now and the auto-check toggle. The manifest is `updates/manifest.json` in
|
||||
this repo, written by `tool/release.sh`. Test builds (`X.Y.Z-dev.N`,
|
||||
`isDevBuild`) also read `devManifestUrl`, the `manifest.json` of the `dev`
|
||||
prerelease (`tool/dev_release.sh`), and take the newer of the two
|
||||
|
||||
@@ -176,6 +176,19 @@ headers), `mpv:` (`cannot open the media` and ffmpeg's lines), `yt-dlp:`
|
||||
the headers sent). A viewer that got no bytes while the source was
|
||||
slow to come up shows mpv's error with "no HTTP error logged".
|
||||
|
||||
For "the video is slow / shaders do nothing", grep `mpv: render` (what
|
||||
each line holds: "Render mode" in [ARCHITECTURE.md](ARCHITECTURE.md)).
|
||||
`render: gpu` with a real `GL_RENDERER` (not `llvmpipe` / `softpipe`) is
|
||||
the good case; `render: softwareGl` is OpenGL on the CPU (Mesa's software
|
||||
rasterizer: missing or blocked GPU drivers, `LIBGL_ALWAYS_SOFTWARE=1`);
|
||||
`render: software` with no GL lines is media_kit's own fallback (it found
|
||||
no usable EGL context: check `render env:` for an X11 session or a
|
||||
`GDK_BACKEND` / `FLUTTER_LINUX_RENDERER` override). Its reason is only on
|
||||
stderr: have the user start the app from a terminal
|
||||
(`watch-together 2>&1 | grep VideoOutput`). `hwdec-current=no` on the
|
||||
first-frame line means software decoding (no hwdec for that codec, or it
|
||||
is off), not software rendering.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Relic gives Range as a typed `RangeHeader`; convert from unit + first range.
|
||||
|
||||
+15
-10
@@ -52,8 +52,8 @@ Problems are numbered; delete a line when its fix ships.
|
||||
the player instead of being one media pipeline.
|
||||
- P18 Bilibili "no video formats" with browser cookies set; the hint
|
||||
wording misleads (D11).
|
||||
- P20 Version not visible. P21 Updates easy to miss. P22 Friends on
|
||||
different versions (D8).
|
||||
- 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).
|
||||
@@ -62,14 +62,19 @@ Problems are numbered; delete a line when its fix ships.
|
||||
- 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 (our read-back check turns them off;
|
||||
dxva2-egl interop error).
|
||||
- P31 Software rendering on a friend's Linux; render mode not logged.
|
||||
- P32 The app freezes on exit, reported mostly by Windows users and
|
||||
other friends; on the user's Linux/NVIDIA machine it shows only as
|
||||
silent crash dumps after the window is gone (NVIDIA EGL exit handler;
|
||||
mpv players alive at teardown; Windows close order).
|
||||
- P33 No volume readout when using the mouse wheel on the video.
|
||||
- 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).
|
||||
|
||||
Reference in new issue
Block a user