#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time    : 2025/9/25 15:30
# @Author  : reenrr
# @File    : merge_video.py
'''
# !/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time    : 2025/09/25
# @Author  : reenrr
# @File    : video_merger.py
# 功能描述：基于 FFmpeg 合并多个视频文件（支持跨格式、进度显示）
# 依赖：需先安装 FFmpeg 并配置环境变量
'''
import os
import subprocess
import platform
from typing import List


class VideoMerger:
    def __init__(self):
        # 检查 FFmpeg 是否安装（通过调用 ffmpeg -version 验证）
        self._check_ffmpeg_installed()

    def _check_ffmpeg_installed(self) -> None:
        """检查 FFmpeg 是否已安装并配置环境变量"""
        try:
            # 调用 FFmpeg 版本命令，无异常则说明安装成功
            subprocess.run(
                ["ffmpeg", "-version"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                check=True
            )
        except FileNotFoundError:
            raise EnvironmentError(
                "未找到 FFmpeg！请先安装 FFmpeg 并配置系统环境变量。\n"
                "下载地址：https://ffmpeg.org/download.html\n"
                "Windows 配置：将 FFmpeg 的 bin 目录添加到 PATH 环境变量；\n"
                "macOS：使用 brew install ffmpeg；\n"
                "Linux：使用 sudo apt install ffmpeg（Ubuntu）。"
            )
        except subprocess.CalledProcessError:
            raise RuntimeError("FFmpeg 安装异常，请重新安装。")

    def _create_video_list_file(self, video_paths: List[str], list_file_path: str = "video_list.txt") -> str:
        """
        创建 FFmpeg 所需的视频列表文件（用于合并多个视频）
        FFmpeg 合并需通过文本文件指定视频路径，格式为：file '视频路径'
        """
        # 检查所有视频文件是否存在
        for idx, path in enumerate(video_paths):
            if not os.path.exists(path):
                raise FileNotFoundError(f"第 {idx + 1} 个视频文件不存在：{path}")
            # 检查是否为文件（而非目录）
            if not os.path.isfile(path):
                raise IsADirectoryError(f"第 {idx + 1} 个路径不是文件：{path}")

        # 写入视频列表（处理路径中的特殊字符，兼容 Windows 反斜杠）
        with open(list_file_path, "w", encoding="utf-8") as f:
            for path in video_paths:
                # 统一路径分隔符为正斜杠（FFmpeg 兼容正斜杠）
                normalized_path = path.replace("\\", "/")
                # 写入格式：file '路径'（单引号避免路径含空格/特殊字符）
                f.write(f"file '{normalized_path}'\n")

        return list_file_path

    def merge_videos(self,
                     video_paths: List[str],
                     output_path: str = "merged_output.mp4",
                     overwrite: bool = False) -> None:
        """
        合并多个视频文件
        :param video_paths: 待合并的视频路径列表（顺序即合并顺序）
        :param output_path: 输出视频路径（默认当前目录 merged_output.mp4）
        :param overwrite: 是否覆盖已存在的输出文件（默认不覆盖）
        """
        # 1. 基础参数校验
        if len(video_paths) < 2:
            raise ValueError("至少需要 2 个视频文件才能合并！")

        # 2. 处理输出文件（若已存在且不允许覆盖，直接退出）
        if os.path.exists(output_path) and not overwrite:
            raise FileExistsError(f"输出文件已存在：{output_path}，若需覆盖请设置 overwrite=True。")

        # 3. 创建 FFmpeg 视频列表文件
        list_file = self._create_video_list_file(video_paths)
        print(f"已创建视频列表文件：{list_file}")

        # 4. 构造 FFmpeg 合并命令
        # -f concat：使用 concat 协议（合并多个文件）
        # -safe 0：允许处理任意路径的文件（避免 FFmpeg 限制相对路径）
        # -i {list_file}：输入视频列表文件
        # -c copy：直接复制音视频流（不重新编码，速度快；若格式不兼容会自动 fallback 编码）
        # -y：强制覆盖输出文件（仅 overwrite=True 时启用）
        ffmpeg_cmd = [
            "ffmpeg",
            "-f", "concat",
            "-safe", "0",
            "-i", list_file,
            "-c", "copy",  # 核心参数：复制流（快速合并），若格式不兼容可删除此句（自动编码）
            "-hide_banner",  # 隐藏 FFmpeg 版本横幅（简化输出）
            "-loglevel", "info"  # 显示进度和关键日志（可改为 warning 减少输出）
        ]
        # 若允许覆盖，添加 -y 参数
        if overwrite:
            ffmpeg_cmd.append("-y")
        # 添加输出路径
        ffmpeg_cmd.append(output_path)

        try:
            print(f"\n开始合并视频（共 {len(video_paths)} 个）：")
            for idx, path in enumerate(video_paths, 1):
                print(f"  {idx}. {os.path.basename(path)}")
            print(f"\n输出路径：{os.path.abspath(output_path)}")
            print("=" * 50)

            # 5. 执行 FFmpeg 命令（实时打印进度）
            process = subprocess.Popen(
                ffmpeg_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,  # 合并 stdout 和 stderr，统一捕获进度
                text=True,
                bufsize=1  # 行缓冲，实时输出日志
            )

            # 实时打印 FFmpeg 输出（显示合并进度）
            for line in process.stdout:
                print(line.strip(), end="\r" if "time=" in line else "\n")  # 进度行覆盖显示，其他行换行

            # 等待命令执行完成，获取返回码
            process.wait()
            if process.returncode == 0:
                print("\n" + "=" * 50)
                print(f"视频合并成功！输出文件：{os.path.abspath(output_path)}")
            else:
                raise RuntimeError(f"FFmpeg 执行失败（返回码：{process.returncode}），请检查日志排查问题。")

        finally:
            # 6. 清理临时文件（视频列表文件）
            if os.path.exists(list_file):
                os.remove(list_file)
                print(f"\n已清理临时列表文件：{list_file}")


if __name__ == "__main__":
    # -------------------------- 配置参数（根据需求修改） --------------------------
    # 1. 待合并的视频路径列表（顺序即合并顺序，支持绝对路径/相对路径）
    # 示例1：相对路径（视频文件与脚本在同一目录）
    # VIDEO_PATHS = [
    #     "video1.mp4",
    #     "video2.mkv",
    #     "video3.avi"
    # ]
    # 示例2：绝对路径（Windows 需用双反斜杠或正斜杠）
    VIDEO_PATHS = [
        r"C:\Project\zjsh\data_collection\data_collection\camera01_videos\video_20250925_062802_part1.mp4",  # Windows 绝对路径（r 表示原始字符串，避免转义）
        r"C:\Project\zjsh\data_collection\data_collection\camera01_videos\video_20250925_062905_part2.mp4"
    ]
    # 2. 输出视频路径（默认当前目录，可自定义）
    OUTPUT_PATH = "merged_video.mp4"
    # 3. 是否覆盖已存在的输出文件（True=覆盖，False=不覆盖）
    OVERWRITE = True

    # -------------------------- 执行合并 --------------------------
    try:
        merger = VideoMerger()
        merger.merge_videos(
            video_paths=VIDEO_PATHS,
            output_path=OUTPUT_PATH,
            overwrite=OVERWRITE
        )
    except Exception as e:
        print(f"\n合并失败：{str(e)}")