politicsforpeace/social-impact-translator
0
Social Impact Video Translator
社会問題を扱う海外の動画に、自然な日本語字幕を焼き付けてSNSで共有するためのWebアプリです。
機能
- 動画ファイルのドラッグ&ドロップ / YouTube等のURL入力
- yt-dlpによる動画取得(最高画質MP4)
- Gemini(既定: gemini-2.5-flash、思考無効化済み)による日本語字幕生成
- 1行最大18文字・最大2行・最大7秒、フィラー除去、平易で誠実な翻訳
- FFmpegによる字幕焼き付け(Noto Sans JP Bold・半透明黒の座布団・下から20%配置)
- SNS向け最適化(libx264 / CRF23〜28自動調整で30MB以下 / yuv420p / aac 128k / faststart)
- ジョブベースの非同期処理とリアルタイム進捗表示
- 完了後30分でサーバー上の一時ファイルを自動削除
セットアップ(ローカル開発)
npm install
cp .env.local.example .env.local # GEMINI_API_KEYを記入
npm run dev # http://localhost:3000ローカルでは ffmpeg-static(npm)と PATH 上の yt-dlp を使用します。 FFMPEG_PATH / YTDLP_PATH 環境変数で任意のバイナリに差し替え可能です。
Docker(本番想定)
# .env.local に GEMINI_API_KEY を設定した上で
docker compose up --build- イメージには ffmpeg / yt-dlp / Noto Sans CJK フォントが同梱されます(
FFMPEG_PATH=/usr/bin/ffmpegでシステム側を優先) - コンテナ起動時に
yt-dlp -Uで最新版へ自己更新します(entrypoint.sh) ./tmpがコンテナの/app/tmpにマウントされ、処理中ファイルをホストから確認できます- APIキーはイメージに焼き込まれず、
env_file(.env.local)から実行時に注入されます
Cloud Run 運用時の注意(Statelessness)
本アプリは ジョブ状態をメモリ内(インスタンスローカル)、動画ファイルを コンテナ内の `tmp/` に保持します。このため Cloud Run では以下の制約があります。
- `max-instances=1` が必須(service.yaml で設定済み)。 複数インスタンスに分散すると、ジョブ作成とポーリングが別インスタンスに 振り分けられ「ジョブが見つかりません」となります
- インスタンスの再起動・入れ替えで処理中ジョブと完成動画は消えます。 ユーザーには再実行してもらう前提のMVP構成です
- 第2世代実行環境(gen2)ではファイルがメモリを消費します。対策として 焼き付け完了時に元動画を即削除し、完成動画も
JOB_TTL_MINUTES(既定30分、 service.yamlでは10分に短縮)で自動削除します。4Giメモリで同時数ジョブが目安です - CPU常時割り当て(
cpu-throttling: false)が必要です。バックグラウンドの 動画処理がリクエスト外で走るためで、これを止めるとジョブが進まなくなります
スケールが必要になった場合は、ジョブ状態を Firestore/Redis、動画を GCS、 処理を Cloud Tasks + Cloud Run Jobs に分離する構成への移行を検討してください。
デプロイ(Cloud Run)
# 1. イメージをビルドしてArtifact Registryへ
gcloud builds submit --tag asia-northeast1-docker.pkg.dev/PROJECT_ID/app/social-impact-translator
# 2. APIキーをSecret Managerへ(初回のみ)
gcloud secrets create gemini-api-key --data-file=- <<< "新しいAPIキー"
# 3. service.yaml の PROJECT_ID を書き換えてデプロイ
gcloud run services replace service.yaml --region asia-northeast1セキュリティ上の注意
GEMINI_API_KEYは必ず.env.local(gitignore済み)または実行環境の環境変数で管理してください- キーが漏えいした可能性がある場合は Google AI Studio で即時ローテーション(削除→再発行)してください
アーキテクチャ
POST /api/jobs ジョブ作成(URL or ファイル)→ 即jobId返却
GET /api/jobs/[jobId] 進捗ポーリング(ステージ + %)
GET /api/jobs/[jobId]/download 完成動画の取得(?dl=1で添付DL)
src/lib/
├── jobs/ ジョブストア(メモリ内)+ パイプライン
├── video/ yt-dlpダウンローダー / FFmpeg焼き付け / 解像度プローブ
├── gemini/ 字幕生成(Files API + 構造化出力)/ ASSビルダー
└── utils/ 一時フォルダ管理(30分TTL)