Adds a pure function that returns the latest keyframe at or before a
given time (with tolerance), replacing the inline lookup pattern that
appears multiple times in main.py. Includes 6 tests covering empty
list, before-first, exact match, between, after-last, and tolerance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lock button (G key) freezes export cursor, timeline scrubs playback only
- In lock mode, clicking crop bar sets a keyframe at current playback time
- Orange diamonds on timeline show keyframe positions
- Export resolves per-clip crop center from nearest preceding keyframe
- Crop bar/overlay updates while scrubbing to preview effective crop
- Unlocking clears all keyframes
- Replace fuzzy filename matching with exact match to prevent marker bleed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each profile has its own set of timeline markers, so the same video
can be cut with different settings (e.g. landscape vs portrait) without
markers interfering. Profile selector in the top bar, persisted in
QSettings, stored per-row in the DB.
- Add `profile` column to ProcessedDB schema (migrates existing rows
to 'default')
- Scope find_similar, get_markers, _get_markers_for by profile
- Add get_profiles() for populating the combo dropdown
- Thread profile through _DBWorker, _after_load, _refresh_markers,
_on_clip_done, dropEvent, _on_open_files
- Editable profile QComboBox in top bar, refreshed after each export
- 5 new tests for profile isolation and backward compatibility (54 total)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Style QComboBox/QSpinBox/QDoubleSpinBox/QCheckBox in dark theme
- Delete and overwrite now operate on the full clip group, not just one sub-clip
- Add get_group/delete_group to ProcessedDB with tests
- Restructure control rows: transport+actions / annotation+path / encoding
- Add "Open Files" button to queue panel (replaces drag-drop-only)
- Playlist right-click to remove items
- Compact time display (1:23.4 / 5:00.0), window title shows filename
- Short side: QLineEdit → QSpinBox with validation
- Tooltips with keyboard shortcuts on all interactive widgets
- Fix arrow hint direction, remove stale mask comment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dead code — masking is handled externally via ComfyUI. Removes
SetupWorker, MaskWorker, SettingsDialog, build_mask_output_dir,
the mask UI row, Settings button, and associated test cases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each batch export creates a subfolder named after the group (e.g.
clip_001/) containing all sub-clips and their audio files. Keeps
the top-level export folder clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each export generates clip_NNN_0, clip_NNN_1, clip_NNN_2 offset by the
spread value (2–8s, default 3s). Preview plays the full span covered by
all three clips. Marker click still overwrites a single clip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each exported clip writes an entry to <folder>/dataset.json containing
its relative path, sound label, and fps. Re-exporting to the same path
updates the existing entry (upsert). Empty labels are skipped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds build_audio_extract_command and runs it in ExportWorker after the
frame sequence completes. Audio written to <sequence_dir>.wav (lossless
pcm_s16le). Extraction failure (no audio stream) is silently ignored.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>