はじめに
DVDレンタルアプリ用のサンプルメディア(映画ポスター画像 + プレビュー動画)が不足していたため、
Gemini Ultra を使って生成し、アプリに組み込みました。
※ この記事は開発中の記録です。生成方法・品質の評価が目的であり、本番サービス向けの商用利用の話ではありません。
なぜメディアが必要だったか
ベースにしている dvdrental データベースにはタイトルと説明文は入っていますが、
ポスター画像やプレビュー動画はありません。
作品一覧・詳細画面でビジュアルがないと UI の確認が難しかったため、ダミーメディアを生成することにしました。
使ったもの
| ツール | 用途 |
|---|---|
| Gemini Ultra(画像生成) | 映画タイトルをもとにポスター画像を生成 |
| Gemini Ultra(動画生成) | 予告編風の短い動画クリップを生成 |
| PowerShell スクリプト | ファイルのリネーム・配置を自動化 |
画像生成のプロンプト例
映画「ACADEMY DINOSAUR」のポスターを生成してください。
モノクロノワール調、1950年代のB級映画風。
縦長(2:3)、テキストなし。
生成された画像は public/media/ に配置します。
動画生成のプロンプト例
映画「ACADEMY DINOSAUR」の15秒予告編風クリップ。
モノクロ。恐竜が登場するコメディ映画の雰囲気。
テキストオーバーレイなし。
生成された動画も public/media/ に配置します。
ファイルの命名規則と配置
フロントエンドは film_id と title のスラッグからメディアのパスを自動解決します。
// FilmDetailView.vue(buildMediaCandidates - 実際のコード)
const toKebab = (value: string) =>
value
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
const buildMediaCandidates = (targetFilm: PublicFilmSummary): MediaCandidates => {
const id = targetFilm.filmId
const slug = toKebab(targetFilm.title)
const titleImageBaseNames = [
`film-${id}-title`, // film-1-title.png
`film-${id}-main`, // film-1-main.jpg
`film-${id}`, // film-1.png
`${slug}-title`, // academy-dinosaur-title.png
slug, // academy-dinosaur.png
]
const titleImages = titleImageBaseNames.flatMap((baseName) =>
['png', 'jpg', 'jpeg', 'webp'].map((ext) => `/media/${baseName}.${ext}`),
)
return { titleImages, previewVideos: [...] }
}
toKebab で "ACADEMY DINOSAUR" → "academy-dinosaur" に変換します。
ファイル名は film-1-title.png のような形式が最も優先度が高く、スラッグ形式は後続の候補です。
フォールバックの実装
メディアが存在しない場合は、候補を順に試してすべて失敗したらフォールバック表示に落とします。
// FilmDetailView.vue(画像フォールバック - 実際のコード)
const titleImageLoadFailed = ref(false)
const titleImageIndex = ref(0)
const currentTitleImage = computed(() => media.value.titleImages[titleImageIndex.value])
const onTitleImageError = () => {
// 次の候補を試す
if (titleImageIndex.value < media.value.titleImages.length - 1) {
titleImageIndex.value++
} else {
// すべての候補が失敗
titleImageLoadFailed.value = true
}
}
<!-- テンプレート側 -->
<div class="media-stage" v-if="currentTitleImage && !titleImageLoadFailed">
<img
class="title-image"
:src="currentTitleImage"
:alt="`${film.title} のタイトル画像`"
@error="onTitleImageError"
>
</div>
<div v-else class="media-fallback">タイトル画像は未設定、または読み込みに失敗しました。</div>
@error イベントで onTitleImageError を呼び、次のインデックスを試します。
すべて失敗したら titleImageLoadFailed フラグを立てて、テンプレートがフォールバック表示に切り替わります。
動画のフォールバック
動画も同様のパターンです。
特定の filmId に対して特別なファイル名も登録できるようにしています。
// FilmDetailView.vue(動画の互換性ファイル名マッピング)
const compatibilityVideoNames: Record<number, string[]> = {
1: ['/media/DVD_レンタル詳細画面用動画作成.mp4'],
2: ['/media/動画作成と追加作成依頼.mp4'],
}
Gemini で生成した動画が日本語ファイル名になった場合も、この compatibilityVideoNames に登録することで対応できます。
PowerShell でのファイルリネーム自動化
Gemini が出力するファイル名はランダムなので、PowerShell でリネームします。
# scripts/import-media.ps1(抜粋)
$mediaDir = "C:\\Users\\y_104\\git\\dvd-rental-customer-app\\frontend\\public\\media"
# film_id=1 のタイトル画像として配置
$sourcePath = "C:\\Users\\y_104\\Downloads\\generated-image.png"
$destPath = Join-Path $mediaDir "film-1-title.png"
Copy-Item -Path $sourcePath -Destination $destPath -Force
Write-Host "配置完了: $destPath"
おまかせで依頼したら予想外に面白い結果が出た
今回一番印象に残ったのが、ほぼ「おまかせ」に近い状態で Gemini Ultra に依頼したときの挙動です。
最初は細かく指定しようとしていましたが、途中から「映画タイトルと基本的な雰囲気のキーワードだけ渡して、あとは任せる」スタイルで試してみました。
映画「BUCKET BROTHERHOOD」のポスターを作ってください。
兄弟の絆がテーマのヒューマンドラマです。あとはお任せします。
すると Gemini は自分で色調・構図・光の当て方まで判断して、想像していたよりずっと映画らしい絵を出してきました。
動画も同様で、「15秒の予告編風、テイストはおまかせ」という指示で、映画の雰囲気に合った意外なシーンを作ってくれることがありました。
「細かく指定する」より「テーマと感情だけ渡してあとは任せる」方が、面白い結果が出ることがある。
これは画像・動画 AI を使う上で個人的に一番学びになった点です。
ただし「おまかせ」は振れ幅も大きいので、用途に合わせて使い分けるのが現実的です。
生成してみた感想
- タイトルが英語でも「雰囲気のあるポスター」は生成できる。
- 動画はプロンプトの言語や詳細度によって品質が大きく変わる。
- おまかせ指示の方が意外と面白い結果が出ることがある(特に動画)。
- ファイルサイズが大きくなりがちなので、
ffmpegでリサイズ・圧縮が必要。 - 著作権に配慮して、既存作品・既存キャラクターに依存しない条件で生成することが重要。
まとめ
DVDレンタルアプリ開発のデモ用として、Gemini Ultra で映画ポスターとプレビュー動画を生成しました。
フロントエンドは film_id と title スラッグから自動的にメディアパスを解決し、存在しない場合はフォールバック表示 にする実装になっています。
この「存在確認してから表示、なければフォールバック」というパターンは、
実際のサービスでも画像が欠けるケースへの備えとして使えます。
