#!/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" 直接拼接；如检测到编码/分辨率不一致，
#     或直接拼接失败，会自动切换为重新编码模式 (libx264 + aac)。

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

for v in "${VIDEOS[@]}"; do
    ABS_PATH=$(realpath "$v")
    ESCAPED_PATH=${ABS_PATH//\'/\'\\\'\'}
    echo "file '$ESCAPED_PATH'" >> "$FILELIST"
done

# 检测编码格式/分辨率是否一致
check_uniform() {
    local codec_set=()
    local res_set=()
    for v in "${VIDEOS[@]}"; do
        local codec res
        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")
        codec_set+=("$codec")
        res_set+=("$res")
    done
    local first_codec="${codec_set[0]}"
    local first_res="${res_set[0]}"
    for c in "${codec_set[@]}"; do
        [ "$c" != "$first_codec" ] && return 1
    done
    for r in "${res_set[@]}"; do
        [ "$r" != "$first_res" ] && return 1
    done
    return 0
}

REENCODE=false
if command -v ffprobe >/dev/null 2>&1; then
    if ! check_uniform; then
        echo "警告: 检测到视频编码格式或分辨率不一致，将自动切换为重新编码模式。"
        REENCODE=true
    fi
else
    echo "提示: 未检测到 ffprobe，跳过格式一致性检查。"
fi

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

if [ "$REENCODE" = true ]; then
    ffmpeg -y -f concat -safe 0 -i "$FILELIST" \
        -c:v libx264 -preset medium -crf 20 -c:a aac -b:a 192k \
        "$OUTPUT"
else
    if ! ffmpeg -y -f concat -safe 0 -i "$FILELIST" -c copy "$OUTPUT"; then
        echo ""
        echo "直接拼接失败，尝试改用重新编码模式..."
        ffmpeg -y -f concat -safe 0 -i "$FILELIST" \
            -c:v libx264 -preset medium -crf 20 -c:a aac -b:a 192k \
            "$OUTPUT"
    fi
fi

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