|
234
|
234
|
INTERRUPT_COUNT=$((INTERRUPT_COUNT + 1))
|
|
235
|
235
|
STOP_AFTER_CURRENT=true
|
|
236
|
236
|
|
|
|
237
|
+ # If quiet-mode progress is mid-line, break it before interrupt logs.
|
|
|
238
|
+ if [[ "$PROGRESS_LINE_OPEN" == true ]]; then
|
|
|
239
|
+ printf '\n'
|
|
|
240
|
+ PROGRESS_LINE_OPEN=false
|
|
|
241
|
+ fi
|
|
|
242
|
+
|
|
237
|
243
|
if [[ "$INTERRUPT_COUNT" -eq 1 ]]; then
|
|
238
|
|
- if [[ -n "$CURRENT_FFMPEG_PID" ]]; then
|
|
239
|
|
- log_msg "WARN" "Stop requested. Will stop after current file. Press Ctrl+C again to abort current encode immediately."
|
|
240
|
|
- else
|
|
241
|
|
- log_msg "WARN" "Stop requested. Exiting before next file."
|
|
242
|
|
- fi
|
|
243
|
|
- return
|
|
|
244
|
+ # ffmpeg is in the same process group and already received SIGINT; it will
|
|
|
245
|
+ # stop gracefully on its own. A second Ctrl+C force-kills it if needed.
|
|
|
246
|
+ log_msg "WARN" "Interrupted; stopping after current encode. Ctrl+C again to force-kill."
|
|
|
247
|
+ elif [[ -n "$CURRENT_FFMPEG_PID" ]]; then
|
|
|
248
|
+ log_msg "WARN" "Force-stopping encode (pid=$CURRENT_FFMPEG_PID)."
|
|
|
249
|
+ kill -9 "$CURRENT_FFMPEG_PID" 2>/dev/null || true
|
|
244
|
250
|
fi
|
|
|
251
|
+}
|
|
245
|
252
|
|
|
246
|
|
- if [[ -n "$CURRENT_FFMPEG_PID" ]]; then
|
|
247
|
|
- log_msg "WARN" "Force-stopping current encode (pid=$CURRENT_FFMPEG_PID)."
|
|
248
|
|
- kill -INT "$CURRENT_FFMPEG_PID" 2>/dev/null || true
|
|
|
253
|
+# Drain all pending keystrokes from /dev/tty (non-blocking) and set
|
|
|
254
|
+# STOP_AFTER_CURRENT if any of them is 'q'/'Q'. Self-contained: saves/restores
|
|
|
255
|
+# stty settings so it works regardless of the terminal's current mode.
|
|
|
256
|
+# Uses min=0 time=0 so the terminal's read() returns immediately when the
|
|
|
257
|
+# buffer is empty (bash treats a 0-byte read as EOF and exits the loop).
|
|
|
258
|
+# -t 1 is a safety net for platforms where select() on a VMIN=0 fd does not
|
|
|
259
|
+# report readable-when-empty; in that case we block at most 1s then exit.
|
|
|
260
|
+check_for_quit_key() {
|
|
|
261
|
+ [[ -e /dev/tty ]] || return 0
|
|
|
262
|
+ local key old_stty found=false
|
|
|
263
|
+ old_stty=$(stty -g </dev/tty 2>/dev/null) || return 0
|
|
|
264
|
+ stty -echo -icanon min 0 time 0 </dev/tty 2>/dev/null \
|
|
|
265
|
+ || { stty "$old_stty" </dev/tty 2>/dev/null || true; return 0; }
|
|
|
266
|
+ while IFS= read -r -s -n 1 -t 1 key </dev/tty 2>/dev/null; do
|
|
|
267
|
+ if [[ "$key" == "q" || "$key" == "Q" ]]; then
|
|
|
268
|
+ found=true
|
|
|
269
|
+ break
|
|
|
270
|
+ fi
|
|
|
271
|
+ done
|
|
|
272
|
+ stty "$old_stty" </dev/tty 2>/dev/null || true
|
|
|
273
|
+ if [[ "$found" == true ]]; then
|
|
|
274
|
+ STOP_AFTER_CURRENT=true
|
|
|
275
|
+ log_msg "INFO" "Quit key pressed; stopping before next file"
|
|
249
|
276
|
fi
|
|
250
|
277
|
}
|
|
251
|
278
|
|
|
252
|
279
|
run_ffmpeg_with_signal_guard() {
|
|
253
|
|
- (
|
|
254
|
|
- trap '' INT TERM
|
|
255
|
|
- exec "$@"
|
|
256
|
|
- ) &
|
|
|
280
|
+ # ffmpeg runs in the same process group as the shell so Ctrl+C (SIGINT)
|
|
|
281
|
+ # reaches it directly and triggers its own graceful shutdown. stdin is
|
|
|
282
|
+ # /dev/null so ffmpeg disables keyboard control and avoids SIGTTIN.
|
|
|
283
|
+ # CURRENT_FFMPEG_PID is tracked for the force-kill second-Ctrl+C path.
|
|
|
284
|
+ "$@" </dev/null &
|
|
257
|
285
|
|
|
258
|
286
|
CURRENT_FFMPEG_PID=$!
|
|
259
|
287
|
if wait "$CURRENT_FFMPEG_PID"; then
|
|
1395
|
1423
|
if [[ "$warmup_elapsed_sec" -lt 0 ]]; then
|
|
1396
|
1424
|
warmup_elapsed_sec=0
|
|
1397
|
1425
|
fi
|
|
1398
|
|
- log_msg "INFO" "Staging RAM disk warm-up before first encode: ${warmup_elapsed_sec}s / $(format_seconds "$warmup_elapsed_sec")"
|
|
1399
|
1426
|
fi
|
|
1400
|
1427
|
fi
|
|
1401
|
1428
|
encode_started_at="$(date +%s)"
|
|
1431
|
1458
|
vlog_msg "CHECKPOINT" "encode_end: $input_file (encode_elapsed=${encode_elapsed_sec}s)"
|
|
1432
|
1459
|
|
|
1433
|
1460
|
if [[ "$ffmpeg_rc" -ne 0 ]]; then
|
|
|
1461
|
+ # Ctrl+C reached ffmpeg directly (same process group); it stopped mid-encode.
|
|
|
1462
|
+ # Treat any user-triggered abort as a clean stop — no error counters, no noise.
|
|
|
1463
|
+ if [[ "$STOP_AFTER_CURRENT" == true && "$INTERRUPT_COUNT" -gt 0 ]]; then
|
|
|
1464
|
+ cleanup_transcode_artifacts "$temp_output_file" "$output_file"
|
|
|
1465
|
+ cleanup_repacked_input "$repacked_input_file"
|
|
|
1466
|
+ return 4
|
|
|
1467
|
+ fi
|
|
|
1468
|
+
|
|
1434
|
1469
|
local failure_rc=1
|
|
1435
|
1470
|
if destination_cannot_accept_file "$input_file" "$out_dir" "$ffmpeg_log"; then
|
|
1436
|
1471
|
failure_rc=3
|
|
1616
|
1651
|
|
|
1617
|
1652
|
local f
|
|
1618
|
1653
|
for f in ${VIDEO_FILES+"${VIDEO_FILES[@]}"}; do
|
|
|
1654
|
+ check_for_quit_key
|
|
1619
|
1655
|
if [[ "$STOP_AFTER_CURRENT" == true ]]; then
|
|
1620
|
1656
|
log_msg "INFO" "Stop requested; ending before next file"
|
|
1621
|
1657
|
break
|