On failure the error window showed a generic message with the whole log
buried in an expander. Lead instead with a focused summary: which step
failed (the process label), its command, and the last stderr lines, while
keeping the full log below. The runner passes the failing process to
error_window, which reads the executor's bounded stderr_data.
build_summary/_stderr_tail are static and unit-tested (tests/
test_error_summary.py): tail extraction, CR handling, blank-line skipping,
stdout fallback, long-line truncation, and Pango-markup escaping of paths/
messages containing & < ' so set_markup can't choke.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The executor drained child stdout/stderr via GLib.IOChannel watches on the
main loop, so a stalled or wedged GUI stopped draining the pipes and the
child would block on a full (~64KB) pipe forever — the cascade that froze a
batch when the editor/terminal died.
Replace the main-loop watches with per-stream background reader threads that
drain pipes with blocking reads into thread-safe buffers; a GLib timer on the
main thread consumes the buffers and does all parsing/UI (threads never touch
GObject/GTK). Now the child can never block on a pipe we own, regardless of
main-loop state. Also: stdout can go straight to a file when requested, stdin
is fed from a file in a thread, the retained end-of-job log is bounded, and
wait_end is guarded to run once.
Tests (tests/test_executor_io.py): line parsing incl. CR progress lines,
2MB output with no loss/deadlock, completion with the main loop never running
(the wedged-GUI case), cancel, stdout-to-file, and exit-code propagation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prioritized A/B/C plan: safety net (tests, subprocess hardening, error
surfacing) first, then finishing fork features, then modernization.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
High profile is now fully automatic instead of relying on the manual
"adjust disc usage" button:
- compute_high_profile_bitrate() (pure, unit-tested) picks the fewest discs
that hold the content down to a quality floor, then fills each disc; a 2h
movie lands on one filled disc (~3750k), a 4h movie on two, a 30min clip
on one near the 9000k cap.
- apply_high_profile_bitrate() applies that rate to every title and forces
2-pass; it runs before split planning so the existing splitter reuses it.
Split discs now get a per-disc menu (listing that disc's titles) when the
project has a menu enabled, honoring the startup/play-all settings; they
still auto-play when menus are off.
Fix: _adjust_audio_bitrate capped its table at 384 (an MP2 limit) and
silently clamped the High profile's 448k AC3 down to 384; extend the table
to AC3 rates (448/512/640) in both the ffmpeg and avconv backends.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the fork's architecture and dev/verify workflow for future work
(no test framework; verify via pure-logic functions and generated
command_var). Extend .gitignore to exclude .claude/ local config
(machine-specific paths + personal permission allowlists), common secret
file patterns, and editor/OS junk, while keeping the shipped base_*.mpg
package data tracked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Driving -progress over pipe:1 made ffmpeg block on the progress write
whenever the GUI stopped draining the pipe (e.g. the parent terminal or
the app's event loop wedging), deadlocking every running encode.
Write -progress to a temp file instead: a file write never blocks, so the
encoder runs to completion regardless of GUI state. The executor calls
optional start_/stop_progress_polling hooks; ffmpeg polls the file on a
500ms GLib timer and removes it when done. Verified an encode completes
with no reader at all (previously a hard deadlock).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The progress bar barely moved and the window showed "not responding"
during long encodes: ffmpeg's carriage-return stats line flushes rarely
over a pipe, and the transcode saturated the CPU.
- Drive progress from ffmpeg's structured `-progress pipe:1` output
(`-stats_period 0.5 -nostats`) and parse out_time/progress in
process_stdout, giving steady frequent updates (verified ~2/sec via the
live executor pipe vs ~1 per whole encode before).
- Run the encoder under `nice -n 10` so the compositor keeps a CPU slice
(avconv gets the nice prefix too).
Also round pad/crop targets to even dimensions and use positive crop
offsets, fixing an odd-height (e.g. 1079) MPEG-2 error surfaced when
burning in subtitles on a 1080p source.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Using every core for a parallel batch starves the compositor and makes
the GUI show "not responding". Default multicore to cores-1 so the
desktop keeps headroom; the user can still raise it in Preferences.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add FORK_NOTES.md describing the new features, a README pointer, and keep
the known-good baseline encoder as contrib/dvd-safe-converter.sh (the
reference the Compatibility profile is modelled on).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a DVD project does not fit one disc at the chosen quality, offer to
split it into several full-quality discs instead of only degrading the
bitrate:
- A pure, unit-tested planner distributes whole titles across discs and
time-splits a single over-long movie at the chapter boundary nearest each
disc's capacity (even-time fallback when it has no chapters).
- on_create_disc_clicked asks split / reduce-bitrate / cancel, then authors
each disc into its own subfolder and emits name-discN.iso per disc.
- dvdauthor_converter takes per-title segment lengths so a split segment's
chapter markers stay inside the segment.
Also adds the project-level encode-profile selector (with custom-bitrate
fields) next to the disc-size control.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce three project-level DVD encode profiles (Compatibility 4500k /
High quality VBR / Custom) via new config keys, and burn a chosen
embedded subtitle track into the picture:
- file_movie carries the hardsub selection (index/kind), persists it, and
builds a subtitle-picker combobox in its properties page; set_final_rates
maps the profile to the final video/audio bitrate.
- ffmpeg burns text subs with the subtitles= filter after scaling and image
subs with an overlay filtergraph before scaling; NTSC DVD audio is now AC3
for all profiles. A start_offset (-ss) is threaded through for segmenting.
- avconv gets the text-sub path and start_offset for parity.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a subtitle branch to the ffprobe/avprobe stream loops that collects
each embedded subtitle track (index, codec, language, title, default/
forced flags) and classifies it as text or image by codec. This is the
basis for letting the user pick a track to burn into the picture.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When opening an old devede project, if the background picture or
music files aren't there, an exception would happen. This can
happen even with the defaults, because in the last version, they
were migrated into a different folder.
Now, if the files aren't found, the new defaults will be used.