feat: export clips, protect bookmarks and clean session storage

This commit is contained in:
2026-10-08 18:56:51 +02:00
parent f6cdef5dd2
commit 7ac7e66ff1
28 changed files with 2286 additions and 35 deletions
+108
View File
@@ -262,3 +262,111 @@ 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.
## Clips, bookmarks and storage cleanup
`clipbin-media::export` copies video and selected AAC tracks to MP4 without
re-encoding. OBS already writes system, microphone and combined tracks when the
mic is enabled: exports default to the combined track, with a Settings option
for the two separate tracks. A session without a mic exports its single track.
The output is closed and synced under a `.partial.mp4` name, then renamed;
only the completed MP4 and saved JSON appear in the Clips library.
Video timestamps use integer frame clocks, retaining presentation/decode
ordering for B-frames. Each segment also persists cumulative AAC sample starts
per stream (`audio_start_samples`, with `#[serde(default)]`). Audio uses its own
1024-sample AAC packet clock across adjacent pieces, avoiding independently reset/rounded Matroska timestamps.
Old session metadata defaults these audio counters to empty. Before cleanup,
the worker reconstructs them from retained finalised files; export can also
reconstruct an older range while its preceding files still exist. If the earlier
files are already unavailable and no exact clock was saved, export fails
honestly rather than guessing the A/V phase.
Keyframes are anchored to the capture run, including its wall-clock offset.
Outer unavailable footage is trimmed from completed ranges. Internal Missing
and Removed spans retain a timestamp jump; players may hold the preceding
picture or skip to the next picture, but the gap is not collapsed. Export
ranges spanning codec, size or frame-rate changes fail honestly. Dropped-frame
placement remains unvalidated, so these ranges fail rather than claiming exact
CFR output.
One bounded export queue runs on a separate thread. Requests retain the action
receipt timestamp while the current segment finalises; cleanup leases are
persisted in `clips/export-requests.json`. Failed exports retain their leases
until retry succeeds or the user releases them from the Clips screen. A crash
between MP4 publication and JSON persistence can leave an unlisted complete MP4;
Open folder lets the user recover it. Partial files are never listed as clips.
Cleanup checks the byte budget every three seconds and after session end. Its
pure selector orders completed sessions first, then active footage, oldest
first. It excludes bookmarks, editor selection and queued/running/failed export
ranges, and never selects the current file. It persists Removed spans before
deleting a segment; a deletion failure restores availability and surfaces an
error. Bookmarks persist in session metadata, protect future post-roll, and can
be released in the editor. Clips remain outside this budget and can be deleted
explicitly.
Global bindings default to Ctrl+Shift+F9 (clip) and Ctrl+Shift+F10 (bookmark).
Settings independently offer F11/F12 alternatives; configuration files accept
F1–F12 (distinct bindings). Wayland uses the GlobalShortcuts portal and displays
its returned binding descriptions; the desktop handles approval and rebinding.
A compositor without that portal reports unavailability, with sidebar buttons
as the fallback. X11 uses XGrabKey with Caps/Num lock variants and detectable
auto-repeat; Windows uses RegisterHotKey with MOD_NOREPEAT on a message thread.
Action timestamps are taken when the native event is received and retained
while the worker queues the export. Real Hyprland first-use approval, portal
restoration/rebinding and shortcuts while a game has focus remain unvalidated.
Native X11 desktop behaviour and Windows registration/conflicts also remain
unvalidated.
New checks:
- `scripts/validate/linux-clips.sh`: fake Steam session, actual sidebar button
clicks, cross-segment MP4, codec preservation, zero-error decode, CFR packet
PTS spacing, continuous audio, original short-clip range, capture continuing
during export, a persisted bookmark, forced one-byte cleanup with neighbouring
Removed spans, a new export of the bookmark after earlier files are deleted,
and a Clips screenshot. Uses only the standard PATH tools,
`rig-pointer` and `wtype`; no ripgrep dependency.
- `scripts/validate/check-clip.py <saved-clip.json>`: reusable packet/decode check
before source cleanup, including selected audio track count, cumulative source
audio clocks and the original per-track A/V phase to within one audio sample.
- `scripts/validate/linux-hotkeys.sh`: actual clip/bookmark X11 key grabs under
Xwayland (`xdotool` injects native X11 keys). The wlr rig has no GlobalShortcuts
portal, so it cannot validate the Wayland shortcut route.
Windows media build setup: use an FFmpeg **7.1 shared development** package
(headers and MSVC import libraries) matching OBS's bundled avcodec-61 DLLs.
Set `FFMPEG_DIR` to that package when building `clipbin-media`; distribute the
matching OBS-bundled FFmpeg DLLs. FFmpeg development/import files for MSVC are
not available in this Linux environment, so `clipbin-media` is excluded from
the Windows cross-lint. `clipbin-core` (including the hotkey bridge),
`clipbin-obs` and `clipbin-probe` are cross-linted. Windows export/remux, the
default player/folder actions, clip deletion, pending-export leases, bookmark
release, cleanup of recently closed files and every new GPUI control still
need linking/running on Windows.
Step 2 validation on the Intel UHD 770 Linux rig (2026-10-08): workspace format,
clippy with `-D warnings`, and all 49 unit tests passed. Windows cross-clippy
passed for core, OBS and probe with the exclusions above. The end-to-end clips
script passed for H.264 with one audio track and with separate system/mic
tracks, and for HEVC with the combined track. The HEVC run requested the default
120 seconds from a shorter session and correctly saved its available 14 seconds.
All checked exports retained their codec, decoded without errors, had exact
1/60-second video spacing and continuous audio, and retained their source A/V
phase. Forced one-byte cleanup removed neighbouring footage while bookmarks
and exported MP4s survived; a retained bookmark could still export after its
preceding files had been deleted. Clips screenshots are produced as `clips.png`
in each script's output directory.
To repeat the extra audio/codec cases, set `CLIPBIN_VALIDATE_MIC=1`, optionally
`CLIPBIN_VALIDATE_SEPARATE_AUDIO=1`, `CLIPBIN_VALIDATE_CODEC=Hevc`, or
`CLIPBIN_VALIDATE_CLIP_SECONDS=120` before `linux-clips.sh`. The original nine
capture scenarios and automatic-session validation also passed using
`env PATH=/usr/bin:/bin:$HOME/.cargo/bin`. Native X11 hotkey registration and
both actions passed under Xwayland. These fake Steam tests run sequentially:
game discovery is machine-wide, although rig teardown only kills its own PIDs.
AV1 export is unvalidated because this GPU has no usable AV1 encoder. B-frame
export and ranges containing an internal timestamp gap need additional media
fixtures; dropped-frame ranges are explicitly rejected. Linux default-player
and file-manager associations still need testing on a real desktop.