字幕自動化 _ Whisper × Deep Translator × FFmpeg 字幕焼き込みを Colab で全自動化する方法

***

# Whisper × Deep Translator × FFmpeg 字幕焼き込みを Colab で全自動化する方法

最近、OpenAI の **Whisper** を使った動画文字起こしが注目されています。英語の動画を日本語に翻訳したいケースも多いですよね。そこで今回は、**Google Colab** を使って一気に以下を行うサンプルを紹介します。

1.  **Whisper** で動画から英語文字起こし
2.  **Deep Translator** で日本語に翻訳
3.  **SRT → ASS → FFmpeg で字幕を動画に焼き込み**
4.  **日本語はもちろん、英数字も見やすいフォントに**
5.  **Colab で完結して手軽！**

## 前提

*   Google Colab の環境を想定
*   `video.mp4` のような動画ファイルがダウンロード可能（あるいは Colab にアップロード済み）
*   実行時に、依存ライブラリがインストールされるのを待つため、多少の時間がかかります

## コード全文

以下を **Colab のセル** にコピー＆ペーストすれば、すべてのステップが完了します。

```python
################################################################################
#  0) Whisper キャッシュクリア (推奨)
#     うまくいかない場合でもキャッシュ削除すると再ダウンロードが走り、
#     SSLエラー等が解決することがあります
################################################################################
!rm -rf ~/.cache/whisper


################################################################################
#  1) 必要なライブラリをインストール
################################################################################
!pip install yt-dlp
!pip install ffmpeg-python
!pip install whisper
!pip install deep-translator

# 古い whisper を削除 → 最新版を GitHub から導入 (任意)
!pip uninstall -y whisper
!pip install git+https://github.com/openai/whisper.git

# 日本語フォント (Noto Sans JP) および Noto Sans をインストール
!apt-get install -y fonts-noto
!apt-get install -y fonts-noto-cjk

import subprocess
import yt_dlp
import whisper
from deep_translator import GoogleTranslator


################################################################################
#  2) 動画をダウンロード (例: X/Twitter 動画)
#
#  今回は Twitter の URL を例示していますが、
#  すでに mp4 があるなら、このステップはスキップ可。
################################################################################
twitter_url = "https://x.com/dfinity/status/1870061438384959498"  # 例: Twitter の動画URL
video_filename = "video.mp4"

ydl_opts = {
    'format': 'best',
    'outtmpl': video_filename
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download([twitter_url])

print("動画がダウンロードされました:", video_filename)


################################################################################
#  3) Whisper で英語文字起こし
################################################################################
model = whisper.load_model("base")
result = model.transcribe(video_filename)
print("【Whisper英語文字起こし】\n", result['text'])


################################################################################
#  4) Deep Translator で英語 → 日本語 翻訳
################################################################################
segments = result['segments']
translated_texts = []

for seg in segments:
    en_text = seg["text"].replace("\n", " ")
    try:
        ja_text = GoogleTranslator(source="en", target="ja").translate(en_text)
    except Exception as e:
        print("翻訳エラー:", e)
        ja_text = "[翻訳エラー]"
    translated_texts.append(ja_text)


################################################################################
#  5) SRTファイル作成 (日本語向けに行長をある程度長めに設定)
################################################################################
def format_timestamp(seconds):
    hrs, secs = divmod(seconds, 3600)
    mins, secs = divmod(secs, 60)
    millis = int((secs - int(secs)) * 1000)
    return f"{int(hrs):02}:{int(mins):02}:{int(secs):02},{millis:03}"

def wrap_text_japanese(text, max_len=30):
    """
    日本語向けに、一定文字数ごとに強制的に改行する例。
    今回は 1 行を 30 文字に設定。
    """
    if not text:
        return ""
    lines = []
    current_line = ""
    for char in text:
        current_line += char
        # 30文字たまったら改行
        if len(current_line) >= max_len:
            lines.append(current_line)
            current_line = ""
    if current_line:
        lines.append(current_line)

    return "\n".join(lines)

def create_srt(segments, texts, output_file="subtitles.srt", max_line_length=30):
    with open(output_file, 'w', encoding='utf-8') as f:
        for i, (segment, text) in enumerate(zip(segments, texts)):
            start, end = segment['start'], segment['end']
            wrapped_text = wrap_text_japanese(text, max_line_length)

            f.write(f"{i+1}\n")
            f.write(f"{format_timestamp(start)} --> {format_timestamp(end)}\n")
            f.write(f"{wrapped_text}\n\n")

srt_file = "subtitles.srt"
create_srt(segments, translated_texts, srt_file)
print("SRTファイルを作成しました:", srt_file)


################################################################################
#  6) SRT → ASS 変換
################################################################################
def convert_srt_to_ass(srt_file, ass_file):
    cmd = [
        "ffmpeg", "-y",
        "-f", "srt",
        "-i", srt_file,
        "-c:s", "ass",
        ass_file
    ]
    try:
        subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print(f"SRT to ASS conversion successful: {ass_file}")
    except subprocess.CalledProcessError as e:
        print("Error during conversion:", e.stderr.decode("utf-8"))

ass_file = "subtitles.ass"
convert_srt_to_ass(srt_file, ass_file)


################################################################################
#  7) ASS 字幕を焼き込む (WrapStyle=2 で自動折り返し)
################################################################################
output_video = "output_video.mp4"

cmd = [
    "ffmpeg", "-y",
    "-i", video_filename,
    "-vf",
    (
        "subtitles=subtitles.ass:"
        "force_style='FontName=Noto Sans,FontSize=20,"
        "MarginL=100,MarginR=100,WrapStyle=2'"
    ),
    "-c:a", "copy",
    output_video
]

try:
    subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print("字幕を焼き込んだ動画を生成しました:", output_video)
except subprocess.CalledProcessError as e:
    print("Error during video processing:", e.stderr.decode("utf-8"))


################################################################################
#  8) Colab から ダウンロード (任意)
################################################################################
# from google.colab import files
# files.download(output_video)

print("\nすべての処理が完了しました。")
```

## コードの解説

1.  **Whisper のキャッシュ削除**
    *   `!rm -rf ~/.cache/whisper` で、キャッシュ破損などを避けるために毎回削除します。

2.  **ライブラリインストール**
    *   `yt-dlp`, `ffmpeg-python`, `whisper`, `deep-translator` を導入
    *   Whisper の既存バージョンをアンインストール → GitHub 最新版を入れているのは、Colab でエラーを回避するための対策です

3.  **日本語フォントをインストール**
    *   `Noto Sans`, `Noto Sans JP` は日本語・英語・数字も幅広く対応

4.  **動画ダウンロード**
    *   ここでは `yt-dlp` を使い、Twitter(X) の動画を例示しています
    *   すでにローカルに mp4 があればこのパートはスキップして構いません

5.  **Whisper (base) モデルで英語文字起こし**
    *   大きなモデルもありますが、Colab で試すには `base` が軽量で安定です

6.  **Deep Translator で翻訳**
    *   文字起こし結果の英語を API で日本語に変換
    *   無料枠内で実行できる範囲を超えると制限される場合もあります

7.  **SRT 作成 + 日本語ラッピング**
    *   `wrap_text_japanese()` 関数で「30文字たまったら改行」という単純なロジック
    *   全角・半角が混在すると行幅は多少前後しますが、実用としては十分です

8.  **SRT → ASS 変換 → FFmpeg で焼き込み**
    *   `ffmpeg` に `-vf subtitles=...:force_style='...'` を指定することで
        *   フォント (`Noto Sans`)、フォントサイズ (20)、左右マージン (100)、WrapStyle=2 (自動折り返し) を強制しています

9.  **結果ダウンロード** (オプション)
    *   Colab 上で生成した `output_video.mp4` をダウンロードする場合は、`google.colab.files.download(output_video)` を利用

## うまくいかない時のヒント

*   **Twitter の動画 URL が無効の場合**
    *   そのまま `yt-dlp` でエラーが出るので、ほかの動画リンクを試す
*   **Whisper ダウンロード時に SSL エラー**
    *   キャッシュ削除や Colab ランタイム再起動を試す
*   **翻訳 API の制限**
    *   エラーになった場合は翻訳回数の制限を超えている可能性があるので、DeepL 等 別の翻訳手段を探す

## まとめ

このように Google Colab 上でスクリプトを一気に実行すれば、英語字幕から日本語訳まで**自動で**作成して、最終的には動画へ焼き込みまでしてくれます。日本語は文字幅があるため**手動で適度に改行した方が見やすい**ことも多いですが、今回のスクリプトでも簡単な整形はカバーできるはずです。\
ぜひ使いやすいようにカスタマイズしてみてください。

***