Skill: ivan830826/audio-video-to-text (version 1.0.0)

--- name: audio-video-to-text description: 音视频转文字技能，使用 Whisper 进行语音识别。支持多种音视频格式，可输出纯文本、SRT/VTT 字幕或 JSON 格式。适用于会议记录、视频字幕生成、采访整理、播客转录等场景。 --- # 音视频转文字 ## 概述 本技能使用 OpenAI Whisper 模型将音频/视频文件转换为文字。支持自动语言检测和多种输出格式。 ## 何时使用 - 会议录音转文字记录 - 视频内容生成字幕（SRT/VTT） - 采访/播客内容整理 - 语音备忘录转文本 - 多语言视频翻译准备 ## 快速开始 ### 1. 安装依赖 ```bash pip install openai-whisper ffmpeg-python ``` 确保系统已安装 ffmpeg： ```bash # Ubuntu/Debian sudo apt-get install ffmpeg # macOS brew install ffmpeg # Windows # 从 https://ffmpeg.org/download.html 下载 ``` ### 2. 基本用法 ```bash python scripts/transcribe.py <输入文件> [输出文件] [选项] ``` ### 3. 示例 ```bash # 转录 MP4 视频，输出文本 python scripts/transcribe.py meeting.mp4 # 转录音频，输出 SRT 字幕 python scripts/transcribe.py podcast.mp3 podcast.srt --output-format srt # 指定中文和较小模型（更快） python scripts/transcribe.py interview.wav --model tiny --language zh # 输出带时间戳的 JSON python scripts/transcribe.py video.mp4 result.json --output-format json ``` ## 命令行选项 | 选项 | 说明 | 默认值 | |------|------|--------| | `--model` | 模型大小：tiny, base, small, medium, large | base | | `--language` | 语言代码：zh, en, ja 等 | 自动检测 | | `--output-format` | 输出格式：txt, srt, vtt, json | txt | | `--device` | 运行设备：cpu, cuda | cpu | | `--keep-audio` | 保留临时音频文件 | false | ## 模型选择指南 | 模型 | 大小 | 速度 | 精度 | 适用场景 | |------|------|------|------|----------| | tiny | 39M | 最快 | 一般 | 快速测试、短音频 | | base | 74M | 快 | 良好 | 日常使用 | | small | 244M | 中等 | 较好 | 正式场合 | | medium | 769M | 慢 | 很好 | 高精度需求 | | large | 1550M | 最慢 | 最佳 | 专业转录 | ## 输出格式说明 ### TXT（纯文本） ``` 这是转录的完整文本内容，适合阅读和编辑。 ``` ### SRT（字幕格式） ``` 1 00:00:01,000 --> 00:00:04,000 这是第一句字幕。 2 00:00:04,500 --> 00:00:07,000 这是第二句字幕。 ``` ### VTT（Web 字幕） ``` WEBVTT 00:00:01.000 --> 00:00:04.000 这是第一句字幕。 00:00:04.500 --> 00:00:07.000 这是第二句字幕。 ``` ### JSON（完整数据） 包含分段、时间戳、置信度等完整信息，适合程序处理。 ## 支持的文件格式 **音频：** MP3, WAV, FLAC, OGG, M4A, AAC **视频：** MP4, AVI, MOV, MKV, WEBM, FLV ## 性能优化建议 1. **短音频优先用 tiny/base 模型** - 速度快，精度够用 2. **长内容用 CPU** - 避免 GPU 内存不足 3. **指定语言** - 可提升准确率和速度 4. **批量处理** - 脚本可循环调用处理多个文件 ## 常见问题 ### 转录质量不佳 - 尝试更大的模型（small/medium/large） - 指定正确的语言代码 - 确保音频质量清晰 ### 处理速度慢 - 使用更小的模型（tiny/base） - 如有 GPU，使用 `--device cuda` - 缩短音频长度或分段处理 ### 内存不足 - 使用更小的模型 - 将长文件分割后分别处理 - 关闭其他占用内存的程序 ## 脚本 - `scripts/transcribe.py` - 主转录脚本 ## 参考资料 - [OpenAI Whisper 文档](https://github.com/openai/whisper) - [ffmpeg 文档](https://ffmpeg.org/documentation.html) - [ISO 639-1 语言代码](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)

path: scripts/transcribe.py
content: #!/usr/bin/env python3
"""
音视频转文字脚本
使用 OpenAI Whisper 进行语音识别

用法:
    python transcribe.py <输入文件> [输出文件] [选项]

选项:
    --model <模型名>     选择模型：tiny, base, small, medium, large (默认：base)
    --language <语言>    指定语言代码，如 zh, en, ja (默认：自动检测)
    --output-format     输出格式：txt, srt, vtt, json (默认：txt)
    --device <设备>      运行设备：cpu, cuda (默认：cpu)

依赖:
    pip install openai-whisper ffmpeg-python
"""

import argparse
import os
import sys
import subprocess
import json
from pathlib import Path


def check_dependencies():
    """检查必要的依赖是否已安装"""
    missing = []
    
    try:
        import whisper
    except ImportError:
        missing.append("openai-whisper")
    
    try:
        import ffmpeg
    except ImportError:
        missing.append("ffmpeg-python")
    
    if missing:
        print(f"错误：缺少依赖项：{', '.join(missing)}")
        print(f"请运行：pip install {' '.join(missing)}")
        sys.exit(1)
    
    # 检查 ffmpeg 是否安装
    try:
        subprocess.run(["ffmpeg", "-version"], capture_output=True, check=True)
    except (subprocess.CalledProcessError, FileNotFoundError):
        print("错误：ffmpeg 未安装")
        print("请安装 ffmpeg:")
        print("  Ubuntu/Debian: sudo apt-get install ffmpeg")
        print("  macOS: brew install ffmpeg")
        print("  Windows: 从 https://ffmpeg.org/download.html 下载")
        sys.exit(1)


def extract_audio(input_file, temp_audio="/tmp/audio_extract.wav"):
    """从视频文件中提取音频"""
    try:
        subprocess.run([
            "ffmpeg", "-i", input_file,
            "-vn", "-acodec", "pcm_s16le",
            "-ar", "16000", "-ac", "1",
            "-y", temp_audio
        ], check=True, capture_output=True)
        return temp_audio
    except subprocess.CalledProcessError as e:
        print(f"音频提取失败：{e.stderr.decode()}")
        sys.exit(1)


def transcribe(audio_file, model_name="base", language=None, device="cpu"):
    """执行语音识别"""
    import whisper
    
    print(f"加载模型：{model_name}...")
    model = whisper.load_model(model_name, device=device)
    
    print("开始转录...")
    options = {}
    if language:
        options["language"] = language
    
    result = model.transcribe(audio_file, **options)
    return result


def format_output(result, output_format="txt", output_file=None):
    """格式化输出结果"""
    if output_format == "txt":
        text = result["text"]
        if output_file:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(text)
            print(f"文本已保存到：{output_file}")
        else:
            print(text)
        return text
    
    elif output_format == "srt":
        srt_content = ""
        for i, segment in enumerate(result["segments"], 1):
            start = format_time_srt(segment["start"])
            end = format_time_srt(segment["end"])
            text = segment["text"].strip()
            srt_content += f"{i}\n{start} --> {end}\n{text}\n\n"
        
        if output_file:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(srt_content)
            print(f"SRT 字幕已保存到：{output_file}")
        else:
            print(srt_content)
        return srt_content
    
    elif output_format == "vtt":
        vtt_content = "WEBVTT\n\n"
        for segment in result["segments"]:
            start = format_time_vtt(segment["start"])
            end = format_time_vtt(segment["end"])
            text = segment["text"].strip()
            vtt_content += f"{start} --> {end}\n{text}\n\n"
        
        if output_file:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(vtt_content)
            print(f"VTT 字幕已保存到：{output_file}")
        else:
            print(vtt_content)
        return vtt_content
    
    elif output_format == "json":
        if output_file:
            with open(output_file, "w", encoding="utf-8") as f:
                json.dump(result, f, ensure_ascii=False, indent=2)
            print(f"JSON 结果已保存到：{output_file}")
        else:
            print(json.dumps(result, ensure_ascii=False, indent=2))
        return result
    
    else:
        print(f"不支持的输出格式：{output_format}")
        sys.exit(1)


def format_time_srt(seconds):
    """将秒数转换为 SRT 时间格式"""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = int(seconds % 60)
    millis = int((seconds % 1) * 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"


def format_time_vtt(seconds):
    """将秒数转换为 VTT 时间格式"""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = int(seconds % 60)
    millis = int((seconds % 1) * 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d}.{millis:03d}"


def main():
    parser = argparse.ArgumentParser(description="音视频转文字工具")
    parser.add_argument("input", help="输入的音视频文件路径")
    parser.add_argument("output", nargs="?", help="输出文件路径（可选）")
    parser.add_argument("--model", default="base", 
                        choices=["tiny", "base", "small", "medium", "large"],
                        help="Whisper 模型大小（默认：base）")
    parser.add_argument("--language", help="语言代码，如 zh, en, ja（默认：自动检测）")
    parser.add_argument("--output-format", default="txt",
                        choices=["txt", "srt", "vtt", "json"],
                        help="输出格式（默认：txt）")
    parser.add_argument("--device", default="cpu",
                        choices=["cpu", "cuda"],
                        help="运行设备（默认：cpu）")
    parser.add_argument("--keep-audio", action="store_true",
                        help="保留临时音频文件")
    
    args = parser.parse_args()
    
    # 检查输入文件
    if not os.path.exists(args.input):
        print(f"错误：文件不存在：{args.input}")
        sys.exit(1)
    
    # 检查依赖
    check_dependencies()
    
    # 确定输出文件
    if args.output:
        output_file = args.output
    else:
        base_name = Path(args.input).stem
        ext = args.output_format
        output_file = f"{base_name}.{ext}"
    
    # 检查是否需要提取音频
    input_ext = Path(args.input).suffix.lower()
    audio_extensions = [".wav", ".mp3", ".flac", ".ogg", ".m4a", ".aac"]
    
    if input_ext in audio_extensions:
        audio_file = args.input
    else:
        print("检测到视频文件，正在提取音频...")
        audio_file = extract_audio(args.input)
    
    try:
        # 执行转录
        result = transcribe(
            audio_file,
            model_name=args.model,
            language=args.language,
            device=args.device
        )
        
        # 输出结果
        format_output(result, args.output_format, output_file)
        
    finally:
        # 清理临时文件
        if audio_file != args.input and not args.keep_audio:
            try:
                os.remove(audio_file)
            except:
                pass


if __name__ == "__main__":
    main()


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