Pythonで英語動画に英語字幕を自動生成してみた【Whisper × MoviePy × FFmpeg】


Unityの認定資格を取得しようと思い、学習バンドルを購入しました。
ところが手続きの際、たぶん「アメリカ人」として登録してしまったようで、
提供された動画コンテンツがすべて英語音声かつ字幕なしという状態になってしまいました。

英語のリスニングにも苦戦する中で、「せめて英語字幕があれば」と思い、
.mp4動画から自動で音声を文字起こしし、英語字幕付き動画を出力するPythonツールを作ってみました。

## 入力と出力
- 入力：英語音声の .mp4 動画
- 出力：英語字幕（SRT形式）を動画に焼き込んだ .mp4

## 使用ライブラリ
- openai-whisper: 音声をテキストに文字起こし（ローカル実行だと無料で使える）
- moviepy: 音声を抽出（mp4→wav）
- ffmpeg: 字幕（SRT）を動画に焼き込む

## セットアップ
仮想環境と依存パッケージ
```bash
# 仮想環境を作成
python -m venv venv
source venv/bin/activate  # Windowsは venv\Scripts\activate

# 必要パッケージをインストール
pip install openai-whisper moviepy ffmpeg-python
```

ffmpegのインストール
```bash
# macOS
brew install ffmpeg
```

## プロジェクト構成
```css
TranslateMovie/
├── input.mp4
├── en_subtitled_video_generator.py
├── output/
│   ├── audio.wav
│   ├── subtitles.srt
│   └── output_with_subs.mp4
```

## 仕組みの概要
1. mp4 → wav へ音声抽出（MoviePy）
2. Whisperで音声→英語テキストに変換
3. Whisperのセグメント情報をもとに SRT ファイルを作成
4. FFmpegで動画に英語字幕を焼き込み

## コア処理（コードの要点）
MP4 → WAV
```python
from moviepy.editor import VideoFileClip
video = VideoFileClip("input.mp4")
video.audio.write_audiofile("output/audio.wav")
```
Whisperで文字起こし
```python
import whisper
model = whisper.load_model("base")
result = model.transcribe("output/audio.wav")
```

SRTファイルを作成
```python
def format_timestamp(seconds):
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    ms = int((seconds - int(seconds)) * 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"

with open("output/subtitles.srt", "w", encoding="utf-8") as f:
    for i, seg in enumerate(result["segments"], start=1):
        f.write(f"{i}\n")
        f.write(f"{format_timestamp(seg['start'])} --> {format_timestamp(seg['end'])}\n")
        f.write(f"{seg['text']}\n\n")
```
FFmpegで字幕を動画に焼き込み
```bash
ffmpeg -i input.mp4 -vf "subtitles=output/subtitles.srt" -c:a copy output/output_with_subs.mp4
```

## 実行スクリプト
ルートディレクトリにinput.mp4を配置して、以下のスクリプトを実行すると英語字幕付きの動画ができあがるはずです。
```python
import os
import whisper
from moviepy.editor import VideoFileClip
import subprocess

# 設定
input_video_path = "input.mp4"
extracted_audio_path = "output/audio.wav"
output_srt_path = "output/subtitles.srt"
output_video_path = "output/output_with_subs.mp4"

# 前に作成した動画などの削除処理
for path in [extracted_audio_path, output_srt_path, output_video_path]:
    if os.path.exists(path):
        os.remove(path)
        print(f"🧹 削除しました: {path}")

# タイムスタンプのフォーマット変換（Whisperの時間 → SRT形式）
def format_timestamp(seconds: float) -> str:
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    ms = int((seconds - int(seconds)) * 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"

# ステップ1：mp4 → wav
print("🎞️ MP4 → WAV に変換中...")
video = VideoFileClip(input_video_path)
video.audio.write_audiofile(extracted_audio_path)

# ステップ2：WAV を Whisper で文字起こし
print("🗣 Whisper による英語文字起こし中...")
model = whisper.load_model("base")
result = model.transcribe(extracted_audio_path, task="transcribe")

# ステップ3：SRTファイルとして保存
print("💬 SRT字幕を保存中...")
with open(output_srt_path, "w", encoding="utf-8") as f:
    for i, segment in enumerate(result["segments"]):
        start = segment["start"]
        end = segment["end"]
        text = segment["text"]
        f.write(f"{i+1}\n")
        f.write(f"{format_timestamp(start)} --> {format_timestamp(end)}\n")
        f.write(f"{text}\n\n")

# ステップ4：ffmpegで字幕を動画に焼き付け
print("📽 字幕を動画に焼き込み中...")
subprocess.run([
    "ffmpeg",
    "-i", input_video_path,
    "-vf", f"subtitles={output_srt_path}:force_style='FontName=NotoSansCJKjp'",
    "-c:a", "copy",
    output_video_path
])

print("✅ 完了！字幕付き動画：", output_video_path)

```

## おわり
最後まで読んでいただきありがとうございました。
英語字幕があるだけでもありがたいのですが、まだえいごだけだt理解しづらいところがあるので
次は日本語字幕バージョンを作ってみようと思います。