#!/bin/bash
#
# fix_and_merge_videos.sh
#
# 一步完成两件事:
#   1) 对文件夹内所有视频执行 ffmpeg -i xxx.mp4 -c copy xxx1.mp4 重新封装，
#      修复时间戳(PTS/DTS)问题，成功后删除旧文件并把新文件改回原文件名。
#   2) 将修复后的所有视频按文件名顺序合并为一个新视频。
#
# 用法:
#   ./fix_and_merge_videos.sh [文件夹路径] [输出文件名] [--skip-fix]
#
# 示例:
#   ./fix_and_merge_videos.sh ./videos output.mp4
#   ./fix_and_merge_videos.sh                      # 当前目录，输出 merged_output.mp4
#   ./fix_and_merge_videos.sh ./videos out.mp4 --skip-fix   # 跳过修复时间戳步骤，直接合并
#
# 说明:
#   - 第一步 remux 阶段如果某个文件失败，会保留原文件不动，不会误删，
#     并在最后给出统计；随后仍会尝试继续合并。
#   - 第二步合并阶段始终使用 "-c copy" 直接拼接，不会自动重新编码。
#     合并前会逐个探测每个视频的编码格式/分辨率：
#       * 全部一致 -> 直接拼接全部视频，输出 $OUTPUT
#       * 发现某个文件与前面不一致 -> 停止运行，报出具体是哪个文件、
#         哪项参数不一致，并将该文件之前已经一致的视频拼接输出到 $OUTPUT，
#         脚本以非零状态退出，方便你检查后手动处理剩余部分。

set -uo pipefail

# ---------- 参数处理 ----------
FOLDER="${1:-.}"
OUTPUT="${2:-merged_output.mp4}"
SKIP_FIX=false

for arg in "$@"; do
    if [ "$arg" = "--skip-fix" ]; then
        SKIP_FIX=true
    fi
done

if [ ! -d "$FOLDER" ]; then
    echo "错误: 文件夹不存在: $FOLDER"
    exit 1
fi

EXTENSIONS=("mp4" "mkv" "mov" "avi" "flv" "wmv" "webm" "ts" "m4v")

FIND_EXPR=()
for ext in "${EXTENSIONS[@]}"; do
    FIND_EXPR+=(-iname "*.${ext}" -o)
done
unset 'FIND_EXPR[${#FIND_EXPR[@]}-1]'

# ==================================================================
# 第一步: 重新封装，修复时间戳问题
# ==================================================================
if [ "$SKIP_FIX" = false ]; then
    echo "===== 第一步: 修复时间戳（重新封装） ====="

    mapfile -t VIDEOS < <(find "$FOLDER" -maxdepth 1 -type f \( "${FIND_EXPR[@]}" \) | sort -V)

    if [ "${#VIDEOS[@]}" -eq 0 ]; then
        echo "错误: 在 $FOLDER 中未找到任何视频文件"
        exit 1
    fi

    echo "找到 ${#VIDEOS[@]} 个视频文件，开始逐个重新封装..."
    echo ""

    SUCCESS_COUNT=0
    FAIL_COUNT=0

    for v in "${VIDEOS[@]}"; do
        DIR=$(dirname "$v")
        BASE=$(basename "$v")
        NAME="${BASE%.*}"
        EXT="${BASE##*.}"
        NEWFILE="${DIR}/${NAME}1.${EXT}"

        if [ -e "$NEWFILE" ]; then
            echo "跳过: $NEWFILE 已存在，请先手动处理"
            FAIL_COUNT=$((FAIL_COUNT + 1))
            continue
        fi

        echo "处理: $BASE"
        if ffmpeg -y -i "$v" -c copy "$NEWFILE" -loglevel error; then
            if [ -s "$NEWFILE" ]; then
                rm -f "$v"
                mv "$NEWFILE" "$v"
                echo "  完成: $BASE (时间戳已修复)"
                SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
            else
                echo "  失败: 生成的文件为空，保留原文件 $BASE"
                rm -f "$NEWFILE"
                FAIL_COUNT=$((FAIL_COUNT + 1))
            fi
        else
            echo "  失败: ffmpeg 处理出错，保留原文件 $BASE"
            rm -f "$NEWFILE"
            FAIL_COUNT=$((FAIL_COUNT + 1))
        fi
        echo ""
    done

    echo "修复完成: 成功 $SUCCESS_COUNT 个，失败 $FAIL_COUNT 个"
    echo ""
else
    echo "===== 已跳过修复时间戳步骤 (--skip-fix) ====="
    echo ""
fi

# ==================================================================
# 第二步: 按文件名顺序合并视频
# ==================================================================
echo "===== 第二步: 按顺序合并视频 ====="

mapfile -t VIDEOS < <(find "$FOLDER" -maxdepth 1 -type f \( "${FIND_EXPR[@]}" \) | sort -V)

if [ "${#VIDEOS[@]}" -eq 0 ]; then
    echo "错误: 在 $FOLDER 中未找到任何视频文件，无法合并"
    exit 1
fi

echo "将按以下顺序合并 ${#VIDEOS[@]} 个视频："
for v in "${VIDEOS[@]}"; do
    echo "  - $(basename "$v")"
done
echo ""

FILELIST=$(mktemp)
trap 'rm -f "$FILELIST"' EXIT

# ---------- 逐个探测编码格式/分辨率，找出第一个不一致的文件 ----------
if ! command -v ffprobe >/dev/null 2>&1; then
    echo "警告: 未检测到 ffprobe，无法提前检测编码一致性，将直接尝试整体拼接。"
    MISMATCH_INDEX=-1
else
    BASE_CODEC=""
    BASE_RES=""
    MISMATCH_INDEX=-1

    for i in "${!VIDEOS[@]}"; do
        v="${VIDEOS[$i]}"
        codec=$(ffprobe -v error -select_streams v:0 -show_entries stream=codec_name \
                -of csv=p=0 "$v" 2>/dev/null || echo "unknown")
        res=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height \
                -of csv=p=0:s=x "$v" 2>/dev/null || echo "unknown")

        if [ "$i" -eq 0 ]; then
            BASE_CODEC="$codec"
            BASE_RES="$res"
            continue
        fi

        if [ "$codec" != "$BASE_CODEC" ] || [ "$res" != "$BASE_RES" ]; then
            MISMATCH_INDEX=$i
            echo ""
            echo "错误: 检测到编码格式/分辨率不一致，无法直接拼接。"
            echo "  不一致文件: $(basename "$v")"
            echo "    该文件编码: $codec, 分辨率: $res"
            echo "  基准文件: $(basename "${VIDEOS[0]}")"
            echo "    基准编码: $BASE_CODEC, 分辨率: $BASE_RES"
            break
        fi
    done
fi

# ---------- 根据检测结果，决定拼接范围 ----------
if [ "$MISMATCH_INDEX" -ge 0 ]; then
    if [ "$MISMATCH_INDEX" -eq 0 ]; then
        echo "第一个文件即出现异常，无法输出任何拼接内容。"
        exit 1
    fi

    echo ""
    echo "将只拼接不一致文件之前的 $MISMATCH_INDEX 个视频，输出到: $OUTPUT"

    : > "$FILELIST"
    for ((i=0; i<MISMATCH_INDEX; i++)); do
        v="${VIDEOS[$i]}"
        ABS_PATH=$(realpath "$v")
        ESCAPED_PATH=${ABS_PATH//\'/\'\\\'\'}
        echo "file '$ESCAPED_PATH'" >> "$FILELIST"
    done

    ffmpeg -y -f concat -safe 0 -i "$FILELIST" -c copy "$OUTPUT"

    echo ""
    echo "已输出不一致文件之前的部分合并结果: $OUTPUT"
    echo "请检查文件 $(basename "${VIDEOS[$MISMATCH_INDEX]}") 后手动处理剩余视频。"
    exit 1
fi

# ---------- 格式一致（或无法检测），直接整体拼接 ----------
for v in "${VIDEOS[@]}"; do
    ABS_PATH=$(realpath "$v")
    ESCAPED_PATH=${ABS_PATH//\'/\'\\\'\'}
    echo "file '$ESCAPED_PATH'" >> "$FILELIST"
done

echo ""
echo "开始合并，输出文件: $OUTPUT"

if ! ffmpeg -y -f concat -safe 0 -i "$FILELIST" -c copy "$OUTPUT"; then
    echo ""
    echo "错误: 直接拼接失败（ffmpeg 报错），请检查视频文件是否损坏。"
    exit 1
fi

echo ""
echo "全部完成，合并结果: $OUTPUT"
