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>
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>
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>
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>
".../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>
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>
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>
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>
- 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>