2026年03月01日 yt-dlpでダウンロードした字幕ファイルをChatGPTの助けを借りたpythonスクリプトで日本語に翻訳 [長年日記]
_ とある外国映画のフルサイズ動画をyt-dlpで取得。日本語字幕に対応していなかったのでChatGPTに相談したら、「あなたにはpythonスクリプトを使った方法をおすすめ...」とアドバイスされた。
_ youtubeからyt-dlpを使って動画と字幕ファイルをダウンロード
字幕でサポートされる言語を確認。
$ yt-dlp --list-subs 'https://www.youtube.com/... snip ...'
動画と字幕ファイルをダウンロード
$ yt-dlp --write-subs --sub-langs "en.*" --convert-subs srt 'https://www.youtube.com/... snip ...'
字幕ファイルが、動画ファイルと共に拡張子「srt」でダウンロードされる。
_ pythonからgoogle翻訳を呼び出して字幕ファイルを日本語に翻訳
作業ディレクトリを用意
$ mkdir srt_translate $ cd srt_translate
pythonの仮想環境を準備
$ python3 -m venv venv
pythonの仮想環境を有効化
$ source venv/bin/activate
必要なモジュールをインストール
$ pip install pysrt deep-translator
インストールされたモジュールを確認
$ pip list Package Version ------------------ ----------- beautifulsoup4 4.14.3 certifi 2026.2.25 chardet 6.0.0.post1 charset-normalizer 3.4.4 deep-translator 1.11.4 idna 3.11 pip 26.0.1 pysrt 1.1.2 requests 2.32.5 soupsieve 2.8.3 typing_extensions 4.15.0 urllib3 2.6.3
翻訳pythonスクリプトを準備。ChatGPTいわく「netflixレベル」とのこと...
$ vi translate_netflix.py
# --- 必要なライブラリの読み込み ---
import pysrt # SRT字幕ファイルの読み込み・編集・保存用
import json # 翻訳結果をキャッシュとして保存するため
import os # ファイルの存在確認などのOS操作
import time # API連続アクセスを避けるための待機処理
from deep_translator import GoogleTranslator # Google翻訳を利用するライブラリ
# --- 入出力ファイル設定 ---
INPUT_FILE = "input.srt" # 英語字幕ファイル
OUTPUT_FILE = "output_netflix_ja.srt" # 日本語字幕の出力先
CACHE_FILE = "cache.json" # 翻訳結果を保存するキャッシュファイル
# --- 動作パラメータ(調整可能) ---
BATCH_SIZE = 40 # まとめて翻訳する字幕行数(大きいほど高速)
SLEEP_TIME = 1 # バッチごとに待機する秒数(API制限回避)
MAX_CHARS_PER_LINE = 15 # 1行あたり最大文字数(放送基準)
MAX_LINES = 2 # 字幕は最大2行まで
CHARS_PER_SECOND = 4 # 1秒あたり読める文字数(放送字幕の目安)
# --- 翻訳エンジン初期化(英語→日本語) ---
translator = GoogleTranslator(source='en', target='ja')
# --- キャッシュ読み込み ---
# 既に翻訳した文章は再翻訳しない(高速化+API負荷軽減)
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "r", encoding="utf-8") as f:
cache = json.load(f)
else:
cache = {} # キャッシュが無ければ空辞書
# --- キャッシュ保存関数 ---
# 翻訳途中で止まっても再開できるよう、随時保存
def save_cache():
with open(CACHE_FILE, "w", encoding="utf-8") as f:
json.dump(cache, f, ensure_ascii=False)
# --- 字幕表示時間(秒)を計算する関数 ---
# SRTの開始時刻と終了時刻から表示秒数を求める
def duration_seconds(sub):
return (sub.end.ordinal - sub.start.ordinal) / 1000.0
# --- 放送・配信レベルの字幕整形関数 ---
# ・口語化
# ・文字数制限
# ・最大2行
# ・読みやすい改行
def format_subtitle(text, max_chars):
# --- 簡易口語化(字幕向けに簡潔に) ---
text = text.replace("私は", "")
text = text.replace("です。", "。")
text = text.replace("ます。", "。")
# --- 改行候補を作成(句読点ごと) ---
parts = text.replace("、", "、\n").replace("。", "。\n").split("\n")
lines = []
# --- 各パートを最大文字数以内に分割 ---
for part in parts:
while len(part) > max_chars:
lines.append(part[:max_chars])
part = part[max_chars:]
if part:
lines.append(part)
# --- 行数が多すぎる場合は2行にまとめる ---
if len(lines) > MAX_LINES:
half = len(lines) // 2
lines = [
"".join(lines[:half]),
"".join(lines[half:])
]
# 最大2行まで返す
return "\n".join(lines[:MAX_LINES])
# --- SRTファイル読み込み ---
print("Loading...")
subs = pysrt.open(INPUT_FILE, encoding='utf-8')
total = len(subs)
print("Total:", total)
# --- バッチ処理用の一時領域 ---
batch = [] # 翻訳待ちテキスト
batch_index = [] # 元の字幕番号
# --- メイン処理ループ ---
for i, sub in enumerate(subs):
# 改行をスペースに変換(翻訳エンジン対策)
text = sub.text.replace("\n", " ")
# --- 表示時間に応じた最大文字数を計算 ---
duration = duration_seconds(sub)
# 字幕全体で表示できる最大文字数
max_chars_total = int(duration * CHARS_PER_SECOND)
# 下限10文字、上限30文字(15×2行)
max_chars_total = max(10, min(max_chars_total, MAX_CHARS_PER_LINE * MAX_LINES))
# 1行あたりの文字数
max_chars_per_line = max_chars_total // MAX_LINES
# --- キャッシュにあれば翻訳せず使用 ---
if text in cache:
subs[i].text = format_subtitle(cache[text], max_chars_per_line)
continue
# --- バッチに追加 ---
batch.append((text, max_chars_per_line))
batch_index.append(i)
# --- 一定数たまったらまとめて翻訳 ---
if len(batch) >= BATCH_SIZE:
# テキストを改行区切りで結合
joined = "\n".join([b[0] for b in batch])
try:
# 一括翻訳(高速化)
translated = translator.translate(joined)
# 結果を行ごとに分割
results = translated.split("\n")
# 元の字幕に戻す
for (src, maxc), idx, dst in zip(batch, batch_index, results):
cache[src] = dst
subs[idx].text = format_subtitle(dst, maxc)
# キャッシュ保存
save_cache()
print(f"{i+1}/{total}")
except Exception as e:
print("Error:", e)
# バッチをリセット
batch = []
batch_index = []
# API負荷軽減
time.sleep(SLEEP_TIME)
# --- 残りのバッチ処理 ---
if batch:
joined = "\n".join([b[0] for b in batch])
translated = translator.translate(joined)
results = translated.split("\n")
for (src, maxc), idx, dst in zip(batch, batch_index, results):
cache[src] = dst
subs[idx].text = format_subtitle(dst, maxc)
save_cache()
# --- 日本語字幕として保存 ---
print("Saving...")
subs.save(OUTPUT_FILE, encoding='utf-8')
print("Done")
翻訳前の字幕ファイルを準備
$ cp ~/... snip .../ダウンロードした字幕ファイル.srt input.srt
翻訳スクリプトを実行
$ python translate_netflix.py
変換結果が「output_netflix_ja.srt」に出力される。
変換が途中で停止しても
- cache.json に保存
- 再実行すると未翻訳分だけ処理
とのこと。
python仮想環境の無効化
$ deactivate
_ ffmpegを使って動画に字幕ファイルを合成
$ ffmpeg -i ダウンロードした動画ファイル.mp4 -vf subtitles=output_netflix_ja.srt 字幕ファイル合成後の動画ファイル.mp4
翻訳された結果が直訳されたっぽい違和感のあるものだったので、さらにChatGPTに相談したところ、ローカルAI環境を構築する方法を提案してきた...こちらの話はまた後ほど。
[ツッコミを入れる]