#!/usr/bin/env bash
# 经 SSH 别名转发远程摄像头，本机落盘录制
#
# 用法:
#   ./record_cam.sh <ssh别名> <摄像头URL> [时长秒]
#
# 目录:
#   resources/video/<别名>/<IP末三位>/<日月年-秒分时>.mp4
# 例:
#   ./record_cam.sh 64ee 'rtsp://admin:pass@192.168.1.202/Streaming/Channels/101' 10
#   → resources/video/64ee/202/29072026-453015.mp4
#
# 流程:
#   1) 解析 URL 中的摄像头 host:port
#   2) ssh -L 将本机临时端口转到 别名主机可达的摄像头端口
#   3) 本机 ffmpeg 对 127.0.0.1 拉流并落盘
#   4) 退出时自动关隧道
#
# 时长可选；不传则一直录，Ctrl+C 结束。

set -euo pipefail

usage() {
  cat <<'EOF'
用法: record_cam.sh <ssh别名> <摄像头URL> [时长秒]

  ssh别名    ~/.ssh/config 中的 Host 别名（如 64ee / 117）
  摄像头URL  远程侧可达的 RTSP/HTTP 地址（host 用内网 IP）
  时长秒     可选；省略则持续录制直到 Ctrl+C

输出:
  <脚本目录>/<别名>/<IP末三位>/<日月年-秒分时>.mp4
  时间格式: DDMMYYYY-SSMMHH（日日月月年年年年-秒秒分分时时）

依赖: 本机 ffmpeg、ssh；别名密钥已配好，BatchMode 免密。
EOF
  exit 1
}

[[ $# -ge 2 && $# -le 3 ]] || usage

ALIAS="$1"
CAM_URL="$2"
DURATION="${3:-}"

if [[ -z "$ALIAS" || -z "$CAM_URL" ]]; then
  usage
fi

if ! command -v ffmpeg >/dev/null 2>&1; then
  echo "[错误] 未找到 ffmpeg，请先安装" >&2
  exit 1
fi

if ! command -v ssh >/dev/null 2>&1; then
  echo "[错误] 未找到 ssh" >&2
  exit 1
fi

if [[ -n "$DURATION" ]] && ! [[ "$DURATION" =~ ^[0-9]+$ ]]; then
  echo "[错误] 时长须为正整数秒: $DURATION" >&2
  exit 1
fi

# 解析 URL → scheme / userinfo@ / host / port / path+query
parse_url() {
  local url="$1"
  local rest hostport

  if [[ "$url" =~ ^([a-zA-Z][a-zA-Z0-9+.-]*)://(.*)$ ]]; then
    URL_SCHEME="${BASH_REMATCH[1]}"
    rest="${BASH_REMATCH[2]}"
  else
    echo "[错误] URL 缺少协议: $url" >&2
    exit 1
  fi

  URL_USERINFO=""
  if [[ "$rest" == *@* ]]; then
    URL_USERINFO="${rest%%@*}"
    rest="${rest#*@}"
  fi

  hostport="${rest%%/*}"
  if [[ "$rest" == */* ]]; then
    URL_PATH="/${rest#*/}"
  else
    URL_PATH=""
  fi

  if [[ "$hostport" == \[* ]]; then
    echo "[错误] 暂不支持 IPv6 地址" >&2
    exit 1
  fi

  if [[ "$hostport" == *:* ]]; then
    URL_HOST="${hostport%%:*}"
    URL_PORT="${hostport##*:}"
  else
    URL_HOST="$hostport"
    case "${URL_SCHEME,,}" in
      rtsp) URL_PORT=554 ;;
      https) URL_PORT=443 ;;
      http) URL_PORT=80 ;;
      *) URL_PORT=554 ;;
    esac
  fi

  if [[ -z "$URL_HOST" || -z "$URL_PORT" ]]; then
    echo "[错误] 无法解析 host/port: $url" >&2
    exit 1
  fi
  if ! [[ "$URL_PORT" =~ ^[0-9]+$ ]]; then
    echo "[错误] 非法端口: $URL_PORT" >&2
    exit 1
  fi
}

# IP 末三段固定三位；非 IPv4 取 host 末 3 字符
extract_cam_dir() {
  local host="$1"
  if [[ "$host" =~ ^([0-9]{1,3}\.){3}([0-9]{1,3})$ ]]; then
    printf '%03d\n' "${BASH_REMATCH[2]}"
    return
  fi
  local compact="${host//./}"
  if [[ ${#compact} -ge 3 ]]; then
    echo "${compact: -3}"
  elif [[ -n "$compact" ]]; then
    echo "$compact"
  else
    echo "unknown"
  fi
}

# 找本机空闲端口
pick_local_port() {
  local p
  for p in $(seq 18554 18654); do
    if ! ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)${p}$"; then
      if ! (echo >/dev/tcp/127.0.0.1/"$p") 2>/dev/null; then
        echo "$p"
        return 0
      fi
    fi
  done
  echo "[错误] 18554-18654 无可用本地端口" >&2
  exit 1
}

build_local_url() {
  local local_port="$1"
  local auth=""
  if [[ -n "$URL_USERINFO" ]]; then
    auth="${URL_USERINFO}@"
  fi
  echo "${URL_SCHEME}://${auth}127.0.0.1:${local_port}${URL_PATH}"
}

parse_url "$CAM_URL"
CAM_DIR="$(extract_cam_dir "$URL_HOST")"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUT_DIR="${SCRIPT_DIR}/${ALIAS}/${CAM_DIR}"
mkdir -p "$OUT_DIR"

# 日月年-秒分时 → DDMMYYYY-SSMMHH
STAMP="$(date +'%d%m%Y-%S%M%H')"
OUT_FILE="${OUT_DIR}/${STAMP}.mp4"

LOCAL_PORT="$(pick_local_port)"
LOCAL_URL="$(build_local_url "$LOCAL_PORT")"

TUNNEL_PID=""
cleanup() {
  if [[ -n "${TUNNEL_PID}" ]] && kill -0 "$TUNNEL_PID" 2>/dev/null; then
    kill "$TUNNEL_PID" 2>/dev/null || true
    wait "$TUNNEL_PID" 2>/dev/null || true
  fi
}
trap cleanup EXIT INT TERM

echo "别名:     $ALIAS"
echo "摄像头:   $CAM_URL"
echo "转发:     127.0.0.1:${LOCAL_PORT} → ${URL_HOST}:${URL_PORT} via ${ALIAS}"
echo "本机URL:  $LOCAL_URL"
echo "目录:     $OUT_DIR"
echo "文件:     $OUT_FILE"
if [[ -n "$DURATION" ]]; then
  echo "时长:     ${DURATION}s"
else
  echo "时长:     持续（Ctrl+C 结束）"
fi
echo

# 探活 SSH 别名
if ! ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new \
  "$ALIAS" 'true' 2>/dev/null; then
  echo "[错误] 无法 BatchMode 连接 ssh 别名: $ALIAS（检查 ~/.ssh/config 与密钥）" >&2
  exit 1
fi

# 后台本地转发（相机对别名主机可达，对本机不可达）
ssh -fN \
  -o ExitOnForwardFailure=yes \
  -o BatchMode=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -L "127.0.0.1:${LOCAL_PORT}:${URL_HOST}:${URL_PORT}" \
  "$ALIAS"

# 找刚起的 ssh 转发进程
TUNNEL_PID="$(
  ss -ltnp 2>/dev/null | grep -E "[:.]${LOCAL_PORT}[[:space:]]" | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | head -1
)"
if [[ -z "$TUNNEL_PID" ]]; then
  TUNNEL_PID="$(pgrep -f "ssh -fN .*${LOCAL_PORT}:${URL_HOST}:${URL_PORT} ${ALIAS}" | head -1 || true)"
fi
if [[ -z "$TUNNEL_PID" ]]; then
  echo "[错误] 隧道已请求但未找到监听进程 :${LOCAL_PORT}" >&2
  exit 1
fi

# 等端口就绪
for _ in $(seq 1 20); do
  if (echo >/dev/tcp/127.0.0.1/"$LOCAL_PORT") 2>/dev/null; then
    break
  fi
  sleep 0.1
done

FFMPEG_ARGS=(
  -hide_banner
  -loglevel info
  -y
)

# RTSP 强制 TCP（经 SSH 隧道时更稳）
if [[ "${URL_SCHEME,,}" == "rtsp" ]]; then
  FFMPEG_ARGS+=(-rtsp_transport tcp)
fi

FFMPEG_ARGS+=(-i "$LOCAL_URL")

# 只拷视频：海康常见 pcm_alaw 不能直接进 mp4
FFMPEG_ARGS+=(
  -map 0:v:0
  -c:v copy
  -an
  -movflags +faststart
)

if [[ -n "$DURATION" ]]; then
  FFMPEG_ARGS+=(-t "$DURATION")
fi

FFMPEG_ARGS+=("$OUT_FILE")

ffmpeg "${FFMPEG_ARGS[@]}"
echo
echo "已保存: $OUT_FILE"
