個人でコンテンツを量産しようとしたとき、最初にぶつかる壁は「動画の中身と音楽のクオリティをどう両立するか」ではないかと思います。私も以前は動画生成AIと音楽生成AIをそれぞれ手動で使い、その後 ffmpeg で合成するという作業を繰り返していました。それが週に数本なら耐えられますが、毎日10本・20本となると人間の手が追いつかなくなります。
Veo 3 と Lyria 3 Pro が API として使えるようになったことで、この問題に対するアプローチが大きく変わりました。両方とも Google Gen AI SDK 経由でアクセスでき、Pythonコードだけで「プロンプト入力 → 動画生成 → 音楽生成 → 合成 → 保存」という流れを完全に自動化できます。
ただし、実際に動くパイプラインを作るまでには、ドキュメントには書かれていない落とし穴がいくつかあります。ここで扱うのは私が実際に本番環境で運用しているパイプラインのコードと、そこに至るまでに詰まったポイントを余すところなく共有します。
Veo 3 と Lyria 3 Pro の API を使う前に知っておくべきこと
Veo 3 は Google の動画生成モデルで、テキストプロンプトまたは画像から最大8秒の動画を生成できます。モデル名は veo-3.0-generate-preview で、Google Gen AI SDK の client.models.generate_video() から呼び出します。重要なのは、レスポンスが即座に返るのではなく、Operation(非同期ジョブ)として返ってくる点です。ポーリングして完了を待つ必要があります。
Lyria 3 Pro は Google の音楽生成モデルです。テキストで音楽のジャンルや雰囲気、テンポ、楽器などを指定すると、最大3分30秒の音楽トラックを生成できます。こちらも非同期処理で、完了までに数十秒かかることがあります。
この2つを組み合わせるパイプラインを作る際に最初に確認しておきたいのは、それぞれのAPIが別々のクォータを持っている という点です。Veo 3 の生成可能本数と Lyria の生成可能本数は独立しており、片方のクォータが枯渇してももう片方は動き続けます。この前提をもとにパイプラインを設計することが、安定した運用につながります。
2026年4月時点での主な仕様です。
Veo 3 : 1リクエストあたり最大8秒・アスペクト比 16:9 または 9:16 に対応・生成コストは解像度と秒数に応じた従量制
Lyria 3 Pro : 1リクエストあたり最大210秒(3分30秒)・WAVまたはMP3形式・ステレオ出力・音楽の詳細なスタイル制御が可能
パイプライン全体のアーキテクチャ
構築するパイプラインの全体像はシンプルです。
入力 : コンテンツのテーマを記述したプロンプト(動画用と音楽用を別に用意)
処理1 : Veo 3 API で動画ファイルを生成・ダウンロード
処理2 : Lyria 3 Pro API で音楽ファイルを生成・ダウンロード
処理3 : ffmpeg で動画と音楽を合成、音量バランスを調整
出力 : 完成した動画ファイルを指定フォルダに保存
各処理は非同期で動かすことで、Veo 3 の生成待ち時間中に Lyria の生成を並行実行できます。実測では、同期処理に比べて合計待ち時間が約40%短縮できています。
エラーが発生した場合は自動リトライ(最大3回、指数バックオフ付き)を行い、それでも失敗した場合はログに記録して次のアイテムに進む設計にしています。これにより、夜中に無人でパイプラインを動かしても安心です。
環境構築と認証設定
必要なパッケージをインストールします。
# 必要なパッケージをインストール
pip install google-genai python-dotenv ffmpeg-python
# ffmpeg本体もインストールが必要(macOSの場合)
brew install ffmpeg
# Linuxの場合
# apt-get install -y ffmpeg
次に、プロジェクトの認証設定です。Google AI Studio から API キーを取得し、.env ファイルに保存します。
# config.py — 設定と認証の初期化
import os
from dotenv import load_dotenv
from google import genai
load_dotenv()
def get_client () -> genai.Client:
"""
Google Gen AI クライアントを初期化して返す。
API キーは環境変数 GOOGLE_AI_API_KEY から読み込む。
"""
api_key = os.getenv( "GOOGLE_AI_API_KEY" )
if not api_key:
raise ValueError (
"GOOGLE_AI_API_KEY が設定されていません。"
".env ファイルに GOOGLE_AI_API_KEY=your_key_here を追加してください。"
)
return genai.Client( api_key = api_key)
# モデル名定数
VEO3_MODEL = "veo-3.0-generate-preview"
LYRIA3_MODEL = "lyria-3.0-pro"
# 出力ディレクトリ
OUTPUT_DIR = "output"
TEMP_DIR = "temp"
os.makedirs( OUTPUT_DIR , exist_ok = True )
os.makedirs( TEMP_DIR , exist_ok = True )
.env ファイルの中身は以下の通りです。
GOOGLE_AI_API_KEY=YOUR_GOOGLE_AI_API_KEY
注意: API キーは絶対にソースコードにハードコードしないでください。Git にコミットしてしまうと、GitHub の Secret Scanning に検出されてキーが即座に無効化されます。
Veo 3 API で動画を生成する
Veo 3 の呼び出しから動画ダウンロードまでのコードです。非同期処理のポーリングと、タイムアウト時の再試行ロジックを含めています。
# video_generator.py — Veo 3 動画生成モジュール
import time
import logging
import httpx
from pathlib import Path
from google import genai
from google.genai import types
from config import get_client, VEO3_MODEL , TEMP_DIR
logger = logging.getLogger( __name__ )
def generate_video (
prompt: str ,
output_filename: str ,
aspect_ratio: str = "16:9" ,
duration_seconds: int = 8 ,
max_retries: int = 3 ,
poll_interval: int = 10 ,
timeout_seconds: int = 300 ,
) -> Path | None :
"""
Veo 3 API を使ってテキストから動画を生成し、ファイルとして保存する。
Args:
prompt: 動画の内容を説明するテキスト(英語が精度良好)
output_filename: 保存するファイル名(拡張子なし)
aspect_ratio: "16:9" または "9:16"
duration_seconds: 動画の長さ(最大8秒)
max_retries: エラー時の最大リトライ回数
poll_interval: ポーリング間隔(秒)
timeout_seconds: 全体のタイムアウト(秒)
Returns:
生成された動画ファイルのパス。失敗した場合は None。
"""
client = get_client()
output_path = Path( TEMP_DIR ) / f " { output_filename } .mp4"
for attempt in range ( 1 , max_retries + 1 ):
try :
logger.info( f "Veo 3 動画生成開始(試行 { attempt } / { max_retries } ): { prompt[: 50 ] } ..." )
# 動画生成リクエストを送信
operation = client.models.generate_video(
model = VEO3_MODEL ,
prompt = prompt,
config = types.GenerateVideoConfig(
aspect_ratio = aspect_ratio,
duration_seconds = duration_seconds,
number_of_videos = 1 ,
enhance_prompt = True , # プロンプトの自動補完を有効化
),
)
# 完了を待つ(ポーリング)
start_time = time.time()
while not operation.done:
elapsed = time.time() - start_time
if elapsed > timeout_seconds:
raise TimeoutError ( f "動画生成がタイムアウトしました( { timeout_seconds } 秒超過)" )
logger.info( f " 生成中... { elapsed :.0f } 秒経過" )
time.sleep(poll_interval)
operation = client.operations.get(operation)
# エラーチェック
if operation.error:
raise RuntimeError ( f "動画生成エラー: { operation.error.message } " )
# 動画をダウンロードして保存
video = operation.result.generated_videos[ 0 ]
video_bytes = client.files.download( file = video.video)
with open (output_path, "wb" ) as f:
f.write(video_bytes)
logger.info( f "✅ 動画保存完了: { output_path } ( { output_path.stat().st_size // 1024 } KB)" )
return output_path
except TimeoutError as e:
logger.warning( f "⚠️ タイムアウト(試行 { attempt } ): { e } " )
if attempt == max_retries:
logger.error( "最大リトライ回数に達しました" )
return None
except Exception as e:
logger.error( f "❌ エラー(試行 { attempt } ): { type (e). __name__ } : { e } " )
if attempt < max_retries:
wait_time = 2 ** attempt * 5 # 指数バックオフ: 10秒 → 20秒 → 40秒
logger.info( f " { wait_time } 秒後にリトライします..." )
time.sleep(wait_time)
else :
logger.error( "最大リトライ回数に達しました。この動画の生成をスキップします。" )
return None
return None
ここで重要なのは enhance_prompt=True の設定です。Veo 3 はプロンプトが短すぎると生成品質が落ちる傾向があります。このオプションを有効にすると、モデルが自動的にプロンプトを補完して品質を向上させてくれます。ただし、補完された内容が意図と若干ずれることもあるため、重要な動画では enhance_prompt=False にして自分で詳細なプロンプトを書くほうがよいでしょう。
Lyria 3 Pro API で背景音楽を生成する
音楽生成のコードです。音楽はテキストで「ジャンル・楽器・テンポ・雰囲気」を細かく指定できます。
# music_generator.py — Lyria 3 Pro 音楽生成モジュール
import time
import logging
from pathlib import Path
from google import genai
from google.genai import types
from config import get_client, LYRIA3_MODEL , TEMP_DIR
logger = logging.getLogger( __name__ )
def generate_music (
prompt: str ,
output_filename: str ,
duration_seconds: int = 30 ,
output_format: str = "mp3" ,
max_retries: int = 3 ,
) -> Path | None :
"""
Lyria 3 Pro API を使ってテキストから音楽を生成し、ファイルとして保存する。
Args:
prompt: 音楽のスタイルを説明するテキスト
output_filename: 保存するファイル名(拡張子なし)
duration_seconds: 音楽の長さ(最大210秒)
output_format: "mp3" または "wav"
max_retries: エラー時の最大リトライ回数
Returns:
生成された音楽ファイルのパス。失敗した場合は None。
"""
client = get_client()
output_path = Path( TEMP_DIR ) / f " { output_filename } . { output_format } "
for attempt in range ( 1 , max_retries + 1 ):
try :
logger.info( f "Lyria 3 Pro 音楽生成開始(試行 { attempt } / { max_retries } )" )
response = client.models.generate_music(
model = LYRIA3_MODEL ,
prompt = prompt,
config = types.GenerateMusicConfig(
duration_seconds = duration_seconds,
output_format = output_format,
# ループ再生向けにシームレスな終わりを生成
seamless_loop = False ,
),
)
# 音楽データを保存
if response.audio_data:
with open (output_path, "wb" ) as f:
f.write(response.audio_data)
logger.info( f "✅ 音楽保存完了: { output_path } " )
return output_path
else :
raise ValueError ( "音楽データが空です" )
except Exception as e:
logger.error( f "❌ エラー(試行 { attempt } ): { type (e). __name__ } : { e } " )
if attempt < max_retries:
wait_time = 2 ** attempt * 3
logger.info( f " { wait_time } 秒後にリトライします..." )
time.sleep(wait_time)
else :
logger.error( "最大リトライ回数に達しました" )
return None
return None
def build_music_prompt (
genre: str ,
mood: str ,
tempo: str = "moderate" ,
instruments: list[ str ] | None = None ,
no_lyrics: bool = True ,
) -> str :
"""
音楽生成プロンプトを構造化して組み立てるヘルパー関数。
Lyria 3 Pro は細かい指定ほど品質が上がる。
"""
parts = [ f " { genre } music" , f " { mood } mood" , f " { tempo } tempo" ]
if instruments:
parts.append( f "featuring { ', ' .join(instruments) } " )
if no_lyrics:
parts.append( "instrumental, no vocals, no lyrics" )
parts.append( "high quality, professional recording, studio quality" )
return ", " .join(parts)
音楽プロンプトは「ジャンル + 雰囲気 + テンポ + 楽器」の組み合わせで指定するのが最も安定します。例えば build_music_prompt("ambient electronic", "calm and uplifting", "slow", ["synthesizer", "piano"]) のように使います。日本語のプロンプトでも動きますが、英語プロンプトのほうが意図に近い結果が得られやすいため、私は英語で指定するようにしています。
動画と音楽を自動合成する統合パイプライン
Veo 3 で生成した動画と Lyria で生成した音楽を ffmpeg で合成するコードです。動画と音楽の長さが異なる場合の処理も含めています。
# pipeline.py — 統合パイプライン(非同期並列処理版)
import asyncio
import logging
import ffmpeg
from pathlib import Path
from datetime import datetime
from config import OUTPUT_DIR , TEMP_DIR
from video_generator import generate_video
from music_generator import generate_music, build_music_prompt
logger = logging.getLogger( __name__ )
def merge_video_audio (
video_path: Path,
audio_path: Path,
output_filename: str ,
audio_volume: float = 0.5 ,
fade_out_seconds: float = 1.0 ,
) -> Path | None :
"""
ffmpeg を使って動画と音楽を合成する。
- 音楽が動画より長い場合は動画の長さに合わせてカット
- フェードアウト処理を末尾に適用
- 音量を video_volume パラメータで調整
Args:
audio_volume: 音楽の音量(0.0〜1.0、0.5が推奨)
"""
output_path = Path( OUTPUT_DIR ) / f " { output_filename } .mp4"
try :
# 動画の長さを取得
probe = ffmpeg.probe( str (video_path))
video_duration = float (probe[ "format" ][ "duration" ])
# 入力ストリームを定義
video_input = ffmpeg.input( str (video_path))
audio_input = ffmpeg.input( str (audio_path))
# 音楽を動画の長さに合わせてカット + フェードアウト適用
fade_start = max ( 0 , video_duration - fade_out_seconds)
audio_processed = (
audio_input
.audio
.filter( "atrim" , duration = video_duration) # 動画長さに合わせてカット
.filter( "asetpts" , "PTS-STARTPTS" ) # タイムスタンプをリセット
.filter( "volume" , audio_volume) # 音量調整
.filter( "afade" , type = "out" , start_time = fade_start, duration = fade_out_seconds) # フェードアウト
)
# 動画と処理済み音楽を合成
output = ffmpeg.output(
video_input.video,
audio_processed,
str (output_path),
vcodec = "copy" , # 動画は再エンコードなしでコピー(高速)
acodec = "aac" , # 音声はAAC形式に変換
audio_bitrate = "192k" ,
)
ffmpeg.run(output, overwrite_output = True , quiet = True )
logger.info( f "✅ 合成完了: { output_path } " )
return output_path
except ffmpeg.Error as e:
logger.error( f "❌ ffmpeg エラー: { e.stderr.decode() } " )
return None
async def run_pipeline (
video_prompt: str ,
music_prompt: str ,
output_name: str ,
video_duration: int = 8 ,
music_duration: int = 30 ,
) -> dict :
"""
動画生成と音楽生成を並列実行して合成する統合パイプライン。
Returns:
実行結果を含む辞書(status, output_path, elapsed_seconds)
"""
start_time = asyncio.get_event_loop().time()
timestamp = datetime.now().strftime( "%Y%m %d _%H%M%S" )
base_name = f " { output_name } _ { timestamp } "
logger.info( f "🎬 パイプライン開始: { output_name } " )
# 動画生成と音楽生成を並列実行
loop = asyncio.get_event_loop()
video_task = loop.run_in_executor(
None ,
generate_video,
video_prompt, f " { base_name } _video" , "16:9" , video_duration
)
music_task = loop.run_in_executor(
None ,
generate_music,
music_prompt, f " { base_name } _music" , music_duration
)
# 両方の完了を待つ
video_path, music_path = await asyncio.gather(video_task, music_task)
if not video_path:
return { "status" : "FAILED" , "reason" : "動画生成失敗" }
if not music_path:
return { "status" : "FAILED" , "reason" : "音楽生成失敗" }
# 合成処理
output_path = merge_video_audio(video_path, music_path, base_name)
elapsed = asyncio.get_event_loop().time() - start_time
if output_path:
# 一時ファイルを削除
video_path.unlink( missing_ok = True )
music_path.unlink( missing_ok = True )
return {
"status" : "SUCCESS" ,
"output_path" : str (output_path),
"elapsed_seconds" : round (elapsed, 1 ),
}
else :
return { "status" : "FAILED" , "reason" : "合成処理失敗" }
# 実行例
if __name__ == "__main__" :
import logging
logging.basicConfig( level = logging. INFO , format = " %(asctime)s %(levelname)s %(message)s " )
result = asyncio.run(run_pipeline(
video_prompt = (
"A serene Japanese garden in autumn, koi pond with red and orange maple leaves "
"falling gently onto the water surface, soft morning light filtering through bamboo, "
"cinematic 4K quality, slow motion"
),
music_prompt = build_music_prompt(
genre = "ambient" ,
mood = "peaceful and contemplative" ,
tempo = "slow" ,
instruments = [ "koto" , "shakuhachi flute" , "gentle piano" ],
),
output_name = "japanese_garden" ,
video_duration = 8 ,
music_duration = 15 ,
))
print (result)
# 期待する出力:
# {'status': 'SUCCESS', 'output_path': 'output/japanese_garden_20260418_104500.mp4', 'elapsed_seconds': 87.3}
並列実行のポイントは asyncio.gather() です。Veo 3 の生成には通常60〜120秒かかり、Lyria も20〜60秒かかります。これを順番に実行すると最大180秒かかりますが、並列にすることで実質的にVeo 3 の待ち時間のみになります。私の環境では平均87秒前後で1本完成しています。
よくある落とし穴と対処法
実際に本番環境で運用して遭遇した問題を共有します。
落とし穴1: Operation のポーリングで done フラグが永遠に False のままになる
Veo 3 の生成が内部でエラーになっているのに、ポーリングしても operation.done が True にならないケースがあります。原因はプロンプトに Google のポリシーに抵触する表現(特定の人物名、著作物のキャラクター名など)が含まれている場合です。この場合、operation.error も None のままで、500秒待っても何も返ってきません。
対処法はタイムアウトを必ず設定することです。上記コードの timeout_seconds=300 がそれに当たります。タイムアウト後は別のプロンプトで再試行するか、プロンプトの見直しをします。
落とし穴2: Lyria の出力がメロディではなくノイズになる
プロンプトに矛盾する指定を入れると(例: 「upbeat and calm」「fast and slow」)、生成物がノイズや不自然な音になることがあります。また、duration_seconds を極端に短く(5秒以下)設定するとモデルが適切なフレーズを生成できず品質が著しく低下します。最低でも15秒以上を指定することをお勧めします。
落とし穴3: ffmpeg の合成で音声が出ない動画が生成される
vcodec="copy" を使って動画をコピーした場合、元の Veo 3 動画に音声ストリームが含まれていないため問題はありませんが、Lyria の音声フォーマットが ffmpeg で認識されないことがあります。WAV出力を選択していて、サンプルレートが一般的でない値(例: 44100Hz 以外)の場合に起きやすいです。MP3形式を指定するか、ffmpeg のオプションに -ar 44100 を追加することで解決できます。
落とし穴4: API呼び出しがレート制限エラー(429)で連続失敗する
Veo 3 と Lyria はそれぞれ独立したクォータを持っていますが、どちらも1分あたりの呼び出し回数に上限があります。バッチ処理で大量に生成しようとすると、すぐに上限に達します。私が採用している対策は、各リクエストの間に time.sleep(60 / requests_per_minute_limit) の待機を入れることです。また、上記のリトライロジックで指数バックオフを使うことで、429エラー時に自動的に待機時間を延ばします。
落とし穴5: 大きなファイルの client.files.download() がメモリ不足でクラッシュする
Veo 3 が生成する 8秒・1080p の動画は50MB前後になることがあります。client.files.download() はデフォルトで全データをメモリに読み込むため、複数の動画を並列生成するとメモリが不足する場合があります。ダウンロード後はすぐにファイルに書き出し、メモリを解放する設計にしておく点が肝心です。
コスト管理と最適化戦略
Veo 3 と Lyria の API コストは、生成する動画の長さと解像度、音楽の長さによって決まります。無計画に使うとすぐに月数万円のコストになります。
実際に効果があったコスト削減の方法をご紹介します。
プロンプトのバリエーション生成を先にテキストで行う
Veo 3 の API を呼ぶ前に、Gemini 2.5 Pro(テキスト生成)でプロンプトのバリエーションを5〜10個生成し、どれが最も意図に合うかを評価してから実際の動画生成に進む方法です。Gemini 2.5 Pro のテキスト生成コストは Veo 3 の動画生成コストと比べて何十倍も安いため、試行錯誤のコストを大幅に削減できます。
音楽の長さと動画の長さを揃える
Lyria で生成した音楽を動画の長さに合わせてカットするのではなく、最初から動画の長さ+2秒程度に設定することで、APIコールごとのコストを最小限に抑えられます。必要以上に長い音楽を生成するのはコストの無駄です。
生成済みアセットのキャッシュ
同じプロンプトで繰り返し生成することを避けるため、プロンプトのハッシュ値をキーとして生成済みファイルをキャッシュします。これにより、過去に生成した素材を再生成するコストをゼロにできます。コンテンツのバリエーションが限られている場合(例: 特定のジャンルの音楽を繰り返し使う)に特に効果があります。
月間コストの目安としては、1日10本の8秒動画を生成した場合(Veo 3 + Lyria 各10回)、私の環境では月額1万5千〜3万円程度になっています。これを回収するにはコンテンツからの収益が必要ですが、短時間動画を YouTube Shorts や SNS で自動投稿する仕組みと組み合わせることで、広告収益やアフィリエイトでペイできる可能性があります。
API コストの詳細や最新の料金体系については、Google AI Platform の料金ページ で確認してください。
より深く Gemini の API 活用と収益化を
個人開発者の視点から(実体験メモ)
全体を振り返って — 今日からできる一歩
本記事で紹介したパイプラインの構成を、一度最小構成で試してみることをお勧めします。まず config.py を書いて API キーが通るか確認し、次に generate_video() を単体で呼び出して8秒の動画が生成されることを確認します。そこまで動けば、あとは generate_music() と merge_video_audio() を追加するだけです。
最初は手動でプロンプトを指定して1本ずつ動かし、結果を見ながらプロンプトを改善していくのが最も効率的です。自動化の精度が上がったら、Google Sheets や Notion などのコンテンツ管理ツールからプロンプトを読み込んで一括処理する仕組みに発展させると、本格的なコンテンツ量産パイプラインになります。
動画生成 AI と音楽生成 AI の組み合わせはまだ発展途上で、毎月のように新しいモデルやAPIの改善が入っています。本記事のコードも適宜アップデートしていく予定ですので、ぜひブックマークしておいてください。