- TimelineWidget.set_cursor: skip update() when clamped value is
unchanged (e.g. clicking at the boundary repeatedly).
- MainWindow: cache container_fps in self._fps on file load; keyPressEvent
reads the cached value instead of hitting the mpv property on every key.
- PlaylistWidget: add _path_set (set[str]) alongside _paths (list[str])
so duplicate detection in add_files is O(1) instead of O(N).
- SettingsDialog._on_install: quit+wait any previous SetupWorker before
starting a new one, preventing leaked threads and duplicate signal
connections on repeated installs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PlaylistWidget._select: only update the two items whose label changes
(old current → plain name, new current → add ▶) instead of rewriting
every item in the playlist.
- TimelineWidget: pre-compute (t/duration, path) fractions in
_hover_cache on set_markers/set_duration so mouseMoveEvent avoids
dividing by duration on every pixel of mouse movement.
- CropBarWidget: cache QPen in __init__ instead of recreating per frame.
- _step_cursor: update label/state immediately and start the timeline's
existing _seek_timer instead of calling _on_cursor_changed directly,
so held arrow keys are debounced the same as mouse drag.
- _refresh_markers: call _get_markers_for(filename) directly after
export (exact match known) and only fall back to fuzzy find_similar
when no exact-match rows exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TimelineWidget: debounce cursor_changed signal with 16ms timer so
mpv.seek is called at most ~60/s during drag; flush on mouseRelease.
Cache QPen/QFont objects in __init__ instead of recreating per frame.
- _normalize_filename: compile _QUALITY_RE and _SEP_RE once at module
level instead of on every call.
- ProcessedDB: add check_same_thread=False; add _get_markers_for() to
avoid a second find_similar pass; store db path.
- _DBWorker(QThread): runs find_similar + _get_markers_for off the main
thread. _after_load starts the worker instead of blocking; stale
results discarded if the user loads a different file first.
- MainWindow: reuse self._settings instead of creating a new QSettings
instance in the mask row setup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Arrow keys / J / L: step one frame; Shift = 1 second jump
Space / P: toggle play/pause
K: pause and return to cursor
E: trigger export
M: jump to next export marker (wraps)
Keys are suppressed when a text field has focus.
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>
Use SAM2VideoPredictor.from_pretrained() instead of the checkpoint-based
build_sam2_video_predictor() which doesn't accept HuggingFace model IDs.
Threshold out_mask_logits at 0.0 and squeeze shape before converting to
binary PNG.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>