Commit Graph

273 Commits

Author SHA1 Message Date
Ethanfel cb4392125d fix: scrub preview fallback before first keyframe + document overwrite behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 16:08:35 +02:00
Ethanfel 328c800d60 feat: apply keyframe crop modes in overwrite exports too
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 16:05:20 +02:00
Ethanfel 7a35e8268b feat: preview effective keyframe crop state during lock-mode scrub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 16:05:08 +02:00
Ethanfel 465894af51 feat: color-code keyframe diamonds by crop mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 16:04:53 +02:00
Ethanfel 1004bd0a28 feat: rewrite export to use per-keyframe crop modes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:58:01 +02:00
Ethanfel 279aee14cb feat: add apply_keyframes_to_jobs helper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:57:27 +02:00
Ethanfel 4f15f77175 feat: snapshot ratio and random flags into crop keyframes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:52:25 +02:00
Ethanfel 17e42c44b3 refactor: widen keyframe tuple to carry ratio and random flags
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:52:04 +02:00
Ethanfel 8e8c8b9774 feat: add resolve_keyframe helper to extract sorted-keyframe lookup
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>
2026-04-14 15:48:00 +02:00
Ethanfel b9e9fa927e chore: add .worktrees/ to .gitignore 2026-04-14 15:45:26 +02:00
Ethanfel 5916b498b1 docs: add keyframe crop modes implementation plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:30:46 +02:00
Ethanfel bda423e8b5 docs: add keyframe crop modes design
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 15:28:40 +02:00
Ethanfel 7b569dd248 docs: fix README to match current codebase
Remove fuzzy matching references (feature was removed), correct
test count (54 → 46), clarify that category is only saved to the
database not dataset.json, and add missing G and ?/F1 shortcuts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:58:53 +02:00
Ethanfel 3a2fd3d50b docs: add segment diagram SVG to README overview
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:36:14 +02:00
Ethanfel cfc0cb2f09 feat: multi-select in playlist for batch hide/remove
Extended selection mode (Ctrl+click, Shift+click) enabled. Right-click
context menu adapts to selection count — hide or remove multiple files
at once. Single click without modifiers still loads the file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 09:09:42 +02:00
Ethanfel d87b3c6da5 fix: disengage lock and clear keyframes when switching clips
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 08:53:33 +02:00
Ethanfel 39e7b19bc5 fix: rewrite playlist to never use hidden items, eliminating scroll bugs
Instead of hiding QListWidget items (which causes Qt to miscalculate
scroll position), the playlist now rebuilds its contents from scratch
whenever visibility changes. Only visible items exist in the widget.

- _rebuild() clears and repopulates from _paths filtered by visibility
- mark_done/unmark_done update in-place for visible items
- set_hidden_basenames and set_hide_exported trigger _rebuild
- Removes _LockedScrollBar, all scroll hacks, and workarounds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 00:09:07 +02:00
Ethanfel ab5c8ae3db fix: scroll playlist to top after session resume
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 00:05:56 +02:00
Ethanfel dcd4a6aace fix: locked scrollbar snaps to top instead of drifting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 00:05:08 +02:00
Ethanfel 96d4dd8d89 fix: replace scrollTo override with locked scrollbar to block all scroll
_LockedScrollBar subclasses QScrollBar and blocks setValue when locked.
This catches ALL scroll sources — Qt layout, auto-scroll, item changes —
not just scrollTo. Locked during setCurrentRow, visibility changes,
playlist checks, and video load (200ms after load to catch late events).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 00:00:58 +02:00
Ethanfel a6b91d9d3f fix: crop keyframes and position apply to random portrait/square exports
Keyframes were skipped when ratio was None (random mode), and random
crop was overwriting the resolved center with base_center. Now
keyframes resolve the center for all jobs first, then random crop
assigns the ratio while preserving the per-job center.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:55:06 +02:00
Ethanfel 12b06e8144 feat: cancel button to abort running exports
ExportWorker now uses Popen instead of subprocess.run so running
ffmpeg processes can be killed on cancel. Cancel button appears
between Export and worker count spinner, enabled during export.
Clips already finished before cancel are kept in the DB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:49:44 +02:00
Ethanfel 4c3b3fb2db fix: override scrollTo to block Qt auto-scroll during playlist operations
Replace timer hacks and scrollbar save/restore with a proper fix:
PlaylistWidget.scrollTo() is overridden to no-op when _scroll_locked
is set. Lock is held during setCurrentRow, visibility changes,
playlist checks, and video load — all operations where Qt's internal
auto-scroll was causing the list to jump.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:47:52 +02:00
Ethanfel 89d6feee47 fix: restore playlist scroll at 0/50/150ms to catch late layout events
mpv video surface resize triggers layout events across multiple event
loop cycles. A single deferred restore was too early.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:45:05 +02:00
Ethanfel 7051cc5b93 fix: defer playlist scroll restore to next event loop iteration
Qt processes layout changes asynchronously, so restoring the scrollbar
immediately after _after_load was too early. Use QTimer.singleShot(0)
to restore after Qt finishes processing pending layout events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:44:03 +02:00
Ethanfel 7c776e24af fix: stash playlist scroll before video load, restore after
Video load triggers layout changes (buttons, crop bar, preview window)
that cause Qt to recalculate the playlist scrollbar position. Now saves
the scroll value in _load_file and restores it at the end of _after_load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:42:55 +02:00
Ethanfel 04e78eb355 fix: remove scrollToItem that was overriding saved scroll position
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:41:19 +02:00
Ethanfel 97986d5138 fix: save/restore scrollbar position to prevent playlist scroll jumping
setCurrentRow, setHidden, and setText all trigger Qt internal scroll
recalculation. Now save scrollbar value before these operations and
restore it after, then use scrollToItem only to bring off-screen
selections into view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 23:40:17 +02:00
Ethanfel 3c903c7188 feat: right-click keyframe diamond on timeline to delete it
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:11:32 +02:00
Ethanfel bef08be091 fix: crop keyframe in lock mode updates video overlay and preview lines
The lock-mode path was only updating the crop bar but not the video
overlay or random overlays. Now sets _crop_center and calls the
appropriate overlay update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:10:02 +02:00
Ethanfel ccc94ccb5c fix: preview crop lines match main overlay colors
Portrait lines are red, square lines are blue — matching the video
overlay. Both shown simultaneously when both random options are on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:07:07 +02:00
Ethanfel d031d6c285 fix: hide-exported checkbox state applied on session resume
_apply_playlist_filters now syncs _hide_exported from checkbox before
refreshing visibility, so exported files are hidden immediately on
startup without needing to toggle the checkbox.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:03:08 +02:00
Ethanfel 9696b94b0c fix: preview crop shows lines only (no dim), updates on random toggle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 17:00:44 +02:00
Ethanfel 633e421a68 feat: show crop region overlay on end-frame preview
PreviewLabel widget replaces plain QLabel, draws dimmed areas outside
the crop window and blue border lines matching the crop bar position.
Updates live when portrait ratio or crop center changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:58:31 +02:00
Ethanfel a543c72ff5 fix: prevent scroll jumping by batching visibility and layout updates
- _apply_visibility wraps setHidden loop with setUpdatesEnabled and
  scrolls back to current item after
- _refresh_playlist_checks wraps mark_done/unmark_done loop similarly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:55:21 +02:00
Ethanfel 31772b898c fix: hidden files reappear on restart, scroll jumps on selection
- Centralize item visibility in _apply_visibility: hidden if
  profile-hidden OR (hide_exported AND done)
- mark_done/unmark_done no longer touch setHidden directly
- Session resume selects first visible item after filters applied
- add_files defers to _select_first_visible to skip hidden items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:53:17 +02:00
Ethanfel 9b8d742fde fix: scroll to selected item to prevent list jumping on click
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:49:01 +02:00
Ethanfel 8392c022f6 feat: session resume, hide exported files, profile-based file hiding
- Resume last session: reload previous playlist files on startup
- Hide exported checkbox: filter out files with existing clips
- Profile-based hiding: right-click → "Hide in profile" persists via DB
- Playlist scrollbar fix: disable updates during batch add
- Drop event and profile switch use unified _apply_playlist_filters()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:47:44 +02:00
Ethanfel d4357f0da4 feat: cursor lock with crop keyframing, remove fuzzy filename matching
- 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>
2026-04-13 16:34:49 +02:00
Ethanfel 2dcf9bc856 fix: preview window stays on top and raises with main window
- Add WindowStaysOnTopHint to keep preview above other windows
- Raise preview on main window activation (alt-tab, taskbar click)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:39:49 +02:00
Ethanfel 703874721b feat: YOLO subject tracking for per-clip crop centering, fix end-frame preview
- Track subject checkbox: auto-adjusts crop center per sub-clip using
  YOLOv8-nano detection on each start frame
- Detects target nearest to user's crop click, follows same class across clips
- Graceful fallback when ultralytics not installed or detection fails
- Fix end-frame preview not updating on clip/spread change (retry on busy grabber)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:12:17 +02:00
Ethanfel bd37938a4a fix: playlist shows clip count per file, export counter fills gaps after delete
- Playlist marks show [N] instead of ✓ to indicate how many clips exported
- Export counter scans from 1 to find first available slot instead of only advancing
- Refresh playlist checks and counter after marker/group deletion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:37:59 +02:00
Ethanfel e28af38743 fix: compact playlist rows by replacing word wrap with middle ellipsis
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:16:24 +02:00
Ethanfel 039d383cf6 fix: VAAPI filter chain, overwrite duplicates, export cursor stash, shutdown guards
- VAAPI: hwdownload before CPU filters, skip HW setup for image sequences
- Delete old DB rows before overwrite re-insert to prevent duplicates
- Stash cursor at export time so async completion uses correct position
- Restore crop_center on marker click, fix falsy 0-value checks
- Remove stale pending marker on export error
- Guard double mpv termination in closeEvent
- SnapPreviewWindow recursion guard for dock/follow moves

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:07:20 +02:00
Ethanfel e283d96417 feat: HW encode toggle, workers control, GPU decode, verbose logging, UI polish
- Add HW encode checkbox (auto-detects NVENC/VAAPI/QSV/AMF/VideoToolbox),
  caps GPU workers to 3 to avoid concurrent session limits
- Add workers spinbox next to Export button to control parallel ffmpeg count
- Enable mpv hwdec=auto for GPU-accelerated video decoding during playback
- Add timestamped terminal logging for startup, file load, export, errors
- Reorder settings row: video settings (Resize, Portrait) then encoding
  (Format, HW, Clips, Spread, random crops)
- Fix filename label wrapping by using QSizePolicy.Ignored + no word wrap
- Alternating row colors in playlist for readability
- Snap-to-edge preview window: docks to main window edges, follows on move
- Fix SEGV on exit: explicit mpv shutdown in MainWindow.closeEvent before
  Qt tears down QObjects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 12:56:00 +02:00
Ethanfel 1e99035393 feat: random square crop, shortcuts dialog, profile dropdown, video aspect fix
- Add "1 random square" checkbox (1:1 crop) alongside random portrait (9:16);
  both share the same ~1-per-3 quota when enabled together
- Multi-overlay system: portrait guides in red, square guides in blue, both
  visible simultaneously on the paused frame
- Replace editable profile QComboBox with non-editable dropdown + "New profile..."
  item via QInputDialog — fixes markers not updating on profile switch
- Refresh playlist checkmarks on profile switch (add/remove done marks)
- Add "?" button and ?/F1 shortcut to show keyboard shortcuts dialog
- Re-render video on widget resize to preserve aspect ratio
- Compute letterbox/pillarbox video rect for correct overlay + click mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 12:10:39 +02:00
Ethanfel 0e38c5666e fix: reset overwrite/delete state on profile switch
Switching profiles now clears the overwrite path, button states, and
next-label so stale state from the old profile doesn't persist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:22:20 +02:00
Ethanfel f9c5a42453 fix: make QComboBox dropdown arrow visible in dark theme
The previous border:none rule hid the dropdown indicator entirely,
making combos look like plain text fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:20:09 +02:00
Ethanfel f8b148f77d feat: profile support for independent marker sets
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>
2026-04-13 11:08:50 +02:00
Ethanfel 462af36bce docs: replace SELVA references with foley
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 03:01:38 +02:00