thumbnail_generation Lambda 概要
SQS(thumbnail-generation キュー)のメッセージをトリガーに、レース動画から AI が選定した瞬間の
フレームを切り出してサムネイル画像を作る Lambda 関数です。
画像を生成するのではなく、動画から切り出す
サムネイルは生成 AI が描画するのではありません。AI(thumbnail_generation_agent)が
レース動画・レース結果・コース画像を解析して「サムネイルに最適な瞬間(動画先頭からの経過秒数)」を
選定し、その位置のフレームを ffmpeg で切り出します。
トリガー
| 送信元 | タイミング |
|---|---|
App Lambda(odds_poc_app) |
ブログのサムネイル生成リクエスト時(POST /v1/blogs/{blog_id}/thumbnail_generate) |
ペイロードは article_id / blog_id のどちらか一方を指定します(両方指定・両方未指定はエラー)。
記事向けの生成経路も実装されていますが、現在 App Lambda が投入するのはブログ向けのみです。
SQS イベントペイロード(受信)
| フィールド | データ型 | 必須 | 備考 |
|---|---|---|---|
| article_id | string | △ | 記事向け。blog_id と排他 |
| blog_id | string | △ | ブログ向け。article_id と排他 |
処理フロー
- SQS イベントを受信し、ペイロードをパース・バリデーションする
- DocumentDB へ接続する
- 対象(
blog/article)とそのrace_idからレースを特定する - レース動画(
{racing_type}/video/{race_id}.mp4)を/tmpへダウンロードし、ffprobeで動画長を得る thumbnail_generation_agentに「最適な瞬間(秒)」を選定させる- 入力: レース動画(S3 URI)・レース結果・コース画像(競馬のみ)
- 失敗した場合は動画長の 90%(ゴール付近)へフォールバックする
- タイムスタンプを動画範囲内へクランプし、
ffmpegでフレームを切り出す - JPEG を S3 へアップロードし、
mediaレコードを upsert する - (ブログの場合)本文 HTML の「10秒サマリー」直下へサムネイル画像を挿入する
flowchart TD
AppLambda([App Lambda]) -->|SQS| Start
Start[SQS トリガー受信] --> DB[DocumentDB へ接続]
DB --> Target[blog / article とレースを特定]
Target --> Download[レース動画を /tmp へダウンロード]
Download --> Duration[ffprobe で動画長を取得]
Duration --> Choose[thumbnail_generation_agent が最適な秒数を選定]
Choose -->|失敗| Fallback[動画長の 90% にフォールバック]
Choose -->|成功| Clamp
Fallback --> Clamp[動画範囲内へクランプ]
Clamp --> Extract[ffmpeg でフレームを切り出し]
Extract --> Upload[S3 へアップロード]
Upload --> Media[media レコードを upsert]
Media -->|ブログの場合| Insert[body_html へサムネイルを挿入]
保存先と命名規則
| 対象 | S3 キー | media.name |
|---|---|---|
| 記事 | {racing_type}/thumbnail/{race_id}.jpg |
thumbnail_{race_id}.jpg |
| ブログ | {racing_type}/thumbnail/blog_{blog_id}.jpg |
thumbnail_blog_{blog_id}.jpg |
media.name はユニークインデックスのため、既存レコードがあれば上書き更新(upsert)します。
SQS 再配信などで同じレースのサムネイルを再生成した場合の冪等性を担保するためです。
公開 URL
S3 バケットは非公開(Block Public Access)のため、ブラウザから参照できる CloudFront 経由の URL
(CLOUDFRONT_DOMAIN 環境変数)を media.s3_url に保存します。
未設定の環境向けに S3 の直 URL へフォールバックします。
本文 HTML への埋め込み(ブログのみ)
ブログ本文の「10秒サマリー」セクション直下へ、次の img タグを挿入します。
<img class="nb-thumbnail" src="{CloudFront URL}" alt="レースのサムネイル画像"
style="max-width:100%;height:auto;border-radius:8px"/>
挿入位置の決定は次の順で行います。
- 既にサムネイル(
<img class="nb-thumbnail">)があれば置き換える(再生成の冪等性) - 「10秒サマリー」セクション(
<section class="nb-summary-section">)があればその直後 - 本文領域(
<div class="nb-body")があればその直前 - いずれも無ければ末尾に追記
このマークアップは blog_builder 側の nb-thumbnail と揃えており、
再ビルド時には blog_builder が既存 body_html から抽出して引き継ぎます。
使用 AWS サービス・ライブラリ
| サービス / ライブラリ | 用途 |
|---|---|
| Amazon SQS | トリガー受信 |
| Amazon S3 | レース動画・コース画像の取得、サムネイルの保存 |
| Amazon CloudFront | 保存したサムネイルの公開配信 |
| Amazon Bedrock | サムネイル位置の選定(Nova Pro) |
| Amazon DocumentDB | blog / article / media の読み書き |
| LangGraph | サムネイル選定エージェントの構築・実行 |
| ffmpeg / ffprobe | 動画長の取得とフレーム抽出 |