transcode.py: handle Ctrl+C better
This commit is contained in:
+39
-10
@@ -12,6 +12,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
@@ -200,7 +201,6 @@ def get_ffmpeg_command(input_path, output_path):
|
||||
|
||||
|
||||
def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=False):
|
||||
exit_after_next = False
|
||||
input_path = Path(input_file)
|
||||
|
||||
logging.debug(f"Processing request for: {input_path}")
|
||||
@@ -285,6 +285,8 @@ def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=Fal
|
||||
cmd = get_ffmpeg_command(input_path, transcode_output_path)
|
||||
logging.debug(f"Executing FFmpeg command: {' '.join(cmd)}")
|
||||
|
||||
graceful_exit = False
|
||||
|
||||
try:
|
||||
with open(ffmpeg_log_file, "w", encoding="utf-8", errors="replace") as f_log:
|
||||
# Write the command itself to the top of the log
|
||||
@@ -292,12 +294,15 @@ def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=Fal
|
||||
f_log.flush()
|
||||
|
||||
# Run FFmpeg and process output in real-time with timestamps
|
||||
# NOTE: start_new_session=True ensures Ctrl+C doesn't kill ffmpeg immediately
|
||||
# allowing us to handle the first press gracefully in Python.
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
start_new_session=True,
|
||||
)
|
||||
|
||||
regex_elapsed = re.compile(r"elapsed=([0-9:.]+)")
|
||||
@@ -305,6 +310,7 @@ def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=Fal
|
||||
# Write FFmpeg output with timestamps as it's generated
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
output = process.stdout.readline() # pyright: ignore[reportOptionalMemberAccess]
|
||||
if output == "" and process.poll() is not None:
|
||||
break
|
||||
@@ -318,11 +324,30 @@ def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=Fal
|
||||
)
|
||||
f_log.write(f"[{timestamp}] {output.rstrip()}\n")
|
||||
f_log.flush()
|
||||
except KeyboardInterrupt:
|
||||
if not graceful_exit:
|
||||
logging.info(
|
||||
" > [GRACEFUL EXIT] Signal received. Finishing current file, then exiting script. Press Ctrl+C again to FORCE QUIT."
|
||||
)
|
||||
graceful_exit = True
|
||||
# Continue loop, process is still running because of start_new_session=True
|
||||
continue
|
||||
else:
|
||||
logging.warning(
|
||||
" >> [FORCE QUIT] Signal received again. Terminating process..."
|
||||
)
|
||||
# Explicitly terminate the process group or process
|
||||
process.terminate()
|
||||
try:
|
||||
process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
raise # Re-raise to trigger outer cleanup
|
||||
|
||||
# Wait for process to complete and get return code
|
||||
result = process.wait()
|
||||
except Exception as e:
|
||||
# Ensure we clean up temporary files on error
|
||||
# Ensure we clean up temporary files on error (e.g. if we forced kill inside loop)
|
||||
if replace_mode and use_temp_file and transcode_output_path.exists():
|
||||
try:
|
||||
transcode_output_path.unlink()
|
||||
@@ -410,19 +435,23 @@ def transcode_file(input_file, output_file=None, skip_av1=True, replace_mode=Fal
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if exit_after_next:
|
||||
logging.info("Quitting early due to user interrupt.")
|
||||
# If a graceful exit was requested, we exit now that the file is fully processed
|
||||
if graceful_exit:
|
||||
logging.info("Exiting script gracefully as requested.")
|
||||
sys.exit(0)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(f"Unexpected error during transcoding of {input_path}: {e}")
|
||||
except KeyboardInterrupt:
|
||||
logging.info(
|
||||
"Will quit after the current file is transcoded. Press Ctrl+C again to force quit."
|
||||
)
|
||||
if exit_after_next:
|
||||
sys.exit(0)
|
||||
exit_after_next = True
|
||||
# This catches the re-raised exception from the inner loop (2nd Ctrl+C / Force Quit)
|
||||
logging.info("Transcoding aborted by user.")
|
||||
# Ensure cleanup happened (redundant check but safe)
|
||||
if replace_mode and use_temp_file and transcode_output_path.exists():
|
||||
try:
|
||||
transcode_output_path.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class NewFileHandler(FileSystemEventHandler):
|
||||
|
||||
Reference in New Issue
Block a user