Skill: zhuchenggong19851114-design/video-ai-process (version 1.0.1)

# video-ai-process - Video AI Process / AI视频处理 全自动AI视频处理系统：转写 → 分析 → 切片 → 飞书 → 自动拼接/客户打分 → 最终视频 --- ## 整体流程 ``` 客户输入视频 ↓ ┌─────────────────────────────────────────────────────────┐ │ Step 1-4：AI处理 │ │ Whisper转写 → MiniMax分析 → FFmpeg切片 │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ Step 5：写入飞书 │ │ 每段片段内容写入视频片段库Bitable │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ Step 6：自动拼接（系统自动执行，无需等待） │ │ 粗剪片段拼接 → 粗剪版_final.mp4 │ │ 精剪片段拼接 → 精剪版_final.mp4 │ │ 自动写入飞书 │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ 客户在飞书查看片段内容 │ │ 在「用户自定义重用性排序」字段打分 │ │ 1=最重要，2=次重要，3=第三... │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ Step 7：自定义拼接（客户打完分通知我们，或心跳自动检查） │ │ 按打分排序拼接 → 自定义粗剪版 + 自定义精剪版 │ │ 写入飞书 │ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ 客户选择最终版本 │ │ 满意 → 结束 │ │ 不满意 → 重新打分 → Step 7重新拼接 │ └─────────────────────────────────────────────────────────┘ ``` --- ## Step 1 - Whisper转写 **目的**：将视频音频转为带时间戳的文本 **命令**： ```python from faster_whisper import WhisperModel model = WhisperModel("small", device="cpu") segments, info = model.transcribe(audio_path, language="zh") segments_list = list(segments) ``` **输出**： - `video_转写.txt`：带时间戳文本 ``` [0.000-2.000] 大家好 [2.000-6.600] 今天我们继续聊AI和OpenCloud ``` - `video.srt`：SRT字幕文件 --- ## Step 2 - MiniMax粗剪分析 **目的**：AI分析内容，生成粗剪分段方案 **Prompt**： ``` 分析以下视频转写，进行粗剪分段。 要求： - 把明显没用的内容去掉（如：技术故障、停顿过长、重复啰嗦） - 保留核心内容，分成2-5分钟的大段 - 每个片段要内容完整，不能断在半句 - 时长不固定，根据内容自然分段 输出格式： 片段编号 | 时间段 | 时长(秒) | 标签 | 摘要 ``` --- ## Step 3 - MiniMax精剪分析 **目的**：从原内容独立选择最精华片段 **Prompt**： ``` 基于粗剪分段，做精剪分析。只保留最精华的内容。 要求： - 只保留真正精华的内容 - 每段30-90秒 - 内容必须完整 - 去掉重复啰嗦的部分 - 精剪和粗剪是独立的选段，不是子集 输出格式： 片段编号 | 时间段 | 时长(秒) | 标签 | 摘要 ``` --- ## Step 4 - FFmpeg切片 **切片命名规则**： ``` 粗剪片段：粗-{序号}{内容标签}.mp4 例：粗-2引入.mp4、粗-3配置要求.mp4 精剪片段：精-{序号}{内容标签}.mp4 例：精-2配置核心.mp4、精-3案例分析.mp4 ``` **FFmpeg命令**： ```bash # 切片 ffmpeg -i 原视频.mp4 -ss {开始秒} -t {时长} \ -c:v libx264 -preset fast -crf 23 \ -c:a aac -b:a 128k 输出.mp4 -y # 拼接 ffmpeg -f concat -safe 0 -i filelist.txt -c copy concat.mp4 -y # 烧录字幕 ffmpeg -i concat.mp4 -vf "subtitles=字幕.srt:force_style='FontSize=18,PrimaryColour=&HFFFFFF,OutlineColour=&H333333,Outline=2,Alignment=2,MarginV=50'" \ -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k 输出_带字幕.mp4 -y ``` --- ## Step 5 - 写入飞书（每段内容） **目的**：将每段片段信息写入视频片段库Bitable **Bitable配置**： ```yaml app_token: YOUR_APP_TOKEN table_id: YOUR_TABLE_ID ``` 或设置环境变量： ```bash export FEISHU_VIDEO_APP_TOKEN="YOUR_APP_TOKEN" export FEISHU_VIDEO_TABLE_ID="YOUR_TABLE_ID" ``` **每条记录字段**： ```json { "视频片段库": "视频一：XXX教程", "分析类型": ["粗分析-粗剪"], "片段编号": "粗-2", "时间段": "0:45-1:30", "时长": 45, "标签": ["开场"], "摘要": "引入主题：讲解OpenCloud配置是小白最需要搞懂的东西", "文件路径": "D:\\OpenClaw\\downloads\\视频切片\\2026-04-14-粗剪\\粗-2引入.mp4", "来源视频": "原视频文件名.mp4", "用户自定义重用性排序": null, "入库时间": 1744512000000 } ``` --- ## Step 6 - 自动拼接（系统自动执行） **目的**：自动生成粗剪版和精剪版视频 **执行时机**：Step 5完成后立即自动执行，无需等待客户 **拼接逻辑**： 1. 读取粗剪片段，按编号顺序拼接 → `粗剪版_final.mp4` 2. 读取精剪片段，按编号顺序拼接 → `精剪版_final.mp4` 3. 生成字幕并烧录 4. 写入飞书： ```json { "视频片段库": "视频一：XXX教程", "分析类型": ["粗分析-粗剪", "最终版"], "片段编号": "粗-最终版", "时间段": "连续拼接", "时长": 195, "标签": ["最终版"], "摘要": "自动拼接粗剪片段：粗-2 + 粗-3 + 粗-4 + 粗-5 + 粗-7", "文件路径": "D:\\OpenClaw\\downloads\\视频切片\\2026-04-14-粗剪\\粗剪版_final.mp4", "来源视频": "原视频文件名.mp4", "用户自定义重用性排序": null, "入库时间": 1744512000000 } ``` **输出文件夹**： ``` D:\OpenClaw\downloads\视频切片\ ├── {日期}-粗剪\ │ ├── 粗-2引入.mp4 │ ├── 粗-3配置要求.mp4 │ └── 粗剪版_final.mp4 ← 自动生成 │ └── {日期}-精剪\ ├── 精-2配置核心.mp4 └── 精剪版_final.mp4 ← 自动生成 ``` --- ## 客户操作 **查看片段**：打开飞书视频片段库，查看每段内容摘要 **打分**：在「用户自定义重用性排序」字段填写数字 - 1 = 最重要 - 2 = 次重要 - 3 = 第三重要... **可选择版本**： - 粗剪版（系统自动拼接） - 精剪版（系统自动拼接） - 自定义版（按打分重新拼接） --- ## Step 7 - 自定义拼接（客户打完分后执行） **触发方式**： | 方式 | 说明 | |------|------| | 方式一 | 客户主动发消息："按打分生成" | | 方式二 | 心跳任务自动检查 | **心跳任务配置**： ```yaml 检查频率：每1小时 检查内容：飞书「用户自定义重用性排序」字段是否有新值 ``` **执行流程**： 1. 读取飞书表格，筛选有打分的记录 2. 按打分数字升序排列 3. 生成拼接方案 4. 显示方案给客户确认 5. 确认后执行拼接 6. 生成字幕 7. 写入飞书 **自定义拼接结果写入飞书**： ```json { "视频片段库": "视频一：XXX教程", "分析类型": ["自定义拼接"], "片段编号": "自定义-001", "时间段": "按打分排序", "时长": 120, "标签": ["自定义"], "摘要": "按客户打分排序：粗-3(1分) + 精-2(2分) + 粗-5(3分)", "文件路径": "D:\\OpenClaw\\downloads\\视频切片\\{日期}-自定义\\自定义粗剪版_final.mp4", "来源视频": "原视频文件名.mp4", "用户自定义重用性排序": null, "入库时间": 1744512000000 } ``` --- ## 最终输出 ``` D:\OpenClaw\downloads\视频切片\ ├── {日期}-粗剪\ │ ├── 粗-2引入.mp4 │ ├── 粗-3配置要求.mp4 │ └── 粗剪版_final.mp4 ← Step 6自动生成 │ ├── {日期}-精剪\ │ ├── 精-2配置核心.mp4 │ └── 精剪版_final.mp4 ← Step 6自动生成 │ └── {日期}-自定义\ ├── 自定义粗剪版_final.mp4 ← Step 7按打分生成 └── 自定义精剪版_final.mp4 ← Step 7按打分生成 ``` **飞书表格记录**： - 粗剪片段（每段一条记录） - 精剪片段（每段一条记录） - 粗剪版_final（Step 6自动写入） - 精剪版_final（Step 6自动写入） - 自定义粗剪版（Step 7按打分写入） - 自定义精剪版（Step 7按打分写入） --- ## 客户选择流程 ``` Step 6 自动生成后： ↓ 客户查看飞书，选择版本 ↓ ┌─────────────────┬─────────────────┐ │ 满意粗剪版？ │ 满意精剪版？ │ │ ↓是 │ ↓是 │ │ 使用粗剪版 │ 使用精剪版 │ │ 结束 │ 结束 │ └─────────────────┴─────────────────┘ ↓ 否 客户在飞书打分 ↓ Step 7 按打分生成自定义版 ↓ 客户选择最终版本 ``` --- ## 时间记录 | Step | 操作 | 耗时 | |------|------|------| | Step 1 | Whisper转写 | ~15秒 | | Step 2 | MiniMax粗剪分析 | ~10秒 | | Step 3 | MiniMax精剪分析 | ~10秒 | | Step 4 | FFmpeg切片 | ~60秒 | | Step 5 | 写入飞书 | ~30秒 | | Step 6 | 自动拼接2个版本 | ~60秒 | | Step 7 | 自定义拼接 | ~90秒 | **总计**：~4.5分钟（不含等待打分时间） --- ## 前置要求 - Python 3.8+ - FFmpeg（包含ffprobe） - faster-whisper（WhisperModel） - MiniMax API（mmx CLI） - 飞书Bitable配置 ```bash export HF_ENDPOINT=https://hf-mirror.com ``` --- ## 飞书表格字段 | 字段 | 类型 | 说明 | |------|------|------| | 视频片段库 | 文本 | 视频唯一标识 | | 分析类型 | 多选 | 粗分析-粗剪/细分析-精剪/自定义拼接/最终版 | | 片段编号 | 文本 | 唯一编号 | | 时间段 | 文本 | 时间范围 | | 时长 | 数字 | 秒 | | 标签 | 多选 | 开场/核心观点/演示/总结/自定义/最终版 | | 摘要 | 文本 | 内容描述 | | 文件路径 | 文本 | 本地文件路径 | | 来源视频 | 文本 | 原视频文件名 | | 用户自定义重用性排序 | 数字 | 客户打分字段 | | 入库时间 | 日期 | 自动记录 | --- ## 文件路径 ``` ~/.openclaw/workspace/skills/video-ai-process/ ├── SKILL.md ├── step1_transcribe.py ├── step2_analyze_cu.py ├── step3_analyze_jing.py ├── step4_segment.py ├── step5_write_feishu.py ├── step6_auto_compose.py ├── step7_custom_compose.py └── video_pipeline.py ``` --- ## 注意事项 1. **删除操作必须确认**：切片前检查文件是否存在 2. **飞书录入**：确认Bitable app_token和table_id 3. **自动拼接**：Step 6在Step 5完成后立即执行，无需等待客户 4. **客户打分**：必须等客户在飞书填写后才能执行Step 7 5. **版本选择**：客户可选择任一版本，或重新打分生成新版本

path: video_pipeline.py
content: #!/usr/bin/env python3
"""
video-ai-process 主流程脚本
全自动AI视频处理：转写 → 分析 → 切片 → 飞书 → 自动拼接

用法：
    python3 video_pipeline.py "/mnt/d/视频/原视频.mp4" "视频一：XXX教程"

执行步骤：
    Step 1: Whisper转写
    Step 2: MiniMax粗剪分析
    Step 3: MiniMax精剪分析
    Step 4: FFmpeg切片
    Step 5: 写入飞书
    Step 6: 自动拼接（粗剪版 + 精剪版）
"""

import os
import sys
import json
import subprocess
import time
from datetime import datetime

# ==================== 配置 ====================
VIDEO_PATH = sys.argv[1] if len(sys.argv) > 1 else input("请输入视频路径: ").strip()
VIDEO_NAME = sys.argv[2] if len(sys.argv) > 2 else input("请输入视频名称（如视频一：XXX教程）: ").strip()

# 日期格式
DATE_STR = datetime.now().strftime("%Y-%m-%d")

# 输出目录
OUTPUT_BASE = f"D:\\OpenClaw\\downloads\\视频切片\\{DATE_STR}"
CU_FOLDER = f"{OUTPUT_BASE}-粗剪"
JING_FOLDER = f"{OUTPUT_BASE}-精剪"

# 飞书配置
FEISHU_APP_TOKEN = os.environ.get("FEISHU_VIDEO_APP_TOKEN", "")
FEISHU_TABLE_ID = os.environ.get("FEISHU_VIDEO_TABLE_ID", "")

# ==================== 工具函数 ====================

def run_command(cmd, desc="", timeout=300):
    """执行命令并记录耗时"""
    print(f"\n{'='*50}")
    print(f"执行: {desc}")
    print(f"命令: {cmd[:100]}...")
    print('='*50)
    start = time.time()
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
    elapsed = time.time() - start
    if result.returncode != 0:
        print(f"❌ 失败! 耗时: {elapsed:.1f}秒")
        print(f"错误: {result.stderr[:500]}")
        raise Exception(f"命令执行失败: {desc}")
    print(f"✅ 完成! 耗时: {elapsed:.1f}秒")
    return result.stdout, elapsed

def log_step(step_num, desc, elapsed):
    """记录步骤耗时"""
    print(f"\n📊 Step {step_num} - {desc}: {elapsed:.1f}秒")

def ensure_folder(folder):
    """确保文件夹存在"""
    os.makedirs(folder, exist_ok=True)
    return folder

# ==================== Step 1: Whisper转写 ====================

def step1_transcribe(video_path):
    """Whisper转写"""
    print("\n" + "🔄"*25)
    print("Step 1: Whisper转写")
    print("🔄"*25)
    
    start = time.time()
    
    # 提取音频
    audio_path = "/tmp/video_audio.wav"
    cmd_extract = f'ffmpeg -i "{video_path}" -vn -acodec pcm_s16le -ar 16000 -ac 1 "{audio_path}" -y'
    run_command(cmd_extract, "提取音频")
    
    # 转写
    transcript_path = "/tmp/video_transcript.txt"
    srt_path = "/tmp/video.srt"
    
    transcribe_code = f'''
import sys
sys.path.insert(0, '/home/success/.openclaw/workspace/skills/video-ai-process')

from faster_whisper import WhisperModel

model = WhisperModel("small", device="cpu")
segments, info = model.transcribe("{audio_path}", language="zh")

# 保存带时间戳文本
with open("{transcript_path}", "w", encoding="utf-8") as f:
    for seg in segments:
        start_ms = seg.start * 1000
        end_ms = seg.end * 1000
        f.write(f"[{{start_ms:.0f}}-{{end_ms:.0f}}] {{seg.text.strip()}}\\n")

print(f"转写完成，共{{len(list(segments))}}个片段")
'''
    
    with open("/tmp/transcribe_step.py", "w") as f:
        f.write(transcribe_code)
    
    result = subprocess.run(["python3", "/tmp/transcribe_step.py"], capture_output=True, text=True)
    if result.returncode != 0:
        raise Exception(f"转写失败: {result.stderr}")
    
    elapsed = time.time() - start
    log_step(1, "Whisper转写", elapsed)
    
    return transcript_path, srt_path, elapsed

# ==================== Step 2: MiniMax粗剪分析 ====================

def step2_analyze_cu(transcript_path):
    """MiniMax粗剪分析"""
    print("\n" + "🔄"*25)
    print("Step 2: MiniMax粗剪分析")
    print("🔄"*25)
    
    start = time.time()
    
    # 读取转写文本
    with open(transcript_path, "r", encoding="utf-8") as f:
        transcript_text = f.read()
    
    # 调用MiniMax分析
    analyze_code = f'''
import sys
import json

transcript = '''{json.dumps(transcript_text)}'''

prompt = f"""
分析以下视频转写，进行粗剪分段。

要求：
- 把明显没用的内容去掉（如：技术故障、停顿过长、重复啰嗦）
- 保留核心内容，分成2-5分钟的大段
- 每个片段要内容完整，不能断在半句
- 时长不固定，根据内容自然分段

输出格式（严格按这个格式）：
片段编号 | 时间段 | 时长(秒) | 标签 | 摘要

转写内容：
{{transcript}}
"""
'''
    # TODO: 调用MiniMax API或mmx CLI
    print("⚠️ 需要接入MiniMax API进行粗剪分析")
    print("分析结果待定")
    
    elapsed = time.time() - start
    log_step(2, "MiniMax粗剪分析", elapsed)
    
    return [], elapsed  # 返回空列表待填充

# ==================== Step 3: MiniMax精剪分析 ====================

def step3_analyze_jing(transcript_path):
    """MiniMax精剪分析"""
    print("\n" + "🔄"*25)
    print("Step 3: MiniMax精剪分析")
    print("🔄"*25)
    
    start = time.time()
    
    # 读取转写文本
    with open(transcript_path, "r", encoding="utf-8") as f:
        transcript_text = f.read()
    
    print("⚠️ 需要接入MiniMax API进行精剪分析")
    print("分析结果待定")
    
    elapsed = time.time() - start
    log_step(3, "MiniMax精剪分析", elapsed)
    
    return [], elapsed

# ==================== Step 4: FFmpeg切片 ====================

def step4_segment(video_path, cu_segments, jing_segments):
    """FFmpeg切片"""
    print("\n" + "🔄"*25)
    print("Step 4: FFmpeg切片")
    print("🔄"*25)
    
    start = time.time()
    
    # 创建输出目录
    ensure_folder(CU_FOLDER)
    ensure_folder(JING_FOLDER)
    
    print(f"粗剪片段目录: {CU_FOLDER}")
    print(f"精剪片段目录: {JING_FOLDER}")
    
    # TODO: 根据分析结果切片
    # 伪代码：
    # for seg in cu_segments:
    #     ffmpeg_cut(video_path, seg.start, seg.end, f"{CU_FOLDER}/粗-{{seg.id}}.mp4")
    # for seg in jing_segments:
    #     ffmpeg_cut(video_path, seg.start, seg.end, f"{JING_FOLDER}/精-{{seg.id}}.mp4")
    
    print("⚠️ 切片逻辑待根据分析结果执行")
    
    elapsed = time.time() - start
    log_step(4, "FFmpeg切片", elapsed)
    
    return elapsed

# ==================== Step 5: 写入飞书 ====================

def step5_write_feishu(cu_segments, jing_segments):
    """写入飞书"""
    print("\n" + "🔄"*25)
    print("Step 5: 写入飞书")
    print("🔄"*25)
    
    start = time.time()
    
    print(f"飞书配置:")
    print(f"  app_token: {FEISHU_APP_TOKEN}")
    print(f"  table_id: {FEISHU_TABLE_ID}")
    
    # TODO: 调用飞书API写入记录
    # 伪代码：
    # for seg in cu_segments:
    #     feishu_create_record({
    #         "视频片段库": VIDEO_NAME,
    #         "分析类型": ["粗分析-粗剪"],
    #         "片段编号": f"粗-{seg.id}",
    #         ...
    #     })
    
    print("⚠️ 写入飞书逻辑待实现")
    
    elapsed = time.time() - start
    log_step(5, "写入飞书", elapsed)
    
    return elapsed

# ==================== Step 6: 自动拼接 ====================

def step6_auto_compose():
    """自动拼接"""
    print("\n" + "🔄"*25)
    print("Step 6: 自动拼接（粗剪版 + 精剪版）")
    print("🔄"*25)
    
    start = time.time()
    
    # TODO: 
    # 1. 读取粗剪片段，生成filelist
    # 2. ffmpeg拼接
    # 3. 烧录字幕
    # 4. 同样处理精剪片段
    
    print("⚠️ 自动拼接逻辑待实现")
    
    elapsed = time.time() - start
    log_step(6, "自动拼接", elapsed)
    
    return elapsed

# ==================== 主流程 ====================

def main():
    print("\n" + "🎬"*25)
    print("video-ai-process 正式开始!")
    print("🎬"*25)
    print(f"\n视频路径: {VIDEO_PATH}")
    print(f"视频名称: {VIDEO_NAME}")
    print(f"输出目录: {OUTPUT_BASE}")
    
    total_start = time.time()
    
    try:
        # Step 1: 转写
        transcript_path, srt_path, t1 = step1_transcribe(VIDEO_PATH)
        
        # Step 2: 粗剪分析
        cu_segments, t2 = step2_analyze_cu(transcript_path)
        
        # Step 3: 精剪分析
        jing_segments, t3 = step3_analyze_jing(transcript_path)
        
        # Step 4: 切片
        t4 = step4_segment(VIDEO_PATH, cu_segments, jing_segments)
        
        # Step 5: 写入飞书
        t5 = step5_write_feishu(cu_segments, jing_segments)
        
        # Step 6: 自动拼接
        t6 = step6_auto_compose()
        
        total_elapsed = time.time() - total_start
        
        print("\n" + "🎉"*25)
        print("video-ai-process 全部完成!")
        print("🎉"*25)
        print(f"\n📊 总耗时: {total_elapsed:.1f}秒")
        print(f"📁 输出目录: {OUTPUT_BASE}")
        print(f"\n💡 下一步:")
        print(f"   1. 查看飞书表格中的片段内容")
        print(f"   2. 如需自定义，在飞书打分")
        print(f"   3. 执行 Step 7 自定义拼接")
        
    except Exception as e:
        print(f"\n❌ 错误: {e}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)