|
1 | 1 | # CHANGELOG |
2 | 2 |
|
3 | 3 |
|
| 4 | +## v0.3.0 (2026-02-18) |
| 5 | + |
| 6 | +### Bug Fixes |
| 7 | + |
| 8 | +- Resolve all ruff lint errors ([#7](https://github.com/OpenAdaptAI/openadapt-capture/pull/7), |
| 9 | + [`de00dab`](https://github.com/OpenAdaptAI/openadapt-capture/commit/de00dab66724eb786dafb80ddfc860c51be13877)) |
| 10 | + |
| 11 | +* fix: resolve all ruff lint errors |
| 12 | + |
| 13 | +- Remove unused variable assignments (share.py, browser_bridge.py, windows.py, test_highlevel.py) - |
| 14 | + Add noqa comment for Quartz import needed by ApplicationServices (darwin.py) - Remove unused |
| 15 | + TYPE_CHECKING import (storage/__init__.py) - Add proper TYPE_CHECKING import for CaptureStats |
| 16 | + annotation (generate_real_capture_plot.py) - Auto-fix import sorting across multiple files |
| 17 | + |
| 18 | +Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
| 19 | + |
| 20 | +* docs: update README with share command and ecosystem links |
| 21 | + |
| 22 | +- Uncomment PyPI badges (package now published as 0.3.0) - Add "Sharing Recordings" section with |
| 23 | + Magic Wormhole usage - Update openadapt-privacy from "Coming soon" to GitHub link - Add share |
| 24 | + extra to optional extras table - Add openadapt-privacy and openadapt-evals to Related Projects |
| 25 | + |
| 26 | +* docs: remove redundant openadapt-ml training section |
| 27 | + |
| 28 | +The detailed training workflow belongs in openadapt-ml's README. This keeps openadapt-capture |
| 29 | + focused on its core functionality. Users can find training info via the Related Projects link. |
| 30 | + |
| 31 | +--------- |
| 32 | + |
| 33 | +Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
| 34 | + |
| 35 | +- Throttle screen capture to reduce system lag during recording |
| 36 | + ([#11](https://github.com/OpenAdaptAI/openadapt-capture/pull/11), |
| 37 | + [`d7134a8`](https://github.com/OpenAdaptAI/openadapt-capture/commit/d7134a83921a551cd8e91ae127f63f0759a146fe)) |
| 38 | + |
| 39 | +The screen reader thread was capturing screenshots in a tight loop with no frame rate limit, causing |
| 40 | + high CPU and memory pressure. With action-gated video, only the most recent screenshot matters |
| 41 | + when an action occurs, so capturing at 100+ fps was pure waste. |
| 42 | + |
| 43 | +Add SCREEN_CAPTURE_FPS config (default: 10 fps). The throttle sleeps for the remainder of the frame |
| 44 | + interval after each capture. Set to 0 for unlimited (legacy behavior). Also available as |
| 45 | + screen_capture_fps param on Recorder constructor and RecordingConfig. |
| 46 | + |
| 47 | +Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
| 48 | + |
| 49 | +- **ci**: Fix release automation — use ADMIN_TOKEN for protected branches |
| 50 | + ([#8](https://github.com/OpenAdaptAI/openadapt-capture/pull/8), |
| 51 | + [`4205cd5`](https://github.com/OpenAdaptAI/openadapt-capture/commit/4205cd5d2c6fd86eff4fd1e3bcd93a59aae03416)) |
| 52 | + |
| 53 | +* fix: resolve all ruff lint errors |
| 54 | + |
| 55 | +- Remove unused variable assignments (share.py, browser_bridge.py, windows.py, test_highlevel.py) - |
| 56 | + Add noqa comment for Quartz import needed by ApplicationServices (darwin.py) - Remove unused |
| 57 | + TYPE_CHECKING import (storage/__init__.py) - Add proper TYPE_CHECKING import for CaptureStats |
| 58 | + annotation (generate_real_capture_plot.py) - Auto-fix import sorting across multiple files |
| 59 | + |
| 60 | +Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
| 61 | + |
| 62 | +* docs: update README with share command and ecosystem links |
| 63 | + |
| 64 | +- Uncomment PyPI badges (package now published as 0.3.0) - Add "Sharing Recordings" section with |
| 65 | + Magic Wormhole usage - Update openadapt-privacy from "Coming soon" to GitHub link - Add share |
| 66 | + extra to optional extras table - Add openadapt-privacy and openadapt-evals to Related Projects |
| 67 | + |
| 68 | +* docs: remove redundant openadapt-ml training section |
| 69 | + |
| 70 | +The detailed training workflow belongs in openadapt-ml's README. This keeps openadapt-capture |
| 71 | + focused on its core functionality. Users can find training info via the Related Projects link. |
| 72 | + |
| 73 | +* fix(ci): fix release automation — use ADMIN_TOKEN to push to protected branches |
| 74 | + |
| 75 | +Root cause: GITHUB_TOKEN cannot push commits to protected branches. Semantic-release created the |
| 76 | + v0.3.0 tag (tags bypass protection) but the "chore: release 0.3.0" commit that bumps |
| 77 | + pyproject.toml was orphaned. |
| 78 | + |
| 79 | +- Use ADMIN_TOKEN for checkout and semantic-release (can push to main) - Add skip-check to prevent |
| 80 | + infinite loops on release commits - Sync pyproject.toml version to 0.3.0 (matches latest tag) |
| 81 | + |
| 82 | +Prerequisite: Add ADMIN_TOKEN secret (GitHub PAT with repo scope) to |
| 83 | + |
| 84 | +repository settings. |
| 85 | + |
| 86 | +Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
| 87 | + |
| 88 | +--------- |
| 89 | + |
| 90 | +Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> |
| 91 | + |
| 92 | +- **ci**: Use v9 branch config for python-semantic-release |
| 93 | + ([#13](https://github.com/OpenAdaptAI/openadapt-capture/pull/13), |
| 94 | + [`11eafca`](https://github.com/OpenAdaptAI/openadapt-capture/commit/11eafcaac1cab88dde4e861e93553fdef4b4ac1b)) |
| 95 | + |
| 96 | +* feat: disable window capture by default, add recording profiling with auto-wormhole |
| 97 | + |
| 98 | +- Make window reader/writer conditional on RECORD_WINDOW_DATA (defaults to False), eliminating |
| 99 | + unnecessary thread + process + expensive platform API calls - Add throttle to read_window_events |
| 100 | + (0.1s) and memory_writer (1s) loops - Add profiling summary at end of record() with duration, |
| 101 | + event counts/rates, config flags, main thread check, and thread count - Auto-send profiling.json |
| 102 | + via Magic Wormhole after recording stops |
| 103 | + |
| 104 | +Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
| 105 | + |
| 106 | +* fix: skip window requirement when RECORD_WINDOW_DATA=False, set log level to WARNING |
| 107 | + |
| 108 | +- When window capture is disabled, skip the window timestamp requirement in process_events instead |
| 109 | + of discarding all action events - Set loguru log level to WARNING by default (was DEBUG) to reduce |
| 110 | + noise during recording |
| 111 | + |
| 112 | +* fix: set log level to INFO not WARNING |
| 113 | + |
| 114 | +* fix: handle missing video frames on early Ctrl+C |
| 115 | + |
| 116 | +video_post_callback crashes with KeyError 'last_frame' when recording stops before any action |
| 117 | + triggers a video frame write. Guard against missing state keys and close the container gracefully. |
| 118 | + |
| 119 | +* fix: guard window event save when capture disabled, fix PyAV pict_type compat |
| 120 | + |
| 121 | +- Second reference to prev_window_event in process_events was unguarded, causing AttributeError when |
| 122 | + RECORD_WINDOW_DATA=False - PyAV pict_type="I" raises TypeError on newer versions; fall back to |
| 123 | + integer constant |
| 124 | + |
| 125 | +* fix: use PictureType.I enum for PyAV pict_type, add video tests |
| 126 | + |
| 127 | +- Use av.video.frame.PictureType.I instead of string "I" which is unsupported in current PyAV |
| 128 | + versions - Add test_video.py with tests for frame writing, key frames, and PictureType enum |
| 129 | + compatibility |
| 130 | + |
| 131 | +* fix: use Agg backend for matplotlib, improve wormhole-not-found message |
| 132 | + |
| 133 | +- Set matplotlib to non-interactive Agg backend so plotting works from background threads (fixes |
| 134 | + RuntimeError when Recorder runs record() in a non-main thread) - Improve wormhole-not-found |
| 135 | + message with install instructions |
| 136 | + |
| 137 | +* fix: reset stop sequence index after match to prevent IndexError |
| 138 | + |
| 139 | +When the stop sequence was fully matched, the index wasn't reset. Extra keypresses after the match |
| 140 | + would index past the end of the sequence list, causing IndexError. |
| 141 | + |
| 142 | +* feat: add per-screenshot timing to profiling, fix stop sequence IndexError |
| 143 | + |
| 144 | +- Track screenshot duration (avg/max/min ms) and total iteration duration per screen reader loop |
| 145 | + iteration in profiling.json - Reset stop sequence index after match to prevent IndexError on extra |
| 146 | + keypresses |
| 147 | + |
| 148 | +* feat: make send_profile opt-in CLI flag, add magic-wormhole as regular dep |
| 149 | + |
| 150 | +Profiling data is no longer auto-sent via wormhole after every recording. Use --send_profile flag to |
| 151 | + opt in. Also promotes magic-wormhole from optional [share] extra to a regular dependency since |
| 152 | + sharing is core functionality. |
| 153 | + |
| 154 | +* fix: add pixel_ratio and audio_start_time to CaptureSession |
| 155 | + |
| 156 | +HTML visualizer referenced these attributes which didn't exist on CaptureSession. Added properties |
| 157 | + with safe fallbacks and updated html.py to use getattr with defaults. |
| 158 | + |
| 159 | +* fix(ci): use v9 branch config for python-semantic-release |
| 160 | + |
| 161 | +The `branch = "main"` key is from v7/v8 and is silently ignored by v9, causing "No release will be |
| 162 | + made, 0.3.0 has already been released!" on every push. Use the v9 `[branches.main]` table. |
| 163 | + |
| 164 | +--------- |
| 165 | + |
| 166 | +Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
| 167 | + |
| 168 | +### Features |
| 169 | + |
| 170 | +- Copy legacy OpenAdapt recording system into openadapt-capture |
| 171 | + ([#9](https://github.com/OpenAdaptAI/openadapt-capture/pull/9), |
| 172 | + [`d359011`](https://github.com/OpenAdaptAI/openadapt-capture/commit/d3590118edad60171c8c2ca47cee181f05d590e2)) |
| 173 | + |
| 174 | +* fix: match legacy OpenAdapt recording architecture |
| 175 | + |
| 176 | +- Action-gated video capture: only encode frames when actions occur (~1-5 fps) instead of every |
| 177 | + screenshot (24fps). This is the core reason legacy OpenAdapt was smooth — not just separate |
| 178 | + processes. Matches legacy RECORD_FULL_VIDEO=False default behavior. - Video encoding in separate |
| 179 | + multiprocessing.Process (avoids GIL) - Screenshots via mss (2-4x faster than PIL.ImageGrab on |
| 180 | + Windows) - SIGINT ignored in worker process (main handles Ctrl+C) - Non-daemon process ensures |
| 181 | + video finalization on shutdown - First frame forced as key frame for seekability - Fix wormhole |
| 182 | + FileNotFoundError on Windows (searches Scripts/ dir) |
| 183 | + |
| 184 | +Legacy patterns matched: - prev_screen_event buffering → _prev_screen_frame - |
| 185 | + prev_saved_screen_timestamp dedup → _prev_saved_screen_timestamp - RECORD_FULL_VIDEO option → |
| 186 | + record_full_video parameter - SIG_IGN in worker processes - mss with CAPTUREBLT=0 on Windows |
| 187 | + |
| 188 | +Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
| 189 | + |
| 190 | +* feat: copy legacy OpenAdapt recording system into openadapt-capture |
| 191 | + |
| 192 | +Replace vibe-coded recording internals with proven legacy OpenAdapt code, adapted only for |
| 193 | + per-capture databases and import paths. |
| 194 | + |
| 195 | +New modules (copied from legacy): - db/models.py: SQLAlchemy models (Recording, ActionEvent, |
| 196 | + Screenshot, WindowEvent, PerformanceStat, MemoryStat) - db/crud.py: batch insert functions, |
| 197 | + post_process_events - extensions/synchronized_queue.py: multiprocessing queue wrapper - utils.py: |
| 198 | + timestamps, screenshots, monitor dims - window/: platform-specific active window capture - |
| 199 | + plotting.py: performance stat visualization |
| 200 | + |
| 201 | +Updated modules: - recorder.py: full legacy record() with multi-process writers, action-gated video, |
| 202 | + stop sequences, SIGINT handling - capture.py: reads from SQLAlchemy DB, fixes session leak, |
| 203 | + mouse_pressed=None handling, disabled event filtering, adds dx/dy/button properties to Action - |
| 204 | + config.py: all legacy recording config values - video.py: legacy functional API wrappers - cli.py: |
| 205 | + wired to new recorder - pyproject.toml: added sqlalchemy, loguru, psutil, tqdm deps |
| 206 | + |
| 207 | +Bug fixes: - Reset stop_sequence_detected on re-entry (Recorder reuse) - Close session on error in |
| 208 | + CaptureSession.load() - Skip click events with mouse_pressed=None - Filter disabled events in |
| 209 | + raw_events() |
| 210 | + |
| 211 | +Tests: 118 passed + 6 performance tests (Windows-only) |
| 212 | + |
| 213 | +Docs: updated README.md and CLAUDE.md to match new architecture |
| 214 | + |
| 215 | +* fix: make pynput import conditional for headless CI |
| 216 | + |
| 217 | +- Wrap Recorder import in try/except in __init__.py and test files - Skip Recorder tests when pynput |
| 218 | + unavailable (no display server) - Fix all ruff I001 import sorting violations - Remove unused |
| 219 | + imports and variables |
| 220 | + |
| 221 | +* fix(ci): exclude browser bridge tests and add timeout |
| 222 | + |
| 223 | +Browser bridge tests hang indefinitely on headless CI due to async websocket fixtures. Add |
| 224 | + pytest-timeout and a 10-minute job timeout. |
| 225 | + |
| 226 | +--------- |
| 227 | + |
| 228 | +Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
| 229 | + |
| 230 | +- Unified Recorder API with config overrides and runtime properties |
| 231 | + ([#10](https://github.com/OpenAdaptAI/openadapt-capture/pull/10), |
| 232 | + [`d840e81`](https://github.com/OpenAdaptAI/openadapt-capture/commit/d840e8107d7d45347201f04958c53293f08902e6)) |
| 233 | + |
| 234 | +Restore the clean Python API from the pre-legacy design on top of the proven legacy multi-process |
| 235 | + recording internals. |
| 236 | + |
| 237 | +Recorder constructor now accepts capture_video, capture_audio, and other recording options as |
| 238 | + keyword params that override config defaults. Adds event_count, is_recording, stats, |
| 239 | + wait_for_ready(), and capture properties for runtime introspection. |
| 240 | + |
| 241 | +Changes: - config.py: Add RecordingConfig dataclass + config_override() context manager for |
| 242 | + temporary config patching - recorder.py: Add shared counter params to record(), fix module-level |
| 243 | + config reads (STOP_SEQUENCES, PLOT_PERFORMANCE), rewrite Recorder class with full API (~120 lines |
| 244 | + replacing ~36) - cli.py: Forward --video/--audio/--images flags to Recorder - __init__.py: Export |
| 245 | + RecordingConfig - tests: 11 new tests (Recorder API + config_override) |
| 246 | + |
| 247 | +Fixes compatibility with record_waa_demos.py which passes capture_video/capture_audio and reads |
| 248 | + recorder.event_count. |
| 249 | + |
| 250 | +Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
| 251 | + |
| 252 | +- **share**: Add Magic Wormhole sharing for recordings |
| 253 | + ([`e7cfb1e`](https://github.com/OpenAdaptAI/openadapt-capture/commit/e7cfb1ec84999184a96682ebf74c08929485fe63)) |
| 254 | + |
| 255 | +- Add share.py module with send/receive via wormhole - Add 'capture share send/receive' CLI commands |
| 256 | + - Add magic-wormhole as optional [share] dependency - Auto-installs wormhole if missing |
| 257 | + |
| 258 | +Usage: capture share send ./my_recording capture share receive 7-guitarist-revenge |
| 259 | + |
| 260 | +Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
| 261 | + |
| 262 | + |
4 | 263 | ## v0.2.0 (2026-01-29) |
5 | 264 |
|
6 | 265 | ### Bug Fixes |
|
0 commit comments