When the random portrait checkbox is on and portrait dropdown is Off,
the crop bar appears and the video overlay shows 2 red vertical lines
(instead of filled red bands) indicating the 9:16 crop boundaries.
Clicking the crop bar or video adjusts the crop center position.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Small panel to the right of the video displays the frame at cursor +
clip_span. Updated with 300ms debounce on cursor move, spread/clip
count change, and file load. Uses ffmpeg to grab a single PNG frame
off the main thread.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Checkbox '1 random portrait' in settings row. When checked, one random
clip in each batch receives a randomly chosen portrait ratio (9:16, 4:5,
or 1:1) with a random crop center. The rest use the global portrait
setting. Disabled for single-clip overwrite exports.
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>
Targets last export by default; targets marker-selected clip when one is
active (shown as "Delete <name>"). Clears on cursor move if no last export.
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>
- Queue ✓: mark files green after export and on drop if already in DB
- Export folder auto-created if missing
- Scrub while playing: cursor drag seeks and continues playback
- Counter auto-advances past existing files on disk
- Instant pending marker on timeline when export starts
- WebP quality 92 + lanczos scaling
- seek() guard against unloaded state (SystemError fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Editable label combo with DB history (most recent first)
- Click marker to arm overwrite mode (↺ indicator, re-exports same file)
- Portrait crop overlay: red bands on cut regions (paused only)
- Cache video dimensions to avoid mpv property reads in paintEvent
- Queue marks done files with ✓ on drop and after export
- Export folder created automatically if missing
- Play continues from new position when scrubbing during playback
- Counter auto-advances past existing files on disk
- Instant marker on timeline when export starts (optimistic)
- Marker right-click delete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mpv embedding:
- Replace wid/QOpenGLWidget with QOffscreenSurface + QOpenGLFramebufferObject
+ QPainter readback — works on Wayland/KDE without sub-surface compositing
- Force desktop OpenGL 3.3 core profile before QApplication (fixes black output on GLES)
- Timer-based render polling (16 ms) replaces signal-flood from mpv C thread;
fixes playback animation and scrubbing preview
- Fix AB-loop: set ab-loop-a/b to "no" on stop (0 means infinite in mpv)
Timeline:
- Full redesign: time ruler with adaptive major/minor ticks, playhead triangle
handle, selection region with edge lines, numbered marker badges
- Height 160 px; layout collapsed from 4 rows to 2 below timeline
- Markers appear immediately on export (optimistic update before ffmpeg finishes)
- Right-click marker → context menu to delete from DB
Hotkeys:
- Replace keyPressEvent with QShortcut(ApplicationShortcut) so keys work
regardless of focused widget; MpvWidget gets NoFocus policy
Export:
- WebP: switch lossless→lossy quality 85, compression_level 1 (~10x faster)
- Add -threads 0 for full CPU utilisation during decode/filter
- Remember last export folder across sessions via QSettings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>