#!/bin/bash
#
# record.sh - 直播录制脚本（断流自动重连 + 超时通知）
# 只负责“录制”本身，不包含监测逻辑（监测请用 monitor.sh）
#
# 用法：
#   启动录制：./record.sh start <name>
#   停止录制：./record.sh stop <name>
#   查询状态：./record.sh list
#   调试模式：./record.sh debug <name>   （前台运行，打印 streamlink 真实报错，方便排查失败原因）
#
# 说明：
#   - start：立即尝试录制。首次调用会自动 fork 到后台运行并立即返回，不产生任何日志文件（除非 ENABLE_LOG=1）。
#     断流后每 3 秒重试一次；若连续 2 分钟都无法恢复录制，调用 ding.py 通知一次并停止录制进程。
#     如果一开始就没连接成功过（主播本来就不在线），会立即通知并退出，不等待 2 分钟。
#   - stop：只发送终止信号让后台进程退出，不对文件夹内任何文件做修改（不合并、不删除）。
#   - 如果是被 monitor.sh 以 "monitored" 模式拉起的（`./record.sh start <name> monitored`），
#     判定下播时通知会用 "{name}_off" 而不是普通的 "{name}"（"{name}_on" 由 monitor.sh 负责发送）。

# 解析脚本自身的绝对路径（不管以什么方式调用，都能正确用于自我拉起后台进程）
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

RECONNECT_INTERVAL=3                    # 断流后重试间隔（秒）
NOTIFY_THRESHOLD=120                    # 断流超过这么久（秒）就通知一次并停止脚本

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

ACTION="$1"
NAME="$2"
ARG3="$3"

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

    FOUND=0
    for pid_file in "$BASE_DIR"/*/.record.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 {start|stop} <name> | $0 list"
    exit 1
fi

REC_DIR="${BASE_DIR}/${NAME}"
PID_FILE="${REC_DIR}/.record.pid"

# ---------- debug：前台运行，打印 streamlink 真实输出，方便定位失败原因 ----------
if [ "$ACTION" = "debug" ]; then
    mkdir -p "$REC_DIR"
    cd "$REC_DIR" || exit 1

    MANUAL_STOP=0
    CURRENT_SL_PID=""
    on_term_debug() {
        MANUAL_STOP=1
        echo "[debug] 收到中断信号，正在停止..."
        if [ -n "$CURRENT_SL_PID" ] && kill -0 "$CURRENT_SL_PID" 2>/dev/null; then
            kill -TERM "$CURRENT_SL_PID" 2>/dev/null
        fi
    }
    trap on_term_debug SIGINT SIGTERM

    interruptible_sleep_debug() {
        sleep "$1" &
        CURRENT_SL_PID=$!
        wait "$CURRENT_SL_PID"
        CURRENT_SL_PID=""
    }

    DISCONNECT_START=0
    NOTIFIED=0
    HAS_CONNECTED_ONCE=0

    echo "[debug] name=$NAME"
    echo "[debug] URL: $(printf "$URL_TEMPLATE" "$NAME")"
    echo "[debug] 输出目录: $REC_DIR"
    echo "[debug] 按 Ctrl+C 停止调试（不会修改文件夹内容）"
    echo ""

    while true; do
        if [ "$MANUAL_STOP" -eq 1 ]; then
            echo "[debug] 已手动停止。"
            exit 0
        fi

        TIME_STR=$(date +%m%d%H%M%S)
        URL=$(printf "$URL_TEMPLATE" "$NAME")
        OUTPUT="${NAME}_${TIME_STR}.mp4"

        echo "[debug] $(date '+%Y-%m-%d %H:%M:%S') 尝试录制 -> $OUTPUT"
        echo "[debug] 命令: streamlink -f \"$URL\" best -o \"$OUTPUT\""

        START_TS=$(date +%s)
        streamlink -f "$URL" best -o "$OUTPUT" &
        CURRENT_SL_PID=$!
        wait "$CURRENT_SL_PID"
        SL_EXIT=$?
        CURRENT_SL_PID=""
        END_TS=$(date +%s)

        echo "[debug] streamlink 退出，退出码=$SL_EXIT，运行时长=$((END_TS - START_TS))秒"

        if [ "$MANUAL_STOP" -eq 1 ]; then
            echo "[debug] 已手动停止。"
            exit 0
        fi

        if [ "$SL_EXIT" -eq 0 ]; then
            echo "[debug] 判定为曾经连接成功（退出码0），重置断流计时，1秒后开始下一段"
            DISCONNECT_START=0
            NOTIFIED=0
            HAS_CONNECTED_ONCE=1
            interruptible_sleep_debug 1
            continue
        fi

        if [ "$HAS_CONNECTED_ONCE" -eq 0 ]; then
            echo "[debug] 第一次尝试就失败，判定主播不在线或录制失败，调用: $PYTHON_BIN $NOTIFY_SCRIPT $NAME"
            "$PYTHON_BIN" "$NOTIFY_SCRIPT" "$NAME"
            echo "[debug] 已通知，停止录制 (name=$NAME)"
            exit 0
        fi

        if [ "$DISCONNECT_START" -eq 0 ]; then
            DISCONNECT_START=$(date +%s)
            NOTIFIED=0
            echo "[debug] 判定为断流/连接失败，开始计时"
        fi

        NOW=$(date +%s)
        ELAPSED=$((NOW - DISCONNECT_START))
        echo "[debug] 已断流 ${ELAPSED} 秒，${RECONNECT_INTERVAL} 秒后重试"

        if [ "$ELAPSED" -ge "$NOTIFY_THRESHOLD" ] && [ "$NOTIFIED" -eq 0 ]; then
            echo "[debug] 超过 ${NOTIFY_THRESHOLD} 秒未恢复，调用: $PYTHON_BIN $NOTIFY_SCRIPT $NAME"
            "$PYTHON_BIN" "$NOTIFY_SCRIPT" "$NAME"
            NOTIFIED=1
            echo "[debug] 已通知，停止录制 (name=$NAME)"
            exit 0
        fi

        interruptible_sleep_debug "$RECONNECT_INTERVAL"
    done
fi

# ---------- stop ----------
if [ "$ACTION" = "stop" ]; then
    if [ -f "$PID_FILE" ]; then
        PID=$(cat "$PID_FILE")
        if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
            kill -TERM "$PID"
            echo "已发送停止信号 (PID $PID)，文件夹内容不会被修改。"
        else
            echo "未找到运行中的进程（可能已经停止）。"
            rm -f "$PID_FILE"
        fi
    else
        echo "未找到 PID 文件，当前可能没有在录制 $NAME。"
    fi
    exit 0
fi

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

mkdir -p "$REC_DIR"

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

# 是否是被 monitor.sh 以 monitored 模式拉起的，决定下播通知用 name 还是 name_off
if [ "$ARG3" = "monitored" ]; then
    RECORD_MONITORED=1
else
    RECORD_MONITORED="${RECORD_MONITORED:-0}"
fi

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

    # 等一下，确认后台进程确实起来了（避免路径/权限问题导致静默失败却提示成功）
    sleep 1
    if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
        echo "录制已在后台启动 (name=$NAME)"
    else
        echo "警告：后台进程似乎没有成功启动，请检查 SCRIPT_PATH=$SCRIPT_PATH 是否可执行，" \
             "或临时把脚本里 ENABLE_LOG 改成 1 再试一次并查看 ${REC_DIR}/.record.log"
    fi
    exit 0
fi

# ================== 以下为后台守护进程实际执行的逻辑 ==================
LOG_FILE="${REC_DIR}/.record.log"

if [ "$ENABLE_LOG" -eq 1 ]; then
    exec >>"$LOG_FILE" 2>&1
    echo "===== $(date '+%Y-%m-%d %H:%M:%S') 守护进程启动 name=$NAME PID=$$ monitored=$RECORD_MONITORED ====="
    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 $$ > "$PID_FILE"
echo "PID 文件已写入: $PID_FILE"

# 决定通知目标：monitored 模式下用 name_off，否则用 name
if [ "$RECORD_MONITORED" = "1" ]; then
    NOTIFY_TARGET="${NAME}_off"
else
    NOTIFY_TARGET="$NAME"
fi

MANUAL_STOP=0
CURRENT_SL_PID=""

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

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

cd "$REC_DIR" || exit 1

DISCONNECT_START=0
NOTIFIED=0
HAS_CONNECTED_ONCE=0

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

    TIME_STR=$(date +%m%d%H%M%S)
    URL=$(printf "$URL_TEMPLATE" "$NAME")
    OUTPUT="${NAME}_${TIME_STR}.mp4"

    echo "$(date '+%Y-%m-%d %H:%M:%S') 尝试录制 -> $OUTPUT ; 命令: streamlink -f \"$URL\" best -o \"$OUTPUT\""

    START_TS=$(date +%s)
    streamlink -f "$URL" best -o "$OUTPUT" &
    CURRENT_SL_PID=$!
    wait "$CURRENT_SL_PID"
    SL_EXIT=$?
    CURRENT_SL_PID=""
    END_TS=$(date +%s)

    echo "$(date '+%Y-%m-%d %H:%M:%S') streamlink 退出，退出码=$SL_EXIT，运行时长=$((END_TS - START_TS))秒"

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

    if [ "$SL_EXIT" -eq 0 ]; then
        # 退出码为 0，说明这次是真正连接上并正常结束的（不是断流失败）
        echo "$(date '+%Y-%m-%d %H:%M:%S') 判定为曾经连接成功（退出码0），重置断流计时，1秒后开始下一段"
        DISCONNECT_START=0
        NOTIFIED=0
        HAS_CONNECTED_ONCE=1
        interruptible_sleep 1
        continue
    fi

    # streamlink 很快就退出了，说明目前处于断流/连不上状态
    if [ "$HAS_CONNECTED_ONCE" -eq 0 ]; then
        # 从启动开始第一次尝试就失败，说明主播不在线或录制失败，无需等待，直接通知并退出
        echo "$(date '+%Y-%m-%d %H:%M:%S') 第一次尝试就失败，判定主播不在线或录制失败，调用: $PYTHON_BIN $NOTIFY_SCRIPT $NOTIFY_TARGET"
        "$PYTHON_BIN" "$NOTIFY_SCRIPT" "$NOTIFY_TARGET"
        echo "$(date '+%Y-%m-%d %H:%M:%S') 已通知，停止录制进程 (name=$NAME)"
        rm -f "$PID_FILE"
        exit 0
    fi

    if [ "$DISCONNECT_START" -eq 0 ]; then
        DISCONNECT_START=$(date +%s)
        NOTIFIED=0
        echo "$(date '+%Y-%m-%d %H:%M:%S') 判定为断流/连接失败，开始计时"
    fi

    NOW=$(date +%s)
    ELAPSED=$((NOW - DISCONNECT_START))
    echo "$(date '+%Y-%m-%d %H:%M:%S') 已断流 ${ELAPSED} 秒，${RECONNECT_INTERVAL} 秒后重试"

    if [ "$ELAPSED" -ge "$NOTIFY_THRESHOLD" ] && [ "$NOTIFIED" -eq 0 ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') 超过 ${NOTIFY_THRESHOLD} 秒未恢复，调用: $PYTHON_BIN $NOTIFY_SCRIPT $NOTIFY_TARGET"
        "$PYTHON_BIN" "$NOTIFY_SCRIPT" "$NOTIFY_TARGET"
        NOTIFIED=1
        echo "$(date '+%Y-%m-%d %H:%M:%S') 已通知，停止录制进程 (name=$NAME)"
        rm -f "$PID_FILE"
        exit 0
    fi

    sleep "$RECONNECT_INTERVAL" &
    CURRENT_SL_PID=$!
    wait "$CURRENT_SL_PID"
    CURRENT_SL_PID=""
done
