refactor: split into multiple files
This commit is contained in:
165
ripper/cli.py
Normal file
165
ripper/cli.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""CLI entry point and main orchestration logic."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from rich.panel import Panel
|
||||
|
||||
from .config import INPUT_PATH, OUTPUT_BASE, console
|
||||
from .scanner import detect_encoder, scan_disc, select_title
|
||||
from .tracks import best_tracks_per_language, print_track_tables
|
||||
from .naming import build_scene_name, get_source_tag, get_volume_label
|
||||
from .encode import build_handbrake_cmd, run_encode
|
||||
from .metadata import lookup_imdb, generate_nfo
|
||||
from .library import list_library
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Rip DVD/Blu-ray to MKV using HandBrakeCLI (H.265 10-bit).",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=(
|
||||
"Examples:\n"
|
||||
" %(prog)s --imdb tt6977338\n"
|
||||
" %(prog)s --name 'Game Night' --year 2018\n"
|
||||
" %(prog)s --scan-only\n"
|
||||
" %(prog)s --list\n"
|
||||
" %(prog)s --name Inception --year 2010 --input /dev/sr0\n"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name", "-n",
|
||||
help="Movie name (spaces OK, will be dotted). "
|
||||
"If omitted, the disc volume label is used.",
|
||||
)
|
||||
parser.add_argument("--year", "-y", help="Release year for the filename.")
|
||||
parser.add_argument(
|
||||
"--imdb",
|
||||
help="IMDB ID (e.g. tt6977338) — fetches name and year from TMDB. "
|
||||
"Requires TMDB_API_KEY env var.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input", "-i",
|
||||
default=INPUT_PATH,
|
||||
help=f"Input path (default: {INPUT_PATH}).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-base",
|
||||
default=OUTPUT_BASE,
|
||||
help=f"Base output directory (default: {OUTPUT_BASE}).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scan-only",
|
||||
action="store_true",
|
||||
help="Only scan the disc and print track info, don't encode.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list", "-l",
|
||||
action="store_true",
|
||||
help="List all ripped movies in the output directory.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# ── Library listing (no disc needed) ─────────────────────────────────
|
||||
if args.list:
|
||||
list_library(args.output_base)
|
||||
return
|
||||
|
||||
# ── Banner ───────────────────────────────────────────────────────────
|
||||
console.print()
|
||||
console.print(
|
||||
Panel(
|
||||
"[bold white]DVD / Blu-ray Ripper[/]\n"
|
||||
"[dim]H.265 10-bit · HandBrakeCLI · Scene Naming[/]",
|
||||
border_style="cyan",
|
||||
padding=(0, 2),
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
|
||||
# ── 1. Detect encoder ────────────────────────────────────────────────
|
||||
encoder = detect_encoder()
|
||||
|
||||
# ── 2. Scan disc ─────────────────────────────────────────────────────
|
||||
scan = scan_disc(args.input)
|
||||
title = select_title(scan)
|
||||
|
||||
# ── 3. Select tracks ─────────────────────────────────────────────────
|
||||
audio_all = title.get("AudioList", [])
|
||||
subtitle_all = title.get("SubtitleList", [])
|
||||
|
||||
audio_sel = best_tracks_per_language(audio_all, "audio")
|
||||
subtitle_sel = best_tracks_per_language(subtitle_all, "subtitle")
|
||||
|
||||
print_track_tables(audio_sel, subtitle_sel)
|
||||
|
||||
if args.scan_only:
|
||||
console.print("\n [dim](scan-only mode, exiting)[/]\n")
|
||||
return
|
||||
|
||||
# ── 4. Build filename ────────────────────────────────────────────────
|
||||
movie_name = args.name
|
||||
year = args.year
|
||||
|
||||
# IMDB lookup overrides --name and --year
|
||||
if args.imdb:
|
||||
movie_name, imdb_year = lookup_imdb(args.imdb)
|
||||
if not year:
|
||||
year = imdb_year
|
||||
|
||||
if not movie_name:
|
||||
label = get_volume_label(args.input)
|
||||
if label:
|
||||
movie_name = label.replace("_", " ").title()
|
||||
console.print(f" [green]✓[/] Using volume label: [bold]{movie_name}[/]")
|
||||
else:
|
||||
movie_name = console.input(" [cyan]Enter movie name:[/] ").strip()
|
||||
if not movie_name:
|
||||
console.print("[bold red]ERROR:[/] No movie name provided.")
|
||||
sys.exit(1)
|
||||
|
||||
source_tag = get_source_tag(args.input)
|
||||
scene = build_scene_name(movie_name, year, title, audio_sel, source_tag)
|
||||
|
||||
# Create output directory
|
||||
out_dir = os.path.join(args.output_base, scene)
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
output_file = os.path.join(out_dir, f"{scene}.mkv")
|
||||
|
||||
console.print()
|
||||
console.print(
|
||||
Panel(
|
||||
f"[bold]{scene}.mkv[/]\n"
|
||||
f"[dim]{out_dir}[/]",
|
||||
title="[bold cyan]Output[/]",
|
||||
border_style="dim",
|
||||
padding=(0, 2),
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
|
||||
# ── 5. Encode ────────────────────────────────────────────────────────
|
||||
cmd = build_handbrake_cmd(
|
||||
args.input, output_file, title, audio_sel, subtitle_sel, encoder,
|
||||
)
|
||||
|
||||
returncode = run_encode(cmd)
|
||||
if returncode != 0:
|
||||
console.print(f"\n[bold red]ERROR:[/] HandBrakeCLI exited with code {returncode}")
|
||||
sys.exit(returncode)
|
||||
|
||||
console.print(f"\n [green]✓[/] Encode complete")
|
||||
|
||||
# ── 6. Generate NFO ──────────────────────────────────────────────────
|
||||
generate_nfo(output_file)
|
||||
|
||||
console.print()
|
||||
console.print(
|
||||
Panel(
|
||||
f"[green]✓ All done![/]\n[dim]{out_dir}[/]",
|
||||
border_style="green",
|
||||
padding=(0, 2),
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
Reference in New Issue
Block a user