Cache per-video pose attributes to skip load-time pose scan (KLAUS-506) - #413
Conversation
There was a problem hiding this comment.
Pull request overview
Introduces a persistent, per-video cache of pose-derived metadata to avoid opening and scanning every pose HDF5 file on subsequent project loads, significantly reducing load time for large projects.
Changes:
- Added
pose_attribute_cachemodule to load/save a schema-versioned JSON cache keyed by video filename and pose-filestattoken. - Updated
Project._run_video_scanto reuse cached per-video pose attributes and rescan only new/changed pose files (while bypassing cache whenenable_video_check=True). - Added unit + integration-style tests covering cache hits, invalidation, new-video-only scans, and
use_cache=False.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/project/test_pose_attribute_cache.py | Adds tests validating cache persistence, cache-hit behavior, and invalidation scenarios. |
| src/jabs/project/project.py | Implements cached scan path and reconstructs scan results from cached pose attributes. |
| src/jabs/project/project_paths.py | Adds pose_attribute_cache_file path helper (returns None when caching is disabled). |
| src/jabs/project/pose_attribute_cache.py | New module handling token generation and resilient cache load/save. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…e-attribute-cache
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/jabs/project/project.py:318
future_to_videois created as a mapping to video names, but the video-name values are never used (the loop iterates over the dict keys). This adds unnecessary work and can be misleading; either use a plain list of futures or use the mapping to add context when a future raises.
future_to_video = {
process_pool.submit(scan_video_metadata, job): job["video"] for job in jobs
}
results: dict[str, VideoScanResult] = {}
for future in as_completed(future_to_video):
| try: | ||
| with cache_path.open("r") as f: | ||
| data = json.load(f) | ||
| except (OSError, ValueError): |
|
Addressed the suppressed low-confidence note about |

Summary
Caches the per-video pose attributes read at project load (frame count, identity count, static objects, lixit keypoints, cm-per-pixel) so subsequent loads rescan only new/changed pose files instead of opening all of them.
Measured on a 488-video project (external SSD): cold load 3.88s (488 pose scans) → cached load 0.01s (0 scans), with identical identity counts and pose version.
What changed
jabs/cache/pose_attribute_cache.json, keyed by video filename and gated by a cheapstattoken (size:mtime_ns) of the pose file; schema-versioned.pose_attribute_cache.py(pose_token/load/save);ProjectPaths.pose_attribute_cache_file(returnsNonewhenuse_cache=False)._run_video_scansplit into_scan_jobs+_run_cached_video_scan. The opt-inenable_video_checkpath bypasses the cache, since video frame counts are not cached.VideoManager/FeatureManager(sameVideoScanResult); no consumer changes.Behavior
stattoken) or a pose-version upgrade invalidates that entry; removed videos are pruned.clear_cache()removes the file harmlessly;use_cache=False→ no persistence.Tests
13 new tests: module unit tests plus cache-hit (no rescan), token invalidation, new-video-only scan,
use_cache=False, and fresh-vs-cached parity. Full suite green.Jira: KLAUS-506