意外と簡単? Geminiで長編動画に自動タグ付け
1時間超えの動画でも工夫次第でGeminiを使って自動タグ付けできちゃいます
Table of contents
author: teikoku-penguin
はじめに
本記事は検証・執筆に生成AIを活用しています。
お久しぶりです。 teikoku-penguinです。
皆さんは長編動画の管理で困ったことはありませんか?特に1時間を超えるような動画の場合、内容を把握して適切なタグを付けるのは大変な作業です。 そこで、「これをAIにやらせてみよう」とGeminiのアプリに頼んでみた方もきっと多いでしょう。しかし思ったとおりにはいかなかったですよね?
今回は、そんなタグ付け作業をGoogle GeminiのAPIを使って自動的に行うPythonアプリケーションを作成しました。このアプリを使えば、どんなに長い動画でも簡単に内容を分析して、適切なタグを自動生成できます。
仕組み
このアプリの仕組みは以下の通りです。
- 動画の分割: 長編動画を30分ごとのセグメントに分割
- AI分析: 各セグメントをGemini APIで分析し、タグを生成
- タグ統合: 全セグメントのタグを統合し、動画全体を表す最終タグリストを作成
なぜ分割するのか?
Gemini APIには動画の長さ制限があるため、長編動画をそのまま処理できません。しかし、動画を適切な長さに分割することで、各セグメントの内容を詳細に分析できます。 具体的には、音声のある動画でgemini-2.5-Proを用いると45分、音声なしで1時間(1Mトークンまで)が目安となります。Files APIを使用する場合、2GB以下のファイルに分割する必要があります。
※ VertexAI環境で利用する場合はFiles APIが使えないため、Cloud Storageなどを利用する必要があります。
タグ生成の流れ
長編動画 → セグメント分割 → 各セグメントのAI分析 → タグ統合 → 最終タグリスト
準備
必要なもの
- Python 3.7以上
- Google Gemini APIキー
- ffmpeg
セットアップ
# 仮想環境の作成
python3 -m venv venv
source venv/bin/activate # macOS/Linux
# 依存関係のインストール
pip install google-generativeai
# 環境変数の設定
export GOOGLE_API_KEY="your-api-key-here"
コード
完全なコード
import os
import sys
import subprocess
import tempfile
import pathlib
import time
import argparse
import google.generativeai as genai
try:
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
except KeyError:
print("エラー: 環境変数 'GOOGLE_API_KEY' が設定されていません。")
print("プログラムを実行する前に、ご自身のAPIキーを設定してください。")
sys.exit(1)
MODEL_NAME = "gemini-2.5-pro"
MAX_SEGMENT_SECONDS = 1800
def split_video_into_segments(video_path: pathlib.Path, output_dir: pathlib.Path, segment_time: int) -> list[pathlib.Path]:
"""
ffmpegを使用して動画を指定した時間でセグメントに分割します。
Args:
video_path (pathlib.Path): 分割対象の動画ファイルパス。
output_dir (pathlib.Path): 分割後のセグメントを保存するディレクトリ。
segment_time (int): セグメントの時間(秒)。
Returns:
list[pathlib.Path]: 分割された動画セグメントのファイルパスのリスト。
"""
print(f"動画を{segment_time}秒ごとに分割しています...")
command = [
'ffmpeg',
'-i', str(video_path),
'-c', 'copy',
'-map', '0',
'-segment_time', str(segment_time),
'-f', 'segment',
'-reset_timestamps', '1',
str(output_dir / 'output_%03d.mp4')
]
try:
result = subprocess.run(command, check=True, capture_output=True, text=True, encoding='utf-8')
except FileNotFoundError:
print("エラー: 'ffmpeg' コマンドが見つかりません。")
print("ffmpegがインストールされ、システムのパスが通っているか確認してください。")
return []
except subprocess.CalledProcessError as e:
print(f"ffmpegの実行中にエラーが発生しました。")
print(f"コマンド: {' '.join(command)}")
print(f"エラー出力:\n{e.stderr}")
return []
segments = sorted(list(output_dir.glob('output_*.mp4')))
print(f"動画の分割が完了しました。{len(segments)}個のセグメントが生成されました。")
return segments
def get_tags_for_segment(segment_path: pathlib.Path, model: genai.GenerativeModel) -> str:
"""
動画セグメントをGemini APIにアップロードし、タグを生成します。
ファイルの準備が整うまで待機する処理を含みます。
Args:
segment_path (pathlib.Path): タグ付けする動画セグメントのファイルパス。
model (genai.GenerativeModel): 事前に生成されたGeminiモデルインスタンス。
Returns:
str: 生成されたタグ(カンマ区切り)。
"""
print(f"\nセグメント '{segment_path.name}' の処理を開始...")
uploaded_file = None
try:
print(f"'{segment_path.name}' をアップロード中...")
uploaded_file = genai.upload_file(path=segment_path, display_name=segment_path.name)
print(f" -> アップロード完了: {uploaded_file.name}")
print("API側でのファイル処理を待機中...")
while uploaded_file.state.name == "PROCESSING":
time.sleep(5)
uploaded_file = genai.get_file(uploaded_file.name)
print(f" -> 現在の状態: {uploaded_file.state.name}")
if uploaded_file.state.name != "ACTIVE":
print(f"エラー: ファイル '{uploaded_file.name}' の処理に失敗しました。状態: {uploaded_file.state.name}")
return ""
print("ファイルが準備完了しました。タグを生成します。")
prompt = "この動画の内容を分析し、最も的確に内容を表すキーワードタグを10個、日本語の単語で、カンマ区切りで生成してください。"
response = model.generate_content([prompt, uploaded_file])
tags = response.text.strip()
print(f" -> 生成されたタグ: {tags}")
return tags
except Exception as e:
print(f"セグメント '{segment_path.name}' の処理中に予期せぬエラーが発生しました: {e}")
return ""
finally:
if uploaded_file:
genai.delete_file(uploaded_file.name)
print(f"アップロードされたファイル '{uploaded_file.name}' を削除しました。")
def merge_tags_into_final_list(all_tags: list[str], model: genai.GenerativeModel, num_final_tags: int = 20) -> str:
"""
各セグメントから集めたタグのリストを統合し、最終的なタグリストを生成します。
Args:
all_tags (list[str]): 全セグメントのタグのリスト。
model (genai.GenerativeModel): 事前に生成されたGeminiモデルインスタンス。
num_final_tags (int): 最終的に生成するタグの数。
Returns:
str: 最終的なタグリスト(カンマ区切り)。
"""
print("\n全てのタグを統合し、最終的なタグリストを生成しています...")
unique_tags = set()
for tag_group in all_tags:
tags = [tag.strip() for tag in tag_group.split(',') if tag.strip()]
unique_tags.update(tags)
tags_string = ", ".join(sorted(list(unique_tags)))
if not tags_string:
print("有効なタグが集まらなかったため、統合をスキップします。")
return ""
prompt = f"""
以下のタグのリストは、一つの長い動画を複数の部分に分割して、それぞれから生成されたものです。
これらのタグを分析・集約し、動画全体の内容を最もよく表す重要なタグを{num_final_tags}個、重要度の高い順にカンマ区切りで生成してください。
タグリスト:
{tags_string}
"""
try:
response = model.generate_content(prompt)
final_tags = response.text.strip()
return final_tags
except Exception as e:
print(f"タグの統合中にエラーが発生しました: {e}")
return ""
def main():
"""
メインの処理を実行します。
"""
parser = argparse.ArgumentParser(description='Gemini APIを使用して長編動画にタグを付けます。')
parser.add_argument('video_path', type=pathlib.Path, help='処理対象の動画ファイルパス')
parser.add_argument('--segment_time', type=int, default=1800, help='動画を分割する時間(秒)。デフォルト: 1800 (30分)')
args = parser.parse_args()
video_path = args.video_path
segment_time = args.segment_time
if segment_time > MAX_SEGMENT_SECONDS:
print(f"エラー: 指定されたセグメント時間 {segment_time} 秒は上限 {MAX_SEGMENT_SECONDS} 秒を超えています。")
print("より短い値を指定してください。")
sys.exit(1)
if segment_time <= 0:
print("エラー: セグメント時間は正の整数で指定してください。")
sys.exit(1)
if not video_path.is_file():
print(f"エラー: ファイル '{video_path}' が見つかりません。")
sys.exit(1)
model = genai.GenerativeModel(MODEL_NAME)
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = pathlib.Path(temp_dir)
segments = split_video_into_segments(video_path, temp_path, segment_time)
if not segments:
print("動画の分割に失敗したため、処理を中断します。")
return
all_tags = []
for segment in segments:
tags = get_tags_for_segment(segment, model)
if tags:
all_tags.append(tags)
if all_tags:
final_tags = merge_tags_into_final_list(all_tags, model)
print("\n--- 最終的なタグリスト ---")
print(final_tags)
print("--------------------------")
else:
print("\nタグを一つも生成できませんでした。")
print("\n処理が完了しました。")
if __name__ == "__main__":
main()
使用方法
# 基本的な使用方法
python main.py 動画ファイルパス
# セグメント時間を指定する場合(30分ごと)
python main.py 動画ファイルパス --segment_time 1800
結果
実際にGoogle Cloud Next 25の基調講演動画(約2時間)でテストした結果、以下のようなタグが生成されました。
各セグメントのタグ生成結果
セグメント1 (0-30分)
Google Cloud, 生成AI, AIエージェント, デジタルトランスフォーメーション, イノベーション,
業務効率化, Gemini, Vertex AI, ビジネス, テクノロジー
セグメント2 (30-60分)
AIエージェント, 業務自動化, 生成AI, Google Agentspace, タスク管理,
ワークフロー, 現場DX, マルチモーダルAI, データ連携, 生産性向上
セグメント3 (60-90分)
生成AI, 広告制作, 動画生成, シナリオ作成, 業務効率化,
マーケティング, プロンプト, 絵コンテ, キャラクター生成, Google Cloud
セグメント4 (90-120分)
Google Cloud Next, AI, 生成AI, Google Cloud, プラットフォーム,
ビジネス変革, パートナーシップ, セキュリティ, 活用事例, 基調講演
最終的な統合タグリスト
生成AI, Google Cloud, Vertex AI, Gemini, ビジネス変革, 生産性向上,
業務効率化, AIエージェント, デジタルトランスフォーメーション, マルチモーダルAI,
活用事例, データ連携, 動画生成, セキュリティ, マーケティング,
イノベーション, プラットフォーム, 広告制作, ワークフロー, 基調講演
処理時間
- 動画分割: 約1分
- 各セグメントのAI分析: セグメント1つあたり約3-4分
- タグ統合: 約1分
- 合計: 約6分(動画の長さによる)
実際の実行ログ
動画を1800秒ごとに分割しています...
動画の分割が完了しました。4個のセグメントが生成されました。
セグメント 'output_000.mp4' の処理を開始...
'output_000.mp4' をアップロード中...
-> アップロード完了: files/xyv5pa4pxo2x
API側でのファイル処理を待機中...
-> 現在の状態: PROCESSING
-> 現在の状態: PROCESSING
-> 現在の状態: PROCESSING
-> 現在の状態: PROCESSING
-> 現在の状態: ACTIVE
ファイルが準備完了しました。タグを生成します。
-> 生成されたタグ: Google Cloud,生成AI,AIエージェント,デジタルトランスフォーメーション,イノベーション,業務効率化,Gemini,Vertex AI,ビジネス,テクノロジー
アップロードされたファイル 'files/xyv5pa4pxo2x' を削除しました。
全てのタグを統合し、最終的なタグリストを生成しています...
--- 最終的なタグリスト ---
生成AI, Google Cloud, Vertex AI, Gemini, ビジネス変革, 生産性向上,
業務効率化, AIエージェント, デジタルトランスフォーメーション, マルチモーダルAI,
活用事例, データ連携, 動画生成, セキュリティ, マーケティング,
イノベーション, プラットフォーム, 広告制作, ワークフロー, 基調講演
--------------------------
処理が完了しました。
精度
- 各セグメントの内容を正確に把握
- 重複タグの自動除去
- 動画全体のテーマを反映した統合タグ
おわりに
今回作成したアプリは、長編動画の管理を大幅に効率化できるツールです。
メリット
- 時間短縮: 手動でタグ付けする時間を大幅に削減
- 精度向上: AIによる客観的な内容分析
- 一貫性: 統一された基準でのタグ付け
- 拡張性: 他の動画処理にも応用可能
今後の改善点
- バッチ処理による複数動画の一括処理
- タグのカテゴリ分類機能
- 動画の要約生成機能
- Web UIの追加
活用シーン
- YouTube動画の管理
- 企業研修動画の整理
- 教育コンテンツのカテゴリ分け
- 動画アーカイブの検索性向上
Gemini APIを使った動画分析は、思ったよりも簡単に実装できます。特に長編動画の管理でお困りの方は、ぜひ試してみてください!
※本記事は、ジーアイクラウド株式会社の見解を述べたものであり、必要な調査・検討は行っているものの必ずしもその正確性や真実性を保証するものではありません。
※リンクを利用する際には、必ず出典がGIC dryaki-blogであることを明記してください。
リンクの利用によりトラブルが発生した場合、リンクを設置した方ご自身の責任で対応してください。
ジーアイクラウド株式会社はユーザーによるリンクの利用につき、如何なる責任を負うものではありません。