Releases: OpenAdaptAI/openadapt-capture
v0.5.0
v0.5.0 (2026-03-04)
Features
- feat: disable window capture by default, add recording profiling with auto-wormhole
- Make window reader/writer conditional on RECORD_WINDOW_DATA (defaults to False), eliminating unnecessary thread + process + expensive platform API calls - Add throttle to read_window_events (0.1s) and memory_writer (1s) loops - Add profiling summary at end of record() with duration, event counts/rates, config flags, main thread check, and thread count - Auto-send profiling.json via Magic Wormhole after recording stops
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
- fix: skip window requirement when RECORD_WINDOW_DATA=False, set log level to WARNING
- When window capture is disabled, skip the window timestamp requirement in process_events instead of discarding all action events - Set loguru log level to WARNING by default (was DEBUG) to reduce noise during recording
-
fix: set log level to INFO not WARNING
-
fix: guard window event save when capture disabled, fix PyAV pict_type compat
- Second reference to prev_window_event in process_events was unguarded, causing AttributeError when RECORD_WINDOW_DATA=False - PyAV pict_type="I" raises TypeError on newer versions; fall back to integer constant
- fix: use PictureType.I enum for PyAV pict_type, add video tests
- Use av.video.frame.PictureType.I instead of string "I" which is unsupported in current PyAV versions - Add test_video.py with tests for frame writing, key frames, and PictureType enum compatibility
- fix: use Agg backend for matplotlib, improve wormhole-not-found message
- Set matplotlib to non-interactive Agg backend so plotting works from background threads (fixes RuntimeError when Recorder runs record() in a non-main thread) - Improve wormhole-not-found message with install instructions
- feat: add per-screenshot timing to profiling, fix stop sequence IndexError
- Track screenshot duration (avg/max/min ms) and total iteration duration per screen reader loop iteration in profiling.json - Reset stop sequence index after match to prevent IndexError on extra keypresses
- feat: make send_profile opt-in CLI flag, add magic-wormhole as regular dep
Profiling data is no longer auto-sent via wormhole after every recording. Use --send_profile flag to opt in. Also promotes magic-wormhole from optional [share] extra to a regular dependency since sharing is core functionality.
- fix: address PR #12 review feedback (5 issues)
- Move magic-wormhole back to optional [share] extra (was accidentally made a required dependency; recorder.py already handles ImportError) - Remove module-level logger.remove() that destroyed global loguru config for all library consumers; configure inside record() instead - Replace duplicate wormhole-finding logic with _find_wormhole() from share.py to eliminate code duplication - Add 60s timeout to _send_profiling_via_wormhole to prevent blocking indefinitely waiting for a receiver - Replace unbounded _screen_timing list with _ScreenTimingStats class that computes running stats (count/sum/min/max) in constant memory
Co-authored-by: Claude Opus 4.6 noreply@anthropic.com
Detailed Changes: v0.4.0...v0.5.0
v0.4.0
v0.3.0
v0.3.0 (2026-02-18)
Bug Fixes
- fix: resolve all ruff lint errors
- Remove unused variable assignments (share.py, browser_bridge.py, windows.py, test_highlevel.py) - Add noqa comment for Quartz import needed by ApplicationServices (darwin.py) - Remove unused TYPE_CHECKING import (storage/init.py) - Add proper TYPE_CHECKING import for CaptureStats annotation (generate_real_capture_plot.py) - Auto-fix import sorting across multiple files
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
- docs: update README with share command and ecosystem links
- Uncomment PyPI badges (package now published as 0.3.0) - Add "Sharing Recordings" section with Magic Wormhole usage - Update openadapt-privacy from "Coming soon" to GitHub link - Add share extra to optional extras table - Add openadapt-privacy and openadapt-evals to Related Projects
- docs: remove redundant openadapt-ml training section
The detailed training workflow belongs in openadapt-ml's README. This keeps openadapt-capture focused on its core functionality. Users can find training info via the Related Projects link.
Co-authored-by: Claude Opus 4.5 noreply@anthropic.com
The screen reader thread was capturing screenshots in a tight loop with no frame rate limit, causing high CPU and memory pressure. With action-gated video, only the most recent screenshot matters when an action occurs, so capturing at 100+ fps was pure waste.
Add SCREEN_CAPTURE_FPS config (default: 10 fps). The throttle sleeps for the remainder of the frame interval after each capture. Set to 0 for unlimited (legacy behavior). Also available as screen_capture_fps param on Recorder constructor and RecordingConfig.
Co-authored-by: Claude Opus 4.6 noreply@anthropic.com
- fix: resolve all ruff lint errors
- Remove unused variable assignments (share.py, browser_bridge.py, windows.py, test_highlevel.py) - Add noqa comment for Quartz import needed by ApplicationServices (darwin.py) - Remove unused TYPE_CHECKING import (storage/init.py) - Add proper TYPE_CHECKING import for CaptureStats annotation (generate_real_capture_plot.py) - Auto-fix import sorting across multiple files
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
- docs: update README with share command and ecosystem links
- Uncomment PyPI badges (package now published as 0.3.0) - Add "Sharing Recordings" section with Magic Wormhole usage - Update openadapt-privacy from "Coming soon" to GitHub link - Add share extra to optional extras table - Add openadapt-privacy and openadapt-evals to Related Projects
- docs: remove redundant openadapt-ml training section
The detailed training workflow belongs in openadapt-ml's README. This keeps openadapt-capture focused on its core functionality. Users can find training info via the Related Projects link.
- fix(ci): fix release automation — use ADMIN_TOKEN to push to protected branches
Root cause: GITHUB_TOKEN cannot push commits to protected branches. Semantic-release created the v0.3.0 tag (tags bypass protection) but the "chore: release 0.3.0" commit that bumps pyproject.toml was orphaned.
- Use ADMIN_TOKEN for checkout and semantic-release (can push to main) - Add skip-check to prevent infinite loops on release commits - Sync pyproject.toml version to 0.3.0 (matches latest tag)
Prerequisite: Add ADMIN_TOKEN secret (GitHub PAT with repo scope) to
repository settings.
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
Co-authored-by: Claude Opus 4.5 noreply@anthropic.com
- feat: disable window capture by default, add recording profiling with auto-wormhole
- Make window reader/writer conditional on RECORD_WINDOW_DATA (defaults to False), eliminating unnecessary thread + process + expensive platform API calls - Add throttle to read_window_events (0.1s) and memory_writer (1s) loops - Add profiling summary at end of record() with duration, event counts/rates, config flags, main thread check, and thread count - Auto-send profiling.json via Magic Wormhole after recording stops
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
- fix: skip window requirement when RECORD_WINDOW_DATA=False, set log level to WARNING
- When window capture is disabled, skip the window timestamp requirement in process_events instead of discarding all action events - Set loguru log level to WARNING by default (was DEBUG) to reduce noise during recording
-
fix: set log level to INFO not WARNING
-
fix: handle missing video frames on early Ctrl+C
video_post_callback crashes with KeyError 'last_frame' when recording stops before any action triggers a video frame write. Guard against missing state keys and close the container gracefully.
- fix: guard window event save when capture disabled, fix PyAV pict_type compat
- Second reference to prev_window_event in process_events was unguarded, causing AttributeError when RECORD_WINDOW_DATA=False - PyAV pict_type="I" raises TypeError on newer versions; fall back to integer constant
- fix: use PictureType.I enum for PyAV pict_type, add video tests
- Use av.video.frame.PictureType.I instead of string "I" which is unsupported in current PyAV versions - Add test_video.py with tests for frame writing, key frames, and PictureType enum compatibility
- fix: use Agg backend for matplotlib, improve wormhole-not-found message
- Set matplotlib to non-interactive Agg backend so plotting works from background threads (fixes RuntimeError when Recorder runs record() in a non-main thread) - Improve wormhole-not-found message with install instructions
- fix: reset stop sequence index after match to prevent IndexError
When the stop sequence was fully matched, the index wasn't reset. Extra keypresses after the match would index past the end of the sequence list, causing IndexError.
- feat: add per-screenshot timing to profiling, fix stop sequence IndexError
- Track screenshot duration (avg/max/min ms) and total iteration duration per screen reader loop iteration in profiling.json - Reset stop sequence index after match to prevent IndexError on extra keypresses
- feat: make send_profile opt-in CLI flag, add magic-wormhole as regular dep
Profiling data is no longer auto-sent via wormhole after every recording. Use --send_profile flag to opt in. Also promotes magic-wormhole from optional [share] extra to a regular dependency since sharing is core functionality.
- fix: add pixel_ratio and audio_start_time to CaptureSession
HTML visualizer referenced these attributes which didn't exist on CaptureSession. Added properties with safe fallbacks and updated html.py to use getattr with defaults.
- fix(ci): use v9 branch config for python-semantic-release
The branch = "main" key is from v7/v8 and is silently ignored by v9, causing "No release will be made, 0.3.0 has already been released!" on every push. Use the v9 [branches.main] table.
Co-authored-by: Claude Opus 4.6 noreply@anthropic.com
Features
- fix: match legacy OpenAdapt recording architecture
- Action-gated video capture: only encode frames when actions occur (~1-5 fps) instead of every screenshot (24fps). This is the core reason legacy OpenAdapt was smooth — not just separate processes. Matches legacy RECORD_FULL_VIDEO=False default behavior. - Video encoding in separate multiprocessing.Process (avoids GIL) - Screenshots via mss (2-4x faster than PIL.ImageGrab on Windows) - SIGINT ignored in worker process (main handles Ctrl+C) - Non-daemon process ensures video finalization on shutdown - First frame forced as key frame for seekability - Fix wormhole FileNotFoundError on Windows (searches Scripts/ dir)
Legacy patterns matched: - prev_screen_event buffering → _prev_screen_frame - prev_saved_screen_timestamp dedup → _prev_saved_screen_timestamp - RECORD_FULL_VIDEO option → record_full_video parameter - SIG_IGN in worker processes - mss with CAPTUREBLT=0 on Windows
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
- feat: copy legacy OpenAdapt recording system into openadapt-capture
Replace vibe-coded recording internals with proven legacy OpenAdapt code, adapted only for per-capture databases and import paths.
New modules (copied from legacy): - db/models.py: SQLAlchemy models (Recording, ActionEvent, Screenshot, WindowEvent, PerformanceStat, MemoryStat) - db/crud.py: batch insert functions, post_process_events - extensions/synchronized_queue.py: multiprocessing queue wrapper - utils.py: timestamps, screenshots, monitor dims - window/: platform-specific active window capture - plotting.py: performance stat visualization
Updated modules: - recorder.py: full legacy record() with multi-process writers, action-gated video, stop sequences, SIGINT handling - capture.py: reads from SQLAlchemy DB, fixes session leak, mouse_pressed=None handling, disabled event filtering, ...
v0.2.0
v0.2.0 (2026-01-29)
Bug Fixes
The PyPI version and downloads badges show "package not found" since openadapt-capture is not yet published to PyPI. Commenting them out until the package is released.
Co-authored-by: Claude Sonnet 4.5 noreply@anthropic.com
- Move openai-whisper to optional [transcribe] extra (
9dca9e5)
The openai-whisper package requires numba → llvmlite which only supports Python 3.6-3.9, causing installation failures on Python 3.12+.
Moving whisper to an optional dependency allows the meta-package (openadapt) to install successfully while users who need transcription can explicitly opt-in with pip install openadapt-capture[transcribe].
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
- Update author email to richard@openadapt.ai (
1987bee)
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
The workflow-name-based badge URL was showing "no status" because GitHub requires workflow runs on the specified branch. Using the filename-based URL format (actions/workflows/test.yml/badge.svg) is more reliable and works regardless of when the workflow last ran.
Co-authored-by: Claude Sonnet 4.5 noreply@anthropic.com
- ci: Remove build_command from semantic-release config (
93cdbb8)
The python-semantic-release action runs in a Docker container where uv is not available. Let the workflow handle building instead.
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
Chores
- Gitignore turn-off-nightshift test capture (
62b25be)
Test capture data (video, screenshots, database) should not be committed.
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
Continuous Integration
- Add auto-release workflow (
c3b3eb8)
Automatically bumps version and creates tags on PR merge:
- feat: minor version bump
- fix/perf: patch version bump
- docs/style/refactor/test/chore/ci/build: patch version bump
Triggers publish.yml which deploys to PyPI.
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
- Switch to python-semantic-release for automated versioning (
b9246a6)
Replaces manual commit parsing with python-semantic-release:
- Automatic version bumping based on conventional commits
- feat: -> minor, fix:/perf: -> patch
- Creates GitHub releases automatically
- Publishes to PyPI on release
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
Documentation
-
Add overview of package purpose
-
Add quick commands for installation, testing, and usage
-
Add architecture overview and key components
-
Add links to related projects
-
Add viewer screenshot to README (
a22c789)
Add screenshot of the Capture Viewer HTML interface to improve documentation and show users what the viewer looks like.
Features
-
Add browser event capture via Chrome extension (
553bb0a) -
Add BrowserBridge WebSocket server for Chrome extension communication
-
Add browser_events.py with Pydantic models for click, key, scroll events
-
Add Chrome extension with manifest v3 for DOM event capture
-
Export browser bridge API from init.py
-
Add step navigation controls to HTML visualizer
-
Comprehensive test suite (800+ lines)
Also includes:
- docs/whisper-integration-plan.md: Whisper strategy analysis
- README improvements with ecosystem documentation
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
- Add faster-whisper backend for 4x faster transcription (
6a8e30e)
Add support for faster-whisper as an alternative transcription backend:
- New transcribe-fast optional dependency in pyproject.toml
- Backend auto-detection (tries faster-whisper first, falls back to openai-whisper)
- New --backend CLI option: auto, faster-whisper, openai-whisper, api
- Maintain backward compatibility with existing --api flag
Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com
Detailed Changes: v0.1.0...v0.2.0