#!/bin/bash
#
# merge_videos.sh
# 按文件名顺序合并指定文件夹内的所有视频文件为一个新视频
#
# 用法:
#   ./merge_videos.sh [文件夹路径] [输出文件名]
#
# 示例:
#   ./merge_videos.sh ./videos output.mp4
#   ./merge_videos.sh                 # 默认当前目录，输出 merged_output.mp4
#
# 说明:
#   默认使用 ffmpeg 的 concat demuxer + "-c copy"（不重新编码，速度快，
#   但要求所有视频编码格式/分辨率/帧率一致，否则可能失败或画面异常）。
#   如果视频格式不一致，脚本会自动检测并提示是否切换为重新编码模式（-re-encode）。

set -euo pipefail

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

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

# 支持的视频扩展名（可按需增删）
EXTENSIONS=("mp4" "mkv" "mov" "avi" "flv" "wmv" "webm" "ts" "m4v")

# ---------- 收集视频文件并按名称排序 ----------
FILELIST=$(mktemp)
trap 'rm -f "$FILELIST"' EXIT

# 构造 find 的 -iname 条件
FIND_EXPR=()
for ext in "${EXTENSIONS[@]}"; do
    FIND_EXPR+=(-iname "*.${ext}" -o)
done
unset 'FIND_EXPR[${#FIND_EXPR[@]}-1]'   # 去掉最后一个多余的 -o

# 找到文件，按文件名自然排序（-V 支持数字顺序，如 1,2,...,10 而不是 1,10,2）
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

# ---------- 生成 ffmpeg concat 所需的列表文件 ----------
for v in "${VIDEOS[@]}"; do
    # 处理路径中的单引号，ffmpeg concat 文件要求用单引号包裹路径
    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 "警告: 检测到视频的编码格式或分辨率不一致，直接拼接可能失败或画面异常。"
        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"
