#!/usr/bin/env bash
# rip-dvd-to-video.sh
# Convert an unencrypted DVD/video source into a NAS-ready MP4 or MKV file.
#
# Legal/safety note:
#   This script is for DVDs you own where copying is legally permitted, and for
#   unencrypted discs or already-decrypted VIDEO_TS folders. It does not bypass,
#   disable, or provide instructions for defeating DVD DRM/encryption/Macrovision.

set -Eeuo pipefail

SCRIPT_NAME="$(basename "$0")"
DEFAULT_SOURCE="/dev/dvd"
DEFAULT_OUTDIR="$HOME/Videos/DVD-Rips"
DEFAULT_PRESET="Fast 1080p30"
DEFAULT_FORMAT="mp4"

usage() {
  cat <<'USAGE'
Usage:
  rip-dvd-to-video.sh --title-name "Movie Name" [options]

Examples:
  # Scan the DVD first and show available titles
  rip-dvd-to-video.sh --scan --source /dev/sr0

  # Rip the longest/main title from an unencrypted DVD to local output
  rip-dvd-to-video.sh --title-name "My Bought DVD" --source /dev/sr0

  # Use a specific DVD title number and copy result to a mounted NAS folder
  rip-dvd-to-video.sh --title-name "My Bought DVD" --title 1 --nas-dir /mnt/media_primary/Movies

  # Work from an already-decrypted VIDEO_TS folder
  rip-dvd-to-video.sh --title-name "My Bought DVD" --source /path/to/VIDEO_TS --format mkv

Options:
  --title-name NAME       Required unless --scan. Human readable output name.
  --source PATH           DVD device or VIDEO_TS/DVD folder. Default: /dev/dvd
  --out-dir DIR           Local staging output directory. Default: ~/Videos/DVD-Rips
  --nas-dir DIR           Optional mounted NAS directory to copy the finished video into.
  --title N               HandBrake title number. Default: 0 = automatic/main feature.
  --preset PRESET         HandBrake preset. Default: "Fast 1080p30"
  --format mp4|mkv        Output container. Default: mp4
  --quality RF            HandBrake constant quality RF. Default: 20
  --audio-lang CODES      Preferred audio languages. Default: eng,und
  --subtitle-lang CODES   Preferred subtitle languages. Default: eng
  --dry-run               Print what would run without transcoding/copying.
  --scan                  Scan source titles only; no output file is made.
  -h, --help              Show this help.

Notes:
  - Install dependencies on Debian/Zorin/Ubuntu with:
      sudo apt update && sudo apt install -y handbrake-cli ffmpeg rsync eject
  - If the DVD cannot be read because of encryption/DRM, this script stops and
    reports that clearly. It intentionally does not include DRM circumvention.
USAGE
}

log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >&2; }
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }

safe_name() {
  # Lower-risk filesystem name: keeps letters/numbers/dot/underscore/hyphen/space.
  printf '%s' "$1" \
    | tr '/:' '--' \
    | tr -cd '[:alnum:]. _()-' \
    | sed -E 's/[[:space:]]+/_/g; s/^_+|_+$//g; s/_+/_/g'
}

SOURCE="$DEFAULT_SOURCE"
OUT_DIR="$DEFAULT_OUTDIR"
NAS_DIR=""
TITLE_NAME=""
TITLE="0"
PRESET="$DEFAULT_PRESET"
FORMAT="$DEFAULT_FORMAT"
QUALITY="20"
AUDIO_LANG="eng,und"
SUBTITLE_LANG="eng"
DRY_RUN=0
SCAN_ONLY=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --title-name) TITLE_NAME="${2:-}"; shift 2 ;;
    --source) SOURCE="${2:-}"; shift 2 ;;
    --out-dir) OUT_DIR="${2:-}"; shift 2 ;;
    --nas-dir) NAS_DIR="${2:-}"; shift 2 ;;
    --title) TITLE="${2:-}"; shift 2 ;;
    --preset) PRESET="${2:-}"; shift 2 ;;
    --format) FORMAT="${2:-}"; shift 2 ;;
    --quality) QUALITY="${2:-}"; shift 2 ;;
    --audio-lang) AUDIO_LANG="${2:-}"; shift 2 ;;
    --subtitle-lang) SUBTITLE_LANG="${2:-}"; shift 2 ;;
    --dry-run) DRY_RUN=1; shift ;;
    --scan) SCAN_ONLY=1; shift ;;
    -h|--help) usage; exit 0 ;;
    *) fail "Unknown argument: $1. Use --help." ;;
  esac
done

[[ "$FORMAT" == "mp4" || "$FORMAT" == "mkv" ]] || fail "--format must be mp4 or mkv"
[[ "$TITLE" =~ ^[0-9]+$ ]] || fail "--title must be a number; use 0 for automatic/main feature"
[[ "$QUALITY" =~ ^[0-9]+$ ]] || fail "--quality must be a whole number RF value, e.g. 18, 20, 22"

have HandBrakeCLI || fail "HandBrakeCLI is missing. Install with: sudo apt update && sudo apt install -y handbrake-cli ffmpeg rsync eject"
have ffmpeg || fail "ffmpeg is missing. Install with: sudo apt update && sudo apt install -y ffmpeg"

if [[ ! -e "$SOURCE" ]]; then
  fail "Source does not exist: $SOURCE. Try /dev/sr0, /dev/dvd, or a VIDEO_TS folder path."
fi

if [[ "$SCAN_ONLY" -eq 1 ]]; then
  log "Scanning DVD/video source: $SOURCE"
  set +e
  HandBrakeCLI --input "$SOURCE" --title 0 --scan
  rc=$?
  set -e
  if [[ $rc -ne 0 ]]; then
    fail "Scan failed. If this is an encrypted/DRM-protected disc, this script cannot process it."
  fi
  exit 0
fi

[[ -n "$TITLE_NAME" ]] || fail "--title-name is required unless using --scan"

mkdir -p "$OUT_DIR"
SAFE_TITLE="$(safe_name "$TITLE_NAME")"
DATE_TAG="$(date '+%Y%m%d-%H%M%S')"
OUTPUT_FILE="$OUT_DIR/${SAFE_TITLE}_${DATE_TAG}.${FORMAT}"
LOG_FILE="$OUT_DIR/${SAFE_TITLE}_${DATE_TAG}.log"
META_FILE="$OUT_DIR/${SAFE_TITLE}_${DATE_TAG}.txt"
PARTIAL_FILE="${OUTPUT_FILE}.partial"

HB_ARGS=(
  --input "$SOURCE"
  --output "$PARTIAL_FILE"
  --title "$TITLE"
  --preset "$PRESET"
  --quality "$QUALITY"
  --format "$FORMAT"
  --markers
  --optimize
  --audio-lang-list "$AUDIO_LANG"
  --all-audio
  --subtitle-lang-list "$SUBTITLE_LANG"
  --all-subtitles
)

SCAN_LOG="$OUT_DIR/${SAFE_TITLE}_${DATE_TAG}.preflight-scan.log"

cat > "$META_FILE" <<META
Title: $TITLE_NAME
Source: $SOURCE
Output: $OUTPUT_FILE
NAS destination: ${NAS_DIR:-none}
HandBrake preset: $PRESET
Title number: $TITLE
Format: $FORMAT
Quality RF: $QUALITY
Audio languages: $AUDIO_LANG
Subtitle languages: $SUBTITLE_LANG
Started: $(date -Is)
Legal/safety note: unencrypted/legally permitted source only; no DRM circumvention performed.
META

log "Output will be: $OUTPUT_FILE"
log "Preflight scan log: $SCAN_LOG"
log "HandBrake transcode log: $LOG_FILE"

if [[ "$DRY_RUN" -eq 1 ]]; then
  printf 'Would preflight scan:\n  '
  printf '%q ' HandBrakeCLI --input "$SOURCE" --title 0 --scan
  printf '\nWould run:\n  '
  printf '%q ' HandBrakeCLI "${HB_ARGS[@]}"
  printf '\n'
  [[ -n "$NAS_DIR" ]] && printf 'Would copy to NAS dir: %q\n' "$NAS_DIR"
  exit 0
fi

log "Running preflight readability scan before ripping."
set +e
HandBrakeCLI --input "$SOURCE" --title 0 --scan > "$SCAN_LOG" 2>&1
scan_rc=$?
set -e
classify_preflight_failure() {
  local log_file="$1"
  if grep -Eiq 'region|wrong region|region code|RCE|not.*region' "$log_file"; then
    printf 'REGION_BLOCKED'
  elif grep -Eiq 'css|encrypted|scrambled|copy protect|copy-protect|drm|libdvdcss|Content Scramble|Macrovision' "$log_file"; then
    printf 'DRM_PROTECTED'
  elif grep -Eiq 'read error|I/O error|input/output error|cannot read|dvdnav.*error|libdvdread.*error|bad sector|medium error' "$log_file"; then
    printf 'READ_ERROR_OR_DAMAGED_DISC'
  elif grep -Eiq 'no title found|no valid title|not a DVD|invalid' "$log_file"; then
    printf 'NO_VALID_DVD_TITLE'
  else
    printf 'UNKNOWN_READABILITY_FAILURE'
  fi
}

if [[ $scan_rc -ne 0 ]]; then
  reason="$(classify_preflight_failure "$SCAN_LOG")"
  case "$reason" in
    REGION_BLOCKED)
      fail "Preflight failed: DVD appears REGION BLOCKED. No rip was started. See: $SCAN_LOG" ;;
    DRM_PROTECTED)
      fail "Preflight failed: DVD appears DRM/COPY-PROTECTED. No rip was started. This script cannot bypass DRM. See: $SCAN_LOG" ;;
    READ_ERROR_OR_DAMAGED_DISC)
      fail "Preflight failed: disc appears unreadable or damaged, or the drive reported read errors. No rip was started. See: $SCAN_LOG" ;;
    NO_VALID_DVD_TITLE)
      fail "Preflight failed: no valid DVD title was found at the selected source. Check the drive/path. No rip was started. See: $SCAN_LOG" ;;
    *)
      fail "Preflight failed: HandBrake could not read the source. No rip was started. See: $SCAN_LOG" ;;
  esac
fi
reason="$(classify_preflight_failure "$SCAN_LOG")"
case "$reason" in
  REGION_BLOCKED)
    fail "Preflight warning: DVD appears REGION BLOCKED. No rip was started. See: $SCAN_LOG" ;;
  DRM_PROTECTED)
    fail "Preflight warning: DVD appears DRM/COPY-PROTECTED. No rip was started. This script cannot bypass DRM. See: $SCAN_LOG" ;;
  READ_ERROR_OR_DAMAGED_DISC)
    fail "Preflight warning: possible damaged/unreadable disc or drive read errors. No rip was started. See: $SCAN_LOG" ;;
  NO_VALID_DVD_TITLE)
    fail "Preflight warning: no valid DVD title found. No rip was started. Check the source path. See: $SCAN_LOG" ;;
esac
log "Preflight scan passed; starting transcode. This can take a while."
set +e
HandBrakeCLI "${HB_ARGS[@]}" 2>&1 | tee "$LOG_FILE"
hb_rc=${PIPESTATUS[0]}
set -e

if [[ $hb_rc -ne 0 ]]; then
  rm -f "$PARTIAL_FILE"
  fail "HandBrake failed. If the log mentions read errors, CSS, region, or DRM/encryption, this script cannot process that disc. See: $LOG_FILE"
fi

[[ -s "$PARTIAL_FILE" ]] || fail "Transcode produced no output file: $PARTIAL_FILE"
mv -f "$PARTIAL_FILE" "$OUTPUT_FILE"

log "Verifying output with ffmpeg."
ffmpeg -v error -i "$OUTPUT_FILE" -f null - < /dev/null || fail "ffmpeg verification failed for $OUTPUT_FILE"

DURATION="$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$OUTPUT_FILE" 2>/dev/null || true)"
SIZE_BYTES="$(stat -c '%s' "$OUTPUT_FILE")"
{
  printf 'Finished: %s\n' "$(date -Is)"
  printf 'Output bytes: %s\n' "$SIZE_BYTES"
  [[ -n "$DURATION" ]] && printf 'Duration seconds: %s\n' "$DURATION"
} >> "$META_FILE"

if [[ -n "$NAS_DIR" ]]; then
  [[ -d "$NAS_DIR" ]] || fail "NAS destination is not a mounted directory: $NAS_DIR"
  have rsync || fail "rsync is missing. Install with: sudo apt update && sudo apt install -y rsync"
  log "Copying output and metadata to NAS: $NAS_DIR"
  rsync -ah --info=progress2 "$OUTPUT_FILE" "$META_FILE" "$LOG_FILE" "$NAS_DIR"/
  log "NAS copy complete."
fi

if have eject && [[ -b "$SOURCE" ]]; then
  log "Ejecting disc: $SOURCE"
  eject "$SOURCE" || log "Could not eject $SOURCE; leaving disc mounted/inserted."
fi

log "Done: $OUTPUT_FILE"
printf '%s\n' "$OUTPUT_FILE"
