From b51c64eee33c77f330a9a2def877f6f28cb39462 Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 8 Oct 2026 17:32:00 +0200 Subject: [PATCH] docs: record automatic session validation and platform gaps --- README.md | 10 ++++--- docs/validation.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7f300d6..def506e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ An open-source game recorder for Windows and Linux, inspired by Plays.tv. Rust a The recording foundation is being validated (see [docs/validation.md](docs/validation.md)). `clipbin-obs` records official OBS capture sources through one hardware encoder into keyframe-aligned segments and reports honest status; `clipbin-probe` exercises it. It passes the Linux headless rig and the hardware sign-off on CachyOS/Hyprland/RX 9070 XT; all Windows validation is still to do. -The desktop interface follows the web Clipbin's look (zinc dark theme, purple accent, Geist Mono): a session library, a session timeline with bookmarks and a clip selection, clips, and settings. It is not yet connected to the recorder, so it shows empty states; `CLIPBIN_SAMPLE_LIBRARY=1` fills it with example sessions for interface work. Steam detection, the recorder in the app, playback, export, persistence, global hotkeys, autostart, and background recording are not implemented yet. +The desktop app detects Steam games, owns the recorder on a background thread, persists settings and sessions, and loads recorded sessions from disk. Linux detects Steam’s `reaper` processes and watches their pidfds; Windows reads Steam’s registry state and watches native process handles (cross-checked, not run). Game exit finalises a session; closing the window keeps the current session recording until the game exits, then exits the app. Ctrl+Q or Quit stops the output cleanly. + +The desktop interface follows the web Clipbin's look (zinc dark theme, purple accent, Geist Mono): a session library, a session timeline with bookmarks and a clip selection, clips, and settings. It shows sessions from disk; `CLIPBIN_SAMPLE_LIBRARY=1` fills it with example sessions for interface work. Playback, export, global hotkeys, storage cleanup, autostart and a tray are not implemented yet. After closing the only window, reopening it without a tray is a follow-up. Windows is a first-class target. Every change must keep the Windows code paths building (`cargo clippy --target x86_64-pc-windows-msvc`) and list what still needs validating on Windows. @@ -16,7 +18,7 @@ Windows is a first-class target. Every change must keep the Windows code paths b ```text crates/ - clipbin-core/ Settings and recorder state; independent of GPUI and native OBS. + clipbin-core/ Settings, Steam detection and session storage; independent of GPUI and OBS. clipbin-obs/ Recording engine over the pinned libobs runtime; all unsafe code is in src/ffi.rs. clipbin-probe/ Validation probe for capture, encoders, segments and status. clipbin-desktop/ GPUI application and initial session-library view. @@ -28,7 +30,7 @@ docs/validation.md Validation status and findings. SPEC.md Product requirements and acceptance checks. ``` -The core has no external dependencies. Keep capture and session operations out of the UI thread. +Keep capture and session operations out of the UI thread. Core uses serde/JSON, dirs, tempfile, rustix on Linux and winreg/windows-sys on Windows. Raw OBS calls live in `clipbin-obs/src/ffi.rs`; raw Windows process calls live in the small `clipbin-core/src/windows_process.rs` bridge. ## Development @@ -85,7 +87,7 @@ cargo test --locked --workspace cargo clippy --locked --target x86_64-pc-windows-msvc -p clipbin-core -p clipbin-obs -p clipbin-probe --all-targets -- -D warnings ``` -Recording validation on Linux, headless: `scripts/validate/linux-rig.sh`. Interface screenshots, headless: `scripts/rig/screenshot-ui.sh` (screens: sessions, session, clips, settings, failed, empty). The rig has no input devices; `rig-pointer` drives a virtual pointer (`move`, `click`, `down`/`up`, `scroll`, `sleep` on stdin) and `wtype` types. `CLIPBIN_TRACE_FRAMES=1` logs frame times. +Recording validation on Linux, headless: `scripts/validate/linux-rig.sh`. Automatic desktop sessions: `scripts/validate/linux-session.sh` (fake Steam reaper, recording and library screenshots, close-window survival, game exit and explicit Quit). Interface screenshots, headless: `scripts/rig/screenshot-ui.sh` (screens: sessions, session, clips, settings, failed, empty). The rig has no input devices; `rig-pointer` drives a virtual pointer (`move`, `click`, `down`/`up`, `scroll`, `sleep` on stdin) and `wtype` types. `CLIPBIN_TRACE_FRAMES=1` logs frame times. Development builds optimise dependencies (`[profile.dev.package."*"]`): unoptimised GPUI made screens noticeably slow, with first frames of 200–300 ms. diff --git a/docs/validation.md b/docs/validation.md index fea8864..42f00ca 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -10,6 +10,7 @@ pass/fail results. | --- | --- | --- | | Linux, headless rig (Wayland portal + Xwayland) | `scripts/validate/linux-rig.sh` | All 9 scenarios pass on devbox (Intel UHD 770, VA-API) | | Linux, real session (CachyOS, Hyprland, RX 9070 XT, 1440p60) | `scripts/validate/linux-hardware.sh` | Passed 2026-10-08 (run 4, dc9cbd6) | +| Linux desktop automatic sessions (headless Wayland) | `scripts/validate/linux-session.sh` | Passed 2026-10-08: Recording sidebar, game exit, library reload, window close and active Quit | | Windows (Game Capture, NVENC/AMF/QSV) | `scripts/validate/windows.ps1` | Not run yet; script untested | ## Probe commands @@ -119,6 +120,8 @@ Windows status: - The assembled runtime keeps the six Game Capture hook binaries byte-identical to the release, with their Authenticode signatures present; signature validity has not been checked on Windows. +- The native Steam process bridge (`clipbin-core/src/windows_process.rs`) uses Toolhelp enumeration, limited process queries and a dedicated thread waiting on the process handle. Registry discovery uses `RunningAppID` and `Apps\\Running`; executable paths are matched beneath the app manifest’s installation directory. This code is cross-checked but unvalidated on Windows, including launcher/child-process identity, handle access permissions and exit ordering. +- Unvalidated: atomic metadata replacement and recovery on Windows, settings/data directory paths, desktop close-window survival and clean Quit. GPUI desktop integration cannot be cross-checked from Linux. - Unvalidated: encoder ids and settings for NVENC, AMF and QSV; Game Capture hooking and its loss detection (the engine expects the source size to drop to 0); WASAPI audio; `obs-ffmpeg-mux.exe` beside the executable; linking @@ -184,3 +187,69 @@ Windows status: - **Plugin configuration.** libobs is started without a module config path; set one under the app's config directory before plugins that save settings are added. + +## Automatic desktop sessions + +`scripts/validate/linux-session.sh [output-dir]` runs the desktop and a fake +Steam reaper (`argv[0]=reaper`, `SteamLaunch`, `AppId=480`) inside the headless +Wayland rig. It verifies Recording status, non-empty segment files and metadata, +closes the only window while recording continues, kills the game process, +checks finalisation, reopens the app to load its library from disk, captures +screenshots and uses Ctrl+Q during a second recording for clean shutdown. +The recording and library screenshots were inspected; storage totals came from +real Matroska file sizes. The existing Linux rig also passed all nine scenarios +after integration (2026-10-08). Its unwritable-output assertion accepts both +OBS Error and EncodeError: the muxer can report either for a permission error. + +Session metadata is `session.json` inside each data-directory session folder; +recovery attempts use separate `run-NNN` folders. Each segment stores its exact +start using `(total_frames_at_file_changed - 1) / fps`. The keyframe triggering +the split is already included in OBS’s count but belongs to the new segment: +the rig observed 121, 241, 361 at the boundaries of 120-frame segments. The +initial file starts at zero. Raw callback counts are preserved in events; the +e2e script checks placements exactly against cumulative earlier ffprobe video +packet counts within each run. Dropped output frames could make this count +drift: each segment records the run’s cumulative dropped-frame count at its +completion, and a nonzero count logs an explicit warning. Placement with +actual dropped frames still needs validation. + +Stopping exactly as a split fires can leave a header-only final Matroska file +(the rig observed 747 bytes, no Cluster and no video packets), despite a counted +split keyframe and zero reported drops. Finalisation checks the small header +prefix for the Cluster ID and deletes an empty tail after output stop. If deletion +fails (including a still-open file on Windows), it is marked unavailable in +metadata. Footage ends at the last real segment’s end and the remainder is +Missing. The offset check verifies every retained segment and explicitly +checks any unavailable marker, without treating an empty file as playable. Wall-clock time places each run inside the session and +marks capture waiting/recovery as Missing spans. Containers are never summed +to derive segment offsets. Metadata is replaced atomically from a synced +same-directory temporary file; startup finalises previously active metadata. + +Run `CLIPBIN_VALIDATE_RECOVERY=1 scripts/validate/linux-session.sh` to also +kill the portal capture stream and check that bounded automatic recovery +resumes into the same session, with a Missing span and a second run directory. +This passed in the headless Wayland rig. A deliberately missing runtime path +also opened the interface with a persistent failure banner and honoured Quit. + +Settings are saved under the config directory, including the portal restore +token. Capture selection clears that token and restarts the current session’s +capture; automatic recovery retries at five-second intervals, at most three +consecutive attempts. A failure stays visible until footage is proven again. + +One game is recorded at a time. Startup discovery also records a game already +running; simultaneous games are selected by PID order and the next still-live +game is detected after the current session ends. Linux Steam names come from +library manifests, with `App ` as the fallback. Windows requires a manifest +installation path to identify candidate processes and fails honestly if none +can be found. Real Steam/Proton lifecycle validation, X11 desktop session +integration and Windows runtime validation remain follow-ups. Audio-source +failure detection remains limited as described above. + +GPUI 0.2.2 ends its Linux event loop when the last window is removed, even +without an application close callback. Main keeps the recorder worker alive +after that return. Closing the window continues the current game session; +when that game exits, it finalises and the process exits. If no session is +active, it exits immediately. It stops discovering new games once closed, +so there is no indefinitely running invisible app. Reopening the window and +persistent background discovery need the future tray integration. Explicit +Quit/Ctrl+Q stops and finalises the current recording immediately.