Skip to content

feat(utility): add printf-style frame pattern support (%04d)#235

Open
throb wants to merge 18 commits intoAcademySoftwareFoundation:mainfrom
throb:feature/printf-frame-patterns
Open

feat(utility): add printf-style frame pattern support (%04d)#235
throb wants to merge 18 commits intoAcademySoftwareFoundation:mainfrom
throb:feature/printf-frame-patterns

Conversation

@throb
Copy link

@throb throb commented Mar 17, 2026

Summary

  • Add printf-style frame sequence pattern support (%04d, %06d, etc.) to parse_cli_posix_path(), complementing existing hash (####) and fmt ({:04d}) patterns
  • Handle Nuke-style space-separated frame ranges (file.%04d.exr 1000-1080) in addition to equals syntax (file.%04d.exr=1000-1080)
  • Defensive conversion in frame_groups_from_sequence_spec() for paths that bypass the main parser

Motivation

Printf-style frame patterns are the standard format used by ShotGrid/Flow Production Tracking, Nuke, Houdini, and most VFX pipeline tools. Without this, integrating xSTUDIO with production tracking systems requires workarounds like directory scanning instead of direct path loading.

Changes

File Change
src/utility/src/helpers.cpp Add xstudio_printf and xstudio_prefix_printf regex patterns, printf_to_fmt converter, space-separated range normalization
src/utility/src/frame_list.cpp Add %0Nd{:0Nd} conversion in frame_groups_from_sequence_spec()

Supported inputs (new)

file.%04d.exr=1000-1080       printf + equals range
file.%04d.exr 1000-1080       printf + space range (Nuke style)
file.1234.%04d.exr            printf with prefix frame number
/full/path/shot.%06d.exr      variable padding width

All existing patterns (####, {:04d}, =range) continue to work unchanged.

Test plan

  • parse_posix_path("test.%04d.exr=1000-1080") → URI test.{:04d}.exr, FrameList 1000-1080
  • parse_posix_path("test.%04d.exr 1000-1080") → same result (space-separated)
  • parse_posix_path("test.####.exr 1000-1080") → hash + space also works
  • parse_posix_path("test.{:04d}.exr 1000-1080") → fmt + space also works
  • parse_posix_path("shot.%06d.exr 100-200") → variable padding width
  • Existing hash and fmt patterns unchanged
  • Tested with real ShotGrid paths loading EXR sequences in xSTUDIO

🤖 Generated with Claude Code

throb and others added 18 commits March 10, 2026 16:56
…e loading

Three issues prevented image sequences (EXR, etc.) from loading on Windows:

1. Broken backslash regex in scan_posix_path() - the character class [\]
   matched ']' instead of '\', leaving backslashes in scanned file paths.
2. pad_size() returned 0 for non-zero-padded frame numbers (e.g. "1000"),
   producing invalid %00d / {:00d} format specifiers that failed extension
   matching in is_file_supported().
3. posix_path_to_uri() did not normalise backslashes to forward slashes on
   Windows, leaking them into file: URIs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --review / -v CLI flag to launch xstudio in "Present" layout
(viewport only, no playlists or timeline). This overrides saved user
layout preferences on startup.

Also add drag-drop handling to the viewport panel so files can be
dropped directly onto the viewer in any layout. Previously drops were
only handled by the media list panel, which doesn't exist in the
Present layout.

Bump version to 1.1.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove Gamma and Saturation from the default hidden toolbar items so
they are visible by default in the viewport toolbar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EXR Performance:
- Dispatch up to 4 concurrent precache requests in do_precache()
- Add configurable EXR decompression thread count preference (default 16)
- Fix off-by-one in EXR resolution reporting (max-min → max-min+1)
- Crop data window to display window by default (0% overscan)
- Add standalone EXR benchmark tool

Hotkey Editor:
- Replace read-only hotkey viewer with full interactive editor
- Click-to-capture key rebinding with conflict detection and warnings
- Persistent hotkey overrides saved to %LOCALAPPDATA%/xstudio/
- Search filtering, per-key and reset-all functionality
- Scrollbar for overflow content

Viewport Fixes:
- Fix SSBO shader debug colors (red/blue → black) for out-of-bounds pixels
- Fix FBO texture wrap mode (GL_CLAMP_TO_EDGE → GL_CLAMP_TO_BORDER)
- Fix overscan display: crop EXR data window to display window by default

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Layer dropdown to the OCIO colour pipeline plugin that lets
users switch between EXR layers/AOVs (e.g. RGBA, sky, mask, displace)
directly from the viewport toolbar and right-click context menu.

The backend stream switching already existed — this wires it to the UI:
- New StringChoiceAttribute populated dynamically from media streams
- Sends current_media_stream_atom on selection change
- Base ColourPipeline stores media source actor ref for stream queries
- CLAUDE.md updated with correct plugin deployment paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracted from PR AcademySoftwareFoundation#198 and adapted for Windows. Provides a VFX-oriented
filesystem browser panel with directory tree, file sequence detection
(via fileseq), version grouping, and thumbnail generation.

Key Windows fixes:
- Drive letter enumeration under virtual "/" root ("This PC")
- Case-insensitive path comparison throughout QML tree
- Forward-slash normalization on all path returns
- Fixed shadowed `time` import in _get_subdirs
- Direct attribute write from DirectoryTree (bypasses signal chain)
- Tree auto-syncs to current path on launch
- Up-directory button in path bar
- Depth spinner persists across sessions (title mismatch fix)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cache dump_json_headers() per part index — metadata is identical across
  all frames in a sequence, eliminates ~400 RTTI dynamic_casts per frame
- Cache MultiPartInputFile handle — reuse when same file path is re-read
  (scrub-back, multi-stream), avoids repeated open+header-parse syscalls
- Batch precache cache checks — single preserve_atom message per group
  instead of N individual request/response round-trips to cache actor
- Bump max_in_flight from 4 to 8 for better pipeline saturation on
  multi-core CPUs (benchmark: 83→88 fps on 32-thread system)

Benchmark on 4312x2274 ZIPS EXR (NVMe): confirms no regression,
app-level overhead reduced by eliminating per-frame redundant work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ions

- Add missing single-click directory navigation in file list view
  (onClicked only handled files, completely ignored folder clicks)
- Add folder navigation to thumbnail view click handler
- Fix directory tree command channel race: change_path and get_subdirs
  fired simultaneously through same attribute, one stomping the other.
  Added 100ms sync delay so get_subdirs waits for change_path to finish.
- Clear file list immediately on path change for instant visual feedback
  (previously old results lingered until new scan completed)
- Auto-expand selected node in directory tree on navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add right-click "Add/Remove from Favorites" on folders in file list
  and directory tree, fix add_pin bug (missing name parameter)
- Add multi-select: Ctrl+click toggles, Shift+click range selects
- Add "Load N Selected Files" context menu and batch double-click
- Add directory tree right-click context menu with favorites and scan
- Remove duplicate unreachable remove_pin handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show trim/move drag handles on clip hover in Select mode
- Suppress slip handles (leftleft/rightright) in Select mode to avoid
  visual doubling with basic trim handles
- Add clip:true to XsClipItem to prevent handle overflow
- Fix rightright drag indicator height when reparented to ancestor
- Add Fit Selection and Fit All menu items to timeline context menu

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Alt+wheel zoom centered on cursor position in timeline
- Shift+Z hotkey to zoom-to-fit all clips
- Ctrl+wheel zoom improved with multiplicative scaling
- Drag files from filesystem browser directly onto timeline tracks
- Auto-create video/audio tracks when dropping on Stack
- Compare Items: select multiple files, right-click to load in A/B or Grid compare mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Add to Timeline" right-click menu item in filesystem browser
  that creates clips directly on the active sequence (or creates a new one)
- Fix Playlist type checking: use isinstance(item, Playlist) instead of
  hasattr(item, 'add_media') to prevent Timeline/Subset crashes
- Fix drag-drop URI format: use file:/// (three slashes) for Windows paths
- Add text/uri-list detection in global drag handler for Drag.Internal drops
- Add External source handling in timeline drag handler
- Document QML rebuild requirement in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EXR files with hierarchical channel names (e.g. bg_wall.Combined.R,
fg_rocks.Diffuse Direct.B) were causing "Unable to choose decoder
routines" errors. The layer grouping split on the first dot, creating
layers with 30+ channels, but the reader only supports 4 channels per
layer (RGBA). Reading past the 4-element pix_type array produced
garbage pixel types that OpenEXR's decoder rejected.

Fix: split on the last dot (rfind) so bg_wall.Combined.R produces
layer "bg_wall.Combined" with channel "R". Also add defensive caps
to prevent array out-of-bounds if a layer still exceeds 4 channels.

Also fix filesystem browser sequence path handling for Add to Timeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Linux

Shell script and batch file wrapping CMake configure/build/deploy workflow
with auto-detected presets, portable deployment, and QML cache management.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added to .gitignore to keep project-specific AI instructions local only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three path handling bugs in URI conversion broke session restore:
- reverse_remap_file_path() was passed the original path instead of
  the resolved absolute path, discarding relative path resolution
- Drive letter regex only matched uppercase [A-Z], missing lowercase
- file URIs used host("localhost") causing parsing mismatches on
  deserialization; switched to host("") for standard file:///C:/ form

Timeline panel also failed to populate on session load due to:
- Model tree container lookup firing before async children arrived;
  added re-trigger in processChildren() for Session/Playlist types
- viewedMediaSetChanged() only handled typeRole=="Timeline" but the
  restored viewed container is often a Playlist; added logic to find
  the first Timeline child inside the Playlist and switch to it

Also updates README to document fork changes and remove the session
restore issue from known issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add support for printf-style frame sequence patterns (e.g. %04d, %06d)
in parse_cli_posix_path(), complementing the existing hash (####) and
fmt ({:04d}) pattern support. This is the standard format used by
ShotGrid, Nuke, Houdini, and most VFX production tracking tools.

Changes:
- helpers.cpp: Add regex matching for %0Nd patterns, converting to
  {:0Nd} internally. Also handle Nuke-style space-separated frame
  ranges ("file.%04d.exr 1000-1080") by normalizing to equals syntax.
- frame_list.cpp: Add defensive %0Nd -> {:0Nd} conversion in
  frame_groups_from_sequence_spec() for paths that bypass the main
  parser.

All existing patterns continue to work unchanged. New supported inputs:
  file.%04d.exr=1000-1080     (printf + equals range)
  file.%04d.exr 1000-1080     (printf + space range, Nuke style)
  file.1234.%04d.exr          (printf with prefix frame number)
  /full/path/shot.%06d.exr    (variable padding width)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant