feat: handle keyboard interruption more gracefully
This commit is contained in:
10
ripper.py
10
ripper.py
@@ -3,17 +3,21 @@
|
||||
DVD/Blu-ray ripper using HandBrakeCLI with scene-style naming.
|
||||
|
||||
Scans a disc, selects the best audio & subtitle track per language
|
||||
(passthrough), encodes with H.265 10-bit AMD VCE (falling back to
|
||||
x265_10bit on CPU), and generates an NFO file via pymediainfo.
|
||||
(passthrough), encodes with H.265/H.264/AV1 (auto-selecting HW
|
||||
acceleration), and generates an NFO file via pymediainfo.
|
||||
|
||||
Usage:
|
||||
python ripper.py --imdb tt6977338
|
||||
python ripper.py --name 'Game Night' --year 2018
|
||||
python ripper.py --codec av1 --quality 30
|
||||
python ripper.py --scan-only
|
||||
python ripper.py --list
|
||||
"""
|
||||
|
||||
import sys
|
||||
from ripper.cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(130)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
"""Allow running as `python -m ripper`."""
|
||||
|
||||
import sys
|
||||
|
||||
from .cli import main
|
||||
|
||||
main()
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(130)
|
||||
|
||||
@@ -136,6 +136,15 @@ def main():
|
||||
)
|
||||
console.print()
|
||||
|
||||
try:
|
||||
_run_pipeline(args, quality)
|
||||
except KeyboardInterrupt:
|
||||
console.print("\n [bold yellow]⚠ Interrupted[/] — exiting.\n")
|
||||
sys.exit(130)
|
||||
|
||||
|
||||
def _run_pipeline(args, quality: int) -> None:
|
||||
"""Core ripping pipeline, separated for clean interrupt handling."""
|
||||
# ── 1. Detect encoder ────────────────────────────────────────────────
|
||||
available = discover_encoders()
|
||||
print_encoder_table(available)
|
||||
@@ -183,7 +192,6 @@ def main():
|
||||
|
||||
# Determine codec tag and bit depth from the selected encoder
|
||||
codec_tag = CODEC_SCENE_TAG.get(args.codec, args.codec.upper())
|
||||
# Check if we're using a 10-bit encoder
|
||||
is_10bit = "10bit" in encoder or "10" in encoder
|
||||
|
||||
scene = build_scene_name(
|
||||
@@ -220,6 +228,9 @@ def main():
|
||||
)
|
||||
|
||||
returncode = run_encode(cmd, input_path=args.input)
|
||||
if returncode == 130:
|
||||
# User cancelled — re-raise so outer handler prints message
|
||||
raise KeyboardInterrupt
|
||||
if returncode != 0:
|
||||
console.print(f"\n[bold red]ERROR:[/] HandBrakeCLI exited with code {returncode}")
|
||||
sys.exit(returncode)
|
||||
|
||||
@@ -116,6 +116,7 @@ def run_encode(cmd: list[str], input_path: str | None = None) -> int:
|
||||
last_progress_time = time.monotonic()
|
||||
stall_warned = False
|
||||
|
||||
try:
|
||||
with progress:
|
||||
for line in process.stdout:
|
||||
line = line.rstrip()
|
||||
@@ -182,6 +183,20 @@ def run_encode(cmd: list[str], input_path: str | None = None) -> int:
|
||||
# Ensure we reach 100%
|
||||
progress.update(task_id, completed=100, phase="Complete")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Gracefully stop HandBrakeCLI on Ctrl+C
|
||||
progress.update(task_id, phase="[bold red]Cancelled[/]", fps="")
|
||||
progress.stop()
|
||||
console.print("\n [bold yellow]⚠ Interrupted[/] — stopping HandBrakeCLI…")
|
||||
process.terminate()
|
||||
try:
|
||||
process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
process.wait()
|
||||
console.print(" [dim]HandBrakeCLI stopped.[/]")
|
||||
return 130 # Standard exit code for SIGINT
|
||||
|
||||
process.wait()
|
||||
return process.returncode
|
||||
|
||||
|
||||
Reference in New Issue
Block a user