#!/bin/bash
#
# monitor.sh - 开播监测脚本
# 只负责“监测”本身，检测使用 streamlink --stream-url（只探测、不下载，不产生视频文件）。
# 检测到开播后，会调用 record.sh 单独拉起一个独立的录制进程（互不影响，remove 不会打断正在进行的录制）。
#
# 用法：
#   添加监测：./monitor.sh add <name>
#   移除监测：./monitor.sh remove <name>
#   查询状态：./monitor.sh list
#
# 说明：
#   - 监测状态下，每 MONITOR_INTERVAL 秒探测一次是否开播（不下载视频，只探测流地址）。
#   - 探测到开播（退出码0）：调用 `python ding.py {name}_on` 通知，然后调用
#     `record.sh start {name} monitored` 单独启动一个独立的录制进程（有自己的 .record.pid，
#     和监测进程完全独立），随后监测进程转入“等待录制结束”状态。
#   - 录制进程内部的断流重连、下播判定（2分钟未恢复）、`{name}_off` 通知，都由 record.sh 自己负责。
#     monitor.sh 只是定期检查 record.sh 的进程是否还活着，一旦发现录制已结束，就自动回到监测状态。
#   - remove 只会终止监测进程本身，不会影响正在进行的录制（如果当时正好在录制的话，
#     用 record.sh stop 单独去停止录制）。
#   - 手动中断（remove / Ctrl+C）不会对文件夹内任何文件做修改。

# 解析脚本自身的绝对路径
SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"

# ================== 以下为需要根据实际情况修改的配置 ==================
BASE_DIR="/home/uftp/stripchat"                 # 主播文件夹的根目录（对应需求里的“xxx文件夹”）
URL_TEMPLATE="https://zh.stripchat.com/%s"       # 直播地址模板，%s 会替换为 name
NOTIFY_SCRIPT="/home/uftp/ding.py"        # ding.py 的绝对路径
PYTHON_BIN="python3"                     # 如果环境里是 python3，改成 python3
RECORD_SCRIPT="/home/uftp/record.sh"      # record.sh 的绝对路径

MONITOR_INTERVAL=300                    # 监测间隔（秒），默认5分钟
RECORD_POLL_INTERVAL=10                 # 录制期间，检查录制进程是否还活着的轮询间隔（秒）

ENABLE_LOG=0                            # 默认不产生日志；排查问题时改成 1，日志会写到每个主播文件夹下的 .monitor.log
# =======================================================================

ACTION="$1"
NAME="$2"

# ---------- list：查询当前正在监测的主播 ----------
if [ "$ACTION" = "list" ]; then
    if [ ! -d "$BASE_DIR" ]; then
        echo "$BASE_DIR 不存在。"
        exit 0
    fi

    FOUND=0
    for pid_file in "$BASE_DIR"/*/.monitor.pid; do
        [ -e "$pid_file" ] || continue
        DIR_NAME=$(basename "$(dirname "$pid_file")")
        PID=$(cat "$pid_file" 2>/dev/null)
        if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
            echo "监测中: $DIR_NAME (PID $PID)"
            FOUND=1
        else
            rm -f "$pid_file"
        fi
    done
    if [ "$FOUND" -eq 0 ]; then
        echo "当前没有正在监测的主播。"
    fi
    exit 0
fi

if [ -z "$NAME" ]; then
    echo "用法: $0 {add|remove} <name> | $0 list"
    exit 1
fi

REC_DIR="${BASE_DIR}/${NAME}"
MONITOR_PID_FILE="${REC_DIR}/.monitor.pid"
RECORD_PID_FILE="${REC_DIR}/.record.pid"

# ---------- remove ----------
if [ "$ACTION" = "remove" ]; then
    if [ -f "$MONITOR_PID_FILE" ]; then
        MPID=$(cat "$MONITOR_PID_FILE")
        if [ -n "$MPID" ] && kill -0 "$MPID" 2>/dev/null; then
            kill -TERM "$MPID"
            echo "已停止对 $NAME 的监测 (PID $MPID)，不影响正在进行的录制。"
        else
            echo "未找到运行中的监测进程（可能已经停止）。"
            rm -f "$MONITOR_PID_FILE"
        fi
    else
        echo "未找到监测 PID 文件，当前可能没有在监测 $NAME。"
    fi
    exit 0
fi

if [ "$ACTION" != "add" ]; then
    echo "用法: $0 {add|remove} <name> | $0 list"
    exit 1
fi

mkdir -p "$REC_DIR"

# 已在监测则不重复启动
if [ -f "$MONITOR_PID_FILE" ]; then
    OLD_MPID=$(cat "$MONITOR_PID_FILE")
    if [ -n "$OLD_MPID" ] && kill -0 "$OLD_MPID" 2>/dev/null; then
        echo "已经在监测 $NAME (PID $OLD_MPID)，未重复启动。"
        exit 0
    fi
fi

# ---------- 第一次调用：fork 到后台，之后立即返回 ----------
if [ "$3" != "__monitor_daemon__" ]; then
    nohup setsid "$SCRIPT_PATH" add "$NAME" __monitor_daemon__ >/dev/null 2>&1 &
    disown

    sleep 1
    if [ -f "$MONITOR_PID_FILE" ] && kill -0 "$(cat "$MONITOR_PID_FILE")" 2>/dev/null; then
        echo "监测已在后台启动 (name=$NAME)"
    else
        echo "警告：监测后台进程似乎没有成功启动，请检查 SCRIPT_PATH=$SCRIPT_PATH 是否可执行，" \
             "或临时把脚本里 ENABLE_LOG 改成 1 再试一次并查看 ${REC_DIR}/.monitor.log"
    fi
    exit 0
fi

# ================== 以下为监测守护进程实际执行的逻辑 ==================
MONITOR_LOG_FILE="${REC_DIR}/.monitor.log"

if [ "$ENABLE_LOG" -eq 1 ]; then
    exec >>"$MONITOR_LOG_FILE" 2>&1
    echo "===== $(date '+%Y-%m-%d %H:%M:%S') 监测守护进程启动 name=$NAME PID=$$ ====="
    echo "PATH=$PATH"
    command -v streamlink >/dev/null 2>&1 && echo "streamlink 路径: $(command -v streamlink)" || echo "警告: 当前 PATH 下找不到 streamlink"
else
    exec >/dev/null 2>&1
fi

echo $$ > "$MONITOR_PID_FILE"
echo "监测 PID 文件已写入: $MONITOR_PID_FILE"

MANUAL_STOP=0
CURRENT_CHECK_PID=""

on_term_monitor() {
    MANUAL_STOP=1
    echo "$(date '+%Y-%m-%d %H:%M:%S') 收到中断信号，正在停止监测..."
    if [ -n "$CURRENT_CHECK_PID" ] && kill -0 "$CURRENT_CHECK_PID" 2>/dev/null; then
        kill -TERM "$CURRENT_CHECK_PID" 2>/dev/null
    fi
}
trap on_term_monitor SIGTERM SIGINT

# 可被信号立即打断的 sleep（普通的前台 sleep 会让 trap 延迟到 sleep 结束才执行，这里用后台+wait 规避）
interruptible_sleep() {
    sleep "$1" &
    CURRENT_CHECK_PID=$!
    wait "$CURRENT_CHECK_PID"
    CURRENT_CHECK_PID=""
}

cd "$REC_DIR" || exit 1

# STATE: MONITOR（监测中）/ RECORDING（已通知开播，等待录制进程结束）
STATE="MONITOR"

echo "$(date '+%Y-%m-%d %H:%M:%S') 开始监测 $NAME，每 ${MONITOR_INTERVAL} 秒探测一次（只探测不下载）"

while true; do
    if [ "$MANUAL_STOP" -eq 1 ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') 已手动停止监测，未对文件夹做任何修改。"
        rm -f "$MONITOR_PID_FILE"
        exit 0
    fi

    if [ "$STATE" = "RECORDING" ]; then
        # 只需要轮询 record.sh 的录制进程是否还活着
        if [ -f "$RECORD_PID_FILE" ] && kill -0 "$(cat "$RECORD_PID_FILE")" 2>/dev/null; then
            interruptible_sleep "$RECORD_POLL_INTERVAL"
            continue
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') 录制进程已结束，回到监测模式"
            STATE="MONITOR"
            interruptible_sleep "$MONITOR_INTERVAL"
            continue
        fi
    fi

    # STATE = MONITOR：探测是否开播（只探测不下载）
    URL=$(printf "$URL_TEMPLATE" "$NAME")
    echo "$(date '+%Y-%m-%d %H:%M:%S') 探测: streamlink --stream-url \"$URL\" best"

    streamlink --stream-url "$URL" best >/dev/null 2>&1 &
    CURRENT_CHECK_PID=$!
    wait "$CURRENT_CHECK_PID"
    CHECK_EXIT=$?
    CURRENT_CHECK_PID=""

    echo "$(date '+%Y-%m-%d %H:%M:%S') 探测退出码=$CHECK_EXIT"

    if [ "$MANUAL_STOP" -eq 1 ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') 已手动停止监测，未对文件夹做任何修改。"
        rm -f "$MONITOR_PID_FILE"
        exit 0
    fi

    if [ "$CHECK_EXIT" -ne 0 ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') 未检测到开播，${MONITOR_INTERVAL} 秒后再次探测"
        interruptible_sleep "$MONITOR_INTERVAL"
        continue
    fi

    echo "$(date '+%Y-%m-%d %H:%M:%S') 检测到 $NAME 开播，通知: $PYTHON_BIN $NOTIFY_SCRIPT ${NAME}_on"
    "$PYTHON_BIN" "$NOTIFY_SCRIPT" "${NAME}_on"

    echo "$(date '+%Y-%m-%d %H:%M:%S') 启动独立录制进程: $RECORD_SCRIPT start $NAME monitored"
    "$RECORD_SCRIPT" start "$NAME" monitored

    sleep 1
    if [ -f "$RECORD_PID_FILE" ] && kill -0 "$(cat "$RECORD_PID_FILE")" 2>/dev/null; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') 录制进程已确认启动，转入等待状态"
        STATE="RECORDING"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') 警告：录制进程似乎没有成功启动，${MONITOR_INTERVAL} 秒后重新探测"
        interruptible_sleep "$MONITOR_INTERVAL"
    fi
done
