Commit Graph
100 Commits
Author SHA1 Message Date
EthanfelandClaude Opus 4.8 86980b38cf docs: Phase 3a implementation plan — interactive waveform selection
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 16:52:29 +02:00
EthanfelandClaude Opus 4.8 799b064703 docs: Phase 3 design — interactive waveform + per-join crossfade/curves + destructive editor
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 16:51:29 +02:00
Ethanfel 43326730f2 polish: preview failure dialog + clean audition temp on close + drop redundant Qt imports 2026-07-02 16:35:53 +02:00
EthanfelandClaude Opus 4.8 14ec29dbbe feat: merge temp cleanup on close + v1.4 changelog + README (Phase 2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 16:21:10 +02:00
Ethanfel 3922fc5174 feat: merge save + preview (crossfade render, duration guard, ffplay preview) 2026-07-02 16:18:29 +02:00
Ethanfel b8aa1606d8 feat: Merge sequence — add (file/selection), reorder, remove 2026-07-02 16:04:01 +02:00
EthanfelandClaude Opus 4.8 de7da3cad6 feat: Merge pane (3rd Audio QToolBox page) — list + crossfade + buttons
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:54:05 +02:00
Ethanfel 52417ea1ee feat: build_crossfade_merge_command — chained acrossfade / concat 2026-07-02 15:45:33 +02:00
EthanfelandClaude Opus 4.8 7573277a03 docs: audio merge (crossfade) — Phase 2 design + implementation plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:37:31 +02:00
Ethanfel 578ad87b1a docs: Audio workspace tab (Phase 1) — v1.3 changelog + README; drop orphaned transport spacer 2026-07-02 15:18:38 +02:00
EthanfelandClaude Opus 4.8 c57c078100 feat: play/stop audition of the current audio region
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:13:06 +02:00
Ethanfel 7501c4729b refactor: cap waveform decode length + guard frombuffer + tighten test 2026-07-02 15:00:34 +02:00
Ethanfel 12eaa944a7 feat: read-only waveform strip + manual refresh in the Audio tab 2026-07-02 14:53:32 +02:00
Ethanfel 4c5c4276c1 feat: core/waveform — peak reduction + ffmpeg region decode 2026-07-02 14:51:47 +02:00
Ethanfel 8e03b97742 feat: extract honors format picker + fade/normalize/gain edits 2026-07-02 14:47:49 +02:00
Ethanfel c9a4ac584f feat: Audio tab QToolBox — Extract & Edit + Scan/Classify panes 2026-07-02 14:35:22 +02:00
Ethanfel 4b3b25ffae feat: audio format + fade/normalize/gain widgets; free them from the transport row 2026-07-02 14:26:06 +02:00
EthanfelandClaude Opus 4.8 52ce2d451a refactor: rename Scan deck tab -> Audio (deck wiring only)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:13:48 +02:00
Ethanfel f584e54a8c feat: audio_edit_filters helper + optional -af chain on build_audio_clip_command 2026-07-02 14:09:14 +02:00
EthanfelandClaude Opus 4.8 8f18c7a817 docs: audio tab Phase 1 implementation plan (extract/edit/classify)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:36:37 +02:00
EthanfelandClaude Opus 4.8 e14cebd081 docs: audio workspace tab design (2-phase: extract/edit/classify + dataset)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:33:19 +02:00
EthanfelandClaude Opus 4.8 eab5c690c7 feat: audio area length — remove the upper cap + step by 1s
The audio extract length is meant for visualizing/grabbing sequences that can
run minutes long, but the control capped it and stepped in fiddly 0.10s
increments. Raise the range to effectively unlimited (24h; ffmpeg stops cleanly
at end-of-file if the source is shorter) and make the arrows step 1s — typing
still allows sub-second precision. Widen the field for the larger values.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 01:11:57 +02:00
EthanfelandClaude Opus 4.8 4445f0e7f4 fix: audio extract honored a silent length clamp — 30s near the end became 3s
_on_extract_audio clamped the duration to (timeline_duration - cursor), so with
the playhead within the requested length of the end (or any under-reported
duration) a 30s request was silently truncated to whatever remained — the user
asked for 30s and got 3s with no indication why.

Drop the clamp: pass the requested length straight to ffmpeg, which stops
cleanly at end-of-file if the source is shorter. Then ffprobe the result and,
when it comes up short, say so ("Saved 3.0s — source ended before 30.0s
requested") instead of silently shrinking. When there's room, 30s now yields
exactly 30s.

Adds core.ffmpeg.probe_duration(). Verified end-to-end: a fitting request
returns the exact length; a genuine near-end request returns the available
audio (rc=0) and is reported as truncated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 00:07:35 +02:00
EthanfelandClaude Opus 4.8 ed63d04abf feat: Extract audio area — exact-length audio slice from the playhead, save-as
A dedicated "♪ Extract audio" button on the transport row grabs an exact
length of audio (set via the adjacent length box, from the playhead) and opens
a Save As dialog. Output format follows the chosen extension — WAV (pcm_s16le),
MP3 (libmp3lame), FLAC, m4a/aac, ogg/opus — re-encoding as needed; unknown
extensions let ffmpeg pick from the container.

- core.ffmpeg.build_audio_clip_command(input, start, duration, out_path):
  fast-seek + exact -t duration + -vn, codec by extension. Verified end-to-end
  (wav/mp3/flac all land at exactly the requested duration).
- Timeline shows the audio area as a distinct teal dashed band spanning
  [cursor, cursor+length], updated live as the playhead or length changes, so
  you see exactly what will be extracted.
- Length + last save dir persist in QSettings; button enabled once a file loads.

Tests: 3 core (codec-by-extension, exact length, case-insensitive) + 2 GUI
(controls exist, band tracks cursor/length).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:48:24 +02:00
EthanfelandClaude Opus 4.8 7ae1720b9e fix: subcategory export buttons hidden by ghost entries + give them their own centered row
Two issues with the per-subprofile (subcategory) export buttons:

1. Visibility was decided by a fuzzy `f.endswith("_" + suffix)` match against
   the hidden-subcats set. A ghost "_blowjob" (empty-base leftover from the
   trailing-slash folder bug) or an unrelated "mp4_no_clap" would match and
   hide the wrong button — so enabling a subcategory in the Sub menu never
   revealed its export button. Match the exact "<base>_<suffix>" folder name
   instead (same name the menu shows and _hidden_subcats stores).

2. The buttons were crammed into the transport row after Export. Move them to
   their own row with stretches on both ends so the (often many) "▸ name"
   buttons stay centered and out of the transport controls.

Also cleared the polluted hidden_subcats/POV_Front set in the user's QSettings
(ghost "_*" names + a hide-all'd set of real "mp4_*"), so every subcategory is
visible again. Regression test added for the exact-match predicate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 14:19:20 +02:00
EthanfelandClaude Fable 5 514607eddd fix: harden export-folder base derivation against a trailing slash
A folder ending in "/" made os.path.basename() return "", so subprofile
folders/labels became "_blowjob" instead of "mp4_blowjob" — cluttering the
subcategory menu and breaking the marker↔category match. rstrip the trailing
separator in _tab_export_folder and the three basename(_txt_folder) sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-19 13:54:32 +02:00
EthanfelandClaude Fable 5 4299de5f97 fix: keep length control + mode in sync on every active-tab switch; dup preserves LTX-2; auto-export + frames use legal LTX-2 length
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 15:35:46 +02:00
EthanfelandClaude Fable 5 86ab606059 docs: changelog + README for LTX-2 mode + tab features (v1.2)
Bump APP_VERSION to 1.2 and add a 1.2 changelog entry covering the
per-tab export folder + mismatch guardrail, Duplicate tab, and LTX-2
export mode. README Interface section gains matching bullets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 15:16:18 +02:00
EthanfelandClaude Fable 5 87ccd8650c feat: honor LTX-2 mode in re-export and auto-export
Mirror the manual export path: re-export and auto-export now read the
active tab's LTX-2 params via _ltx2_export_params() and override
short_side/duration plus thread target_fps/snap32/frames through to
ExportWorker. Foley tabs return None and keep byte-identical behavior.
For auto-export, params are captured at batch-build time so queued
batches keep their own geometry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 15:16:02 +02:00
EthanfelandClaude Fable 5 ad9e564991 feat: LTX-2 frames length control + route 25fps/÷32/exact-frames through export
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 15:10:07 +02:00
EthanfelandClaude Fable 5 4baac54930 feat: per-tab LTX-2 mode toggle + [LTX2] badge (pipeline wiring in next stage)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 15:03:32 +02:00
EthanfelandClaude Fable 5 879684ce25 fix: audio extract duration for LTX-2 frame-exact clips
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:58:56 +02:00
EthanfelandClaude Fable 5 92774216d4 feat: LTX-2 ffmpeg params (target_fps, snap32, frames)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:58:50 +02:00
EthanfelandClaude Fable 5 02fd0f0919 feat: LTX-2 legal-frame helpers (core/ltx2.py)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:58:44 +02:00
EthanfelandClaude Fable 5 c537ac678d docs: LTX-2 per-tab export mode implementation plan
6 stages: core/ltx2 frame math (TDD), ffmpeg target_fps/snap32/frames (TDD),
per-tab _mode, tab duplicate/convert menu, length-control swap + export wiring,
finalize. Builds on tab-export-folder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:55:05 +02:00
EthanfelandClaude Fable 5 755f7e5131 docs: LTX-2 per-tab export mode design
Per-tab foley|ltx2 pipeline mode + "Duplicate as LTX-2". LTX-2: frame-exact
length (F%8==1), force 25fps, center-crop to ÷32. Soft preset, builds on the
per-tab export folder feature. core/ffmpeg gains optional target_fps/snap32.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:55:05 +02:00
EthanfelandClaude Fable 5 1eb7de2a1a fix: duplicate-tab folder is a sibling, not a child, when source ends in /
".../AlexisCrystal/" + "_copy" was producing ".../AlexisCrystal/_copy"; rstrip
the trailing separator first → ".../AlexisCrystal_copy". Regression test uses a
trailing-slash source folder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:52:12 +02:00
EthanfelandClaude Fable 5 d7680283a2 test: isolate QSettings in GUI tests so they never touch the real ~/.config/8cut
Constructing MainWindow loads and (on close) re-saves the playlist tabs; a test
that mutated tab state could persist into the user's real session. Redirect
QSettings to a temp dir at import time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:47:35 +02:00
EthanfelandClaude Fable 5 bf4b6dad2d feat: right-click "Duplicate tab" — clone files into a new tab with adapted name + own folder
New tab copies the source tab's video list + separators, gets a unique
"<name> copy" label and an adapted own export folder ("<folder>_copy"), and
inherits the tab-named-folder flag. No files are moved or copied — you export
into the new tab's folder. Keeps Foley/variant datasets separate without the
file-shuffling that a misexport used to require.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-18 14:36:47 +02:00
EthanfelandClaude Fable 5 4715c0ce49 fix: sync export folder when selecting a file in a side-by-side list; tighten guardrail; rename per-tab attr
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 01:06:21 +02:00
EthanfelandClaude Fable 5 e5ce59c065 feat: bind export folder to each file-list tab + export-folder mismatch guardrail
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-16 00:56:55 +02:00
EthanfelandClaude Fable 5 cbbdfeadb1 feat: logo-based icon set + accent aligned to brand palette
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:56:54 +02:00
EthanfelandClaude Fable 5 8a7d761815 chore: drop stale scaffolding comments flagged in final review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:47:41 +02:00
EthanfelandClaude Fable 5 140a424469 docs: changelog + README for the UI overhaul (v1.1)
Bump APP_VERSION to 1.1 with a "What's new" entry covering the menu bar,
tabbed control deck, side-by-side panels, status bar, and visual polish.
Add an Interface section to the README. Shortcuts unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:39:53 +02:00
EthanfelandClaude Fable 5 bc6e30a2d4 change: deck split shows exactly the pinned panels (no leftover column)
Pinning 2 of 3 panels previously showed a 3rd "leftover" tab-column, which
read as all-three-pinned and was confusing. Now the split view shows exactly
the pinned panels (pin 2 -> 2 columns, pin 3 -> 3). Adds an always-available
View > Side-by-side panels submenu of checkable toggles as the way to pin a
panel while already in split view (the right-click-tab gesture only works in
tabbed mode). Tests assert exactly-N-columns and the menu-pin path; the win
fixture now resets deck state so tests don't depend on persisted layout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 16:26:35 +02:00
EthanfelandClaude Fable 5 2ea3a9149a fix: allow pinning the 3rd deck panel from split mode; dedupe header height
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:50:04 +02:00
EthanfelandClaude Fable 5 e820c106af test: structure tests for control-deck side-by-side mode
- test_deck_stack_exists: _deck_stack present; default shows _control_deck.
- test_pinning_two_panels_switches_to_split: pin 2 panels + refresh →
  stack shows _deck_split_container.

Pin via _pinned flags directly (not the toggle handler) so no QSettings
write leaks into other function-scoped windows; existing 6 tests run in
default/tabbed state and still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:36:08 +02:00
EthanfelandClaude Fable 5 780832d4aa feat: side-by-side mode for the control deck (pin panels into columns)
Mirror the playlist pin→side-by-side pattern for the Export / Crop &
Track / Scan control-deck panels. Right-click a deck tab → "Show
side-by-side"; pinning 2+ panels lays them out as resizable QSplitter
columns, with any unpinned panel kept reachable in a leftover tab-column.
The ✕ header returns a panel to tabs. State persists across launches via
the deck_pinned QSettings key.

- _DeckTabBar: minimal QTabBar emitting pin_toggle_requested(idx).
- _build_control_deck wraps _control_deck + a split container in a
  QStackedWidget (_deck_stack), mounted in right_layout in its place;
  sets _pinned/_label/_deck_key on each page; builds _deck_panels.
- _refresh_deck_layout / _detach_deck_panels / _clear_deck_split /
  _on_deck_pin_toggle / _on_deck_unpin / _save_deck_layout, guarded by
  _deck_loading. Reparented pages are setVisible(True) so they don't
  render blank (same gotcha the playlist documents).
- Restore block at the end of __init__ reads deck_pinned (str/list).
- Height-pin now targets _deck_stack and fits the tallest split-mode
  column (22px header + content) so split mode never clips.

Default (nothing pinned) behaves exactly like the prior tabbed deck.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:36:03 +02:00
EthanfelandClaude Fable 5 6037f15e7b docs: multi-pane control deck design + plan addendum
Pin deck panels (Export/Crop/Scan) side-by-side as resizable columns,
mirroring the playlist pin pattern; unpinned panels stay reachable as a
tab-column. Spec for the multi-pane feature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:31:22 +02:00
EthanfelandClaude Fable 5 035eaf3894 style: unified theme, primary Export, group separators, clearer labels
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:19:25 +02:00
EthanfelandClaude Fable 5 35ea1baec8 fix: keep Subprofiles▸Remove menu in sync with subprofile changes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:15:07 +02:00
EthanfelandClaude Fable 5 6a71386ed8 fix: robust deck height, state-aware Scan/Train menu items, import cleanup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:04:15 +02:00
EthanfelandClaude Fable 5 d1fb35af8e refactor: populate Crop & Scan tabs; menu-only buttons hidden; drop settings row
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:54:10 +02:00
EthanfelandClaude Fable 5 c55693094d refactor: add control deck; move export/encode controls into Export tab
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:48:26 +02:00
EthanfelandClaude Fable 5 5832d08b26 feat: real status bar replaces inline status label
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:37:52 +02:00
EthanfelandClaude Fable 5 b4cfa7561a fix: resolve menu-bar shortcut collision, checkmark desync, brittle test
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:33:05 +02:00
EthanfelandClaude Fable 5 0ccc29709e feat: add menu bar wired to existing handlers; move profile selector and help into menu-bar corner
Adds MainWindow._build_menubar building File/Edit/Scan/View/Help menus
whose actions reuse the existing handler methods. Profile combo and the
? shortcuts button move from top_bar into a TopRightCorner widget. Adds
_show_about and _rebuild_remove_subprofile_menu helpers. Bidirectional
sync for Hide exported / Show hidden; forward-only sync for Review mode
(reverse added in a later stage).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:24:24 +02:00
EthanfelandClaude Fable 5 7e917d00a6 test: add MainWindow structure smoke test (skips headless)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:22:49 +02:00
EthanfelandClaude Fable 5 2ffb81eaa3 docs: UI restructure design + implementation plan
Tabbed control deck reorg of MainWindow: menu bar for rare actions,
always-visible transport bar, 3-tab control deck (Export / Crop & Track /
Scan), real status bar, plus a visual-polish pass. No behavior, shortcut,
or core/ changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 11:18:47 +02:00
EthanfelandClaude Opus 4.6 b448085242 fix: many more distinct subcategory marker colors (24, was 5)
Subcategory marker groups cycled through only 5 colors, so the 6th repeated.
Generate 24 colors across the hue wheel, interleaved (coprime step) so
consecutive groups are ~105 deg apart and colors only repeat after 24.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-11 15:02:16 +02:00
EthanfelandClaude Opus 4.6 7cf90c1e5c feat: jump playback to 3s before the new end when the play area shrinks
When autoclip (A / back button) or a wheel scroll reduces the clip span, seek
playback to 3s before the new end and loop there so the shorter cut point can
be reviewed immediately. Growing the play area is unaffected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-08 13:14:10 +02:00
EthanfelandClaude Opus 4.6 5aa6878cf6 fix: pin mpv speed every playback tick so it can't drift to half
The x2/x4 toggle always derives the right speed (1/2/4), but mpv's speed could
still drift out of sync during the ab-loop playback (e.g. across loop seeks),
making it feel half-speed after x2 -> x4 -> x2. Reassert the desired speed on
each render tick while playing so mpv always matches the buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-08 11:45:56 +02:00
EthanfelandClaude Opus 4.6 0e903812fa feat: discreet ghost mark at the cursor's previous position
When you move the highlight start (click/drag the timeline), leave a single
faint dashed line at where it was, so an accidental move is easy to undo by
eye. Only the most recent prior position is kept, it's suppressed when leaving
an already-exported spot (it has a marker), and it clears on a new file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-08 11:17:56 +02:00
EthanfelandClaude Opus 4.6 d23ae2e88a fix: speed (x2/x4) stays in sync with playback; reclick resumes 1x
The clip preview loops via ab-loop; mpv's speed could drift out of sync with
the x2/x4 buttons, so toggling a speed off didn't reliably return to normal.
Centralize the desired speed in MpvWidget and reapply it on every play_loop,
and derive the effective speed from the button state on each click so a
re-click cleanly returns to 1x.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 22:29:16 +02:00
EthanfelandClaude Opus 4.6 d97de8de10 feat: mouse back side-button triggers autoclip on the timeline
The lower/back side button (Qt BackButton) fits the clip count to the current
play position — same as the A hotkey / autoclip. Forward button left unused.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 22:04:31 +02:00
EthanfelandClaude Opus 4.6 c6673228fa change: right-click = delete menu, middle-click = lock, wheel = ±clips
- Restore right-click to the marker/keyframe delete menu (no longer toggles
  lock); kept the no-seek guard so it doesn't nudge the cursor.
- Middle-click (no drag) now toggles cursor lock; middle-drag still pans.
- Plain mouse wheel over the timeline adds/removes clips (up +1, down -1,
  clamped); Ctrl+wheel still zooms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 21:55:53 +02:00
EthanfelandClaude Opus 4.6 fa4104eded fix: middle/right click no longer scrub the timeline cursor
mousePressEvent fell through to _seek for right-click, and mouseMoveEvent
scrubbed on any held button — so a middle/right click that nudged the mouse a
pixel moved the cursor. Right-click now returns early in press/release, and the
drag-seek in mouseMoveEvent is restricted to the left button. Middle still
bumps the clip count, right still toggles lock / opens the delete menu, left
still scrubs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 21:28:46 +02:00
EthanfelandClaude Opus 4.6 9f7d2e1185 feat: timeline right-click toggles lock, middle-click bumps clip count
- Right-click on empty timeline toggles cursor lock (G). Right-clicking a
  marker/keyframe still opens the delete menu.
- Middle-click (press+release without dragging) adds one to the clip count
  and wraps at the max; middle-drag still pans the zoomed view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 21:24:52 +02:00
EthanfelandClaude Opus 4.6 c2e6c62c00 fix: timeline hotkeys keep working after clicking buttons / setting spinboxes
A focused QPushButton swallows Space/Enter and a focused spin box swallows
every key (its line edit also makes _KeyFilter suppress the app shortcuts), so
clicking Export or the clip-count spinner left the timeline hotkeys dead.

- Give all main-window buttons (incl. dynamic subprofile/format/unpin buttons)
  NoFocus so they never trap keyboard focus.
- Spin boxes clearFocus on editingFinished so hotkeys resume after Enter
  (clicking elsewhere already releases focus via _KeyFilter).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 21:06:32 +02:00
EthanfelandClaude Opus 4.6 8aa8d8805b perf: background the scan-panel DB reads on file load
load_for_file no longer runs three DB queries on the UI thread during file
load. A _ScanLoadWorker reads the bundle (hard negatives, scan-export times,
latest scan results) via its own short-lived connection — safe alongside the
main connection now that WAL is on. The table rebuild stays on the UI thread
in _on_scan_bundle_loaded; the timeline scan regions are synced from the new
loaded(filename) signal. Stale results from rapid file switches are ignored,
and the worker is drained on shutdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 20:16:47 +02:00
EthanfelandClaude Opus 4.6 35c67f4bd5 perf: single-pass get_training_stats (was O(folders × rows))
Group clips by export folder in one scan instead of re-scanning every row for
each folder; also drops the extra get_export_folders() query. Speeds up the
train-dialog stats with many subcategories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 19:52:13 +02:00
EthanfelandClaude Opus 4.6 b738a19304 perf: cut DB scans, timeline repaints, and per-frame allocations
Database:
- Enable WAL + synchronous=NORMAL + bigger cache pragmas
- Add (profile, filename) index covering the hot queries
- _refresh_playlist_checks: one get_clip_counts_grouped() scan for the whole
  profile instead of one query per file (was O(N) full scans per keystroke/
  tab switch/file load)

Timeline (60fps playback):
- set_play_position only repaints when the playhead moves a whole pixel or the
  view scrolls (≈30x fewer full repaints in non-zoomed playback)
- Cache all per-paint QColor/QPen objects and the other-folder color table in
  __init__ instead of allocating them every frame; drop the per-paint
  visible-markers list comprehension

File load / startup:
- PlaylistWidget stats files for the missing-set only when paths change, not on
  every filter keystroke
- Cache the vid-folder lookup (DB + os.listdir) per (file, folder) so spinner
  ticks don't repeat it; m-counter still recomputed so it stays correct
- Swap the waveform worker without blocking the UI thread (no wait(1000))
- Defer the changelog modal so the window is interactive first

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 19:50:41 +02:00
EthanfelandClaude Opus 4.6 dbd8e6a8ac fix: opened/dropped files go to the visible tab, not the last-interacted one
_on_open_files/dropEvent added to self._playlist (the last-interacted pane,
which could be the tab whose file is loaded in the player). Now they target
_add_target_playlist(): the currently visible tab in tab view, or the active
pane in side-by-side, and make it the active list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 17:04:31 +02:00
EthanfelandClaude Opus 4.6 73dfea4ae9 fix: show() reparented lists in side-by-side so they lay out
QTabWidget hides its non-current pages. Pinning a tab that wasn't the active
one reparented a hidden QListWidget into the split panel, where it stayed
hidden — the layout collapsed it, overlapping the header in the middle with
an empty/black list. setVisible(True) after reparenting fixes the panes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 16:38:47 +02:00
EthanfelandClaude Opus 4.6 2170e72cbd fix: usable side-by-side layout; make tab→folder a per-tab option
Side-by-side layout:
- Reduce pinned lists' min width (200→60) so two fit, widen the main
  splitter's left section when split is active (steal from the video pane)
  and restore on collapse. Fixed-height headers, list stretch, rebuild on
  reparent — fixes the empty/misplaced panes.

Tab→folder per tab:
- Replace the global "Tab→folder" checkbox with a per-tab toggle in the tab
  right-click menu ("Export to tab-named folder"). _tab_export_folder() now
  reads the active tab's flag; persisted per tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 16:28:26 +02:00
EthanfelandClaude Opus 4.6 c9915914c4 fix: rename tab export-folder helper to avoid clobbering self._export_folder
self._export_folder is an existing str attribute (stashed during export), so
the new _export_folder() method shadowed it and 'str' object is not callable
crashed on startup at _update_next_label. Renamed to _tab_export_folder().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 16:19:50 +02:00
EthanfelandClaude Opus 4.6 251747bb0b feat: side-by-side pinned tabs + optional tab-name in export folder
Side-by-side:
- Right-click a tab → "Show side-by-side" to pin it; 2+ pinned tabs render
  in a horizontal splitter (each with a name header + ✕ to unpin), via a
  QStackedWidget that swaps between the tab view and split view.
- Tabs are now backed by self._pws (source of truth) so persistence and
  layout work regardless of where each list is parented; pinned state saved.
- self._playlist resolves to the last-interacted pane (active pw), so exports
  and edits follow the pane you're acting in. Pinned flag persisted per profile.

Tab → export folder (optional):
- New "Tab→folder" checkbox: when on, the active tab's name is appended to the
  export folder (mp4 → mp4_BatchA). Default "List N" tabs are ignored.
- _export_folder()/_export_base_name() helpers route exports, markers, next
  label, and delete/clear paths so they stay consistent with the tab folder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 16:13:26 +02:00
EthanfelandClaude Opus 4.6 13c4d3f7f6 fix: keep separators when removing their anchor file; add Copy name
- _remove_paths re-anchors a separator to the next surviving file (or a
  trailing separator) instead of dropping it when its anchor is removed;
  used by both Remove and Delete-from-disk. Removal now persists tabs.
- Add "Copy name" / "Copy N names" to the playlist context menu (basenames
  to clipboard, newline-joined for multi-select).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 15:55:42 +02:00
EthanfelandClaude Opus 4.6 1d49ce7cee perf: run waveform ffmpeg at low priority so it yields to mpv on load
The first load of a file decodes the whole audio track in a background
thread; nice'ing it (os.nice(15)) reduces disk/CPU contention with mpv
during the initial open. Result is cached, so subsequent loads are fast.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 12:14:04 +02:00
EthanfelandClaude Opus 4.6 109bc658c3 feat: flag playlist files missing from disk (⚠ orange strikethrough)
Missing files are kept in the list instead of being silently dropped on load,
and styled distinctly with a tooltip. add_files gains allow_missing; tab
restore keeps missing entries so they're visible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-06 12:12:03 +02:00
EthanfelandClaude Opus 4.6 ec7138f51b feat: single Disable all / Enable all for every subcategory at once
Replace the per-folder submenu/buttons with one batch action: "Disable all"
moves every enabled subcategory (excluding the main folder and already-disabled
ones) to _disabled in one click; "Enable all" restores them. Available in both
the playlist right-click menu and the Sub button menu.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 14:19:49 +02:00
EthanfelandClaude Opus 4.6 68c633ab46 feat: add "Disable all in" / "Enable all in" to playlist right-click menu
Folder-wide disable/enable is now reachable right next to the per-video
"Disable in" submenu, listing every subcategory in the profile (not just
ones tied to the loaded video). Backed by profile-wide subcategory counts
pushed to the playlist in _refresh_playlist_checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 14:14:42 +02:00
EthanfelandClaude Opus 4.6 d0a94e7b68 fix: Sub menu lists all profile subcategories so Disable/Enable all is reachable
Previously the Sub menu only showed folders from the current video's markers
plus configured subprofiles, so subcategories without clips on the loaded
video (or without a matching subprofile) never appeared. Now it also includes
every subcategory that has clips anywhere in the profile (active or disabled).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 14:11:11 +02:00
EthanfelandClaude Opus 4.6 632c2dc076 feat: disable/enable all clips in a subcategory folder at once
- Sub menu now has per-folder "Disable all" / "Enable all" buttons with live counts
- relocate_video_clips accepts filename=None to move every video's clips in a folder
- get_all_folder_counts returns profile-wide per-folder counts (incl _disabled)
- Disable-all confirms before moving; both refresh markers + playlist counts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 14:08:20 +02:00
EthanfelandClaude Opus 4.6 0f335c5e66 feat: tabbed file lists with editable labels
- Wrap the playlist in a QTabWidget; each tab is its own file list
- "+" corner button adds tabs; double-click a tab to rename inline; tabs are closable (last tab protected) and movable
- self._playlist now resolves to the active tab's PlaylistWidget
- Persist tabs (label + files + separators) per profile as JSON; falls back to legacy session_files/separators on first load
- Filter box and playlist filters apply to the active tab; tab switches reapply filters and refresh marks
- Profile switch/duplicate/delete now save/load/copy/remove per-profile tab state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 13:30:18 +02:00
EthanfelandClaude Opus 4.6 f1f8fd5244 feat: playlist separator can be added above or below a file
- Context menu offers both "Add/Remove separator above" and "below"
- "Below" anchors to the next visible file, or a trailing line via end sentinel when clicking the last file
- End sentinel preserved across rebuilds and persisted per profile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 12:47:43 +02:00
EthanfelandClaude Opus 4.6 299779cf29 feat: disable videos per-subcategory, named models, multi-category training, playlist separators
- Train dialog: multi-select positive subcategories via checkbox list, optional model name suffix ({profile}_{model}_{name}.joblib)
- list_trained_models recognizes named model variants
- Disable a video per-subcategory: moves its clips to a sibling {subcat}_disabled folder, rewrites DB output_path, migrates dataset.json, marks the name red
- Disabled clips excluded from training, stats, timeline, and playlist counts
- Playlist per-video count reflects only visible, non-disabled subcategories
- Persist subcategory show/hide visibility per profile across restarts
- Add/remove playlist separator rows (right-click) to mark batches, persisted per profile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-05 12:45:03 +02:00
EthanfelandClaude Opus 4.6 56218c18f4 feat: speech detection, format export buttons, subcategory controls, crop overlay during playback
- Add speech detection via faster-whisper with red waveform coloring for speech regions
- Add format variant export buttons (P/S) next to Export and subprofile buttons when portrait/square enabled
- Add force_ratio parameter to _on_export for deterministic format exports
- Add subcategory show/hide with persistent checkbox menu (no longer closes on toggle)
- Show crop overlay lines during video playback, not just when paused
- Delete marker now also removes files from disk and cleans up annotations
- Clear all markers also deletes files and DB entries
- Add playlist text filter, clip spread tick lines on timeline
- Fix LD_PRELOAD for GLIBCXX in conda launcher

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-14 18:23:43 +02:00
EthanfelandClaude Opus 4.6 2c45aff668 feat: add delete-from-disk option in playlist context menu
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-10 11:38:37 +02:00
EthanfelandClaude Opus 4.6 07e2f733b9 feat: bulk update source paths in train dialog
Add ProcessedDB.update_source_paths() to re-resolve missing or stale
source_path entries by matching filenames against a directory listing
and the current playlist. Exposed as "Update paths" button in the
train dialog next to the video dir field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-09 13:47:48 +02:00
EthanfelandClaude Opus 4.6 8c5a4c4524 fix: marker labels show actual m-number from filename instead of time order
Extract the manual export counter (m1, m2, ...) from the output path
so timeline markers match their filenames. Falls back to sequential
numbering for old-format paths without m-prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 11:42:15 +02:00
EthanfelandClaude Opus 4.6 4e5b631efb fix: right-click delete works on other-folder markers too
The context menu hit test only searched the current folder's markers.
Now also checks other-folder markers so the delete option appears
for subprofile markers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 11:38:49 +02:00
EthanfelandClaude Opus 4.6 ec77b8224f feat: show other-folder markers in distinct colors on timeline
Subprofile/subfolder exports now appear as colored markers (yellow,
green, blue, purple, orange) with their own numbering, separate from
the main folder's red markers. Each folder gets its own color and
independent sequence numbers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 11:36:38 +02:00
EthanfelandClaude Opus 4.6 9becd5a06d fix: filter timeline markers by current export folder
Subprofile exports (folder_suffix) created markers that interleaved
with main folder markers, shifting their numbering. Now get_markers
and _get_markers_for accept an export_folder parameter and use
SQL LIKE to only return markers whose output_path is in that folder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 11:32:39 +02:00
EthanfelandClaude Opus 4.6 fae5560e2d feat: overview scrollbar on timeline when zoomed in
Thin 8px scrollbar appears above the ruler when the timeline is zoomed.
Shows a draggable thumb representing the current view window. Click
outside the thumb to jump, drag the thumb to pan. Ruler and track
shift down to make room. Scrollbar hidden when not zoomed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 11:47:37 +02:00
EthanfelandClaude Opus 4.6 07e3a1223c fix: unpack 4-tuple markers in export overlap check
The marker format was extended to include clip_span but the overlap
check in _on_export still unpacked 3 values, causing a crash on export.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 11:43:15 +02:00
EthanfelandClaude Opus 4.6 3af6e05fb7 fix: use exact seeking instead of keyframe-based seeking
mpv's "absolute" seek lands on the nearest keyframe before the target,
causing playback to start ~3s before the marker. Switch to
"absolute+exact" for both seek() and play_loop() so playback starts
at the precise requested time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 11:39:57 +02:00
EthanfelandClaude Opus 4.6 d787871735 fix: auto-pan timeline to follow playback position when zoomed in
Revert span opacity back to 35 (was fine). The actual issue was the
play position line disappearing when scrolled out of the zoomed view.
Now set_play_position auto-pans the view window to keep the playback
marker visible with a 10% margin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 11:36:25 +02:00
EthanfelandClaude Opus 4.6 85c08d7c48 fix: seek to exact marker position on click, increase clip span visibility
- _on_marker_clicked now explicitly sets cursor and seeks mpv to start_time
  instead of relying on the timeline's indirect seek chain
- Doubled clip span area opacity (35 → 70) so spans are always visible
- Trigger end-frame preview after config restoration on marker click

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-03 11:34:36 +02:00
EthanfelandClaude Opus 4.6 f6966a092a feat: per-profile playlists, marker span display, precise marker seek
- Per-profile playlist persistence (session_files/{profile} in QSettings)
- Training data resolves source videos via playlist paths before fallback dir
- Guard against deleted video files in _load_file
- Fix marker double-click to seek to exact marker time instead of click pixel
- Show manual clip spans as light amber areas on the timeline
- Extend marker tuples with clip_span from DB (clip_duration + overlap)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-02 17:11:50 +02:00