TEXTBOOK SECTION / AI LEARNING

15秒動画台本メーカーを作る

AI活用概論の「GAS × 無料AI APIでつくる自動コンテンツ生成アプリ入門」より、15秒動画台本メーカーを作るを解説。生成AI、AI活用、DX、業務改善を実践しながら学べるオンライン教材です。

6GAS × 無料AI APIでつくる自動コンテンツ生成アプリ入門概論 / AI活用 / ChatGPT / Gemini / Claude / 基礎から学ぶ

OVERVIEW

この節で学べること

概要を表示する
項目内容
教材名AI活用概論
GAS × 無料AI APIでつくる自動コンテンツ生成アプリ入門
15秒動画台本メーカーを作る
カテゴリ概論 / AI活用 / ChatGPT / Gemini / Claude / 基礎から学ぶ
学習内容生成AI、AI活用、DX、業務改善を実践しながら理解するための教材です。

TABLE OF CONTENTS

目次

CONTENT

ここから

つかみ・紹介・魅力・案内の4分割で動画構成を作る

この教材では、Google Apps ScriptのWebアプリで、15秒動画台本メーカーを作ります。

前回は、画像生成AIに入れるための画像プロンプトを作りました。

今回は、企画を短い動画にするための動画台本を自動で作ります。

動画と聞くと難しく感じるかもしれません。

でも、15秒動画は次の4つに分けると、とても作りやすくなります。

つかみ
↓
紹介
↓
魅力
↓
案内

今日作るアプリは、テーマを入力すると、AIがこの4分割で動画の流れを考えてくれるものです。


このページで完成するもの

今回作るのは、次のようなアプリです。

項目内容
入力企画タイトル、ターゲット、伝えたいこと、雰囲気
AI生成15秒動画の構成、テロップ、ナレーション
表示Web画面に表示
保存スプレッドシートに履歴保存

今回のゴールはこれです。

15秒で何を見せるかを、
AIと一緒に決められるようにする

動画そのものが完成しなくても大丈夫です。

まずは、動画の設計図を作ります。


1. 15秒動画の基本

15秒動画は、短いからこそ流れが大切です。

おすすめは、この4分割です。

時間役割内容
0〜3秒つかみ見る人の興味を引く
3〜7秒紹介何の企画か伝える
7〜12秒魅力良さを見せる
12〜15秒案内次の行動を伝える

たとえば、カフェ紹介ならこうです。

0〜3秒:
放課後、どこ寄る?

3〜7秒:
学校帰りに行けるカフェを紹介

7〜12秒:
勉強も休憩もできる場所が見つかる

12〜15秒:
気になるカフェを見てみよう

これだけで、動画の形になります。


2. 今回作るファイル

今回も、使うファイルは2つだけです。

Code.gs
index.html
ファイル役割
Code.gsGemini APIを呼び出し、動画台本を作る
index.html入力画面と結果表示を作る

3. 事前準備

Googleスプレッドシートを作ります。

https://sheets.google.com/

ファイル名は次にします。

AI動画台本メーカー

スプレッドシートからApps Scriptを開きます。

拡張機能
↓
Apps Script

プロジェクト名も、次にします。

AI動画台本メーカー

4. APIキーを確認する

前回と同じ GEMINI_API_KEY を使います。

Apps Scriptの左側にある歯車アイコンを開きます。

プロジェクトの設定
↓
スクリプト プロパティ

次の名前で保存されていればOKです。

プロパティ
GEMINI_API_KEY自分のGemini APIキー

APIキーは人に見せないでください。

コードに直接書かず、スクリプトプロパティに保存します。


5. Code.gsを貼り付ける

Code.gs の中身をすべて消して、次のコードを貼り付けます。

const SHEET_NAME = '動画台本履歴';
const MODEL_NAME = 'gemini-2.5-flash';

function doGet() {
  setupSheet_();

  return HtmlService
    .createHtmlOutputFromFile('index')
    .setTitle('AI動画台本メーカー')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

function generateVideoScript(formData) {
  validateFormData_(formData);
  setupSheet_();

  const prompt = buildVideoScriptPrompt_(formData);
  const result = callGemini_(prompt);

  saveHistory_(formData, result);

  return {
    ok: true,
    text: result,
    message: '15秒動画台本を作成しました。'
  };
}

function buildVideoScriptPrompt_(formData) {
  return `
あなたは、学生向けの短いPR動画を作る動画ディレクターです。

次の企画情報をもとに、15秒動画の台本を作ってください。

【企画タイトル】
${formData.title}

【ターゲット】
${formData.target}

【伝えたいこと】
${formData.message}

【雰囲気】
${formData.tone}

【使いたい画像・場面】
${formData.scene || '未入力'}

【見た人にしてほしい行動】
${formData.action || '未入力'}

以下の形式で出してください。

1. 動画タイトル
2. 15秒動画の目的
3. 0〜3秒:つかみ
   ・映像
   ・テロップ
   ・ナレーション
4. 3〜7秒:紹介
   ・映像
   ・テロップ
   ・ナレーション
5. 7〜12秒:魅力
   ・映像
   ・テロップ
   ・ナレーション
6. 12〜15秒:案内
   ・映像
   ・テロップ
   ・ナレーション
7. Veo用プロンプト
8. AudioLM用ナレーション文
9. テロップだけを4つに整理
10. 発表で使える一言

条件:
・初心者でも作れる内容にする
・スマホで見やすい9:16動画を想定する
・テロップは短くする
・ナレーションは自然で聞き取りやすくする
・人物の顔ははっきり写さない
・動画生成時には文字やロゴを入れない
・動画が完成しなくても、構成だけで提出できる形にする
・少し前向きで、作ってみたくなる雰囲気にする
`;
}

function callGemini_(prompt) {
  const apiKey = PropertiesService
    .getScriptProperties()
    .getProperty('GEMINI_API_KEY');

  if (!apiKey) {
    throw new Error('GEMINI_API_KEY が設定されていません。');
  }

  const url =
    'https://generativelanguage.googleapis.com/v1beta/models/' +
    MODEL_NAME +
    ':generateContent?key=' +
    encodeURIComponent(apiKey);

  const payload = {
    contents: [
      {
        role: 'user',
        parts: [{ text: prompt }]
      }
    ],
    generationConfig: {
      temperature: 0.7,
      maxOutputTokens: 1400
    }
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  const statusCode = response.getResponseCode();
  const responseText = response.getContentText();

  if (statusCode !== 200) {
    throw new Error('Gemini APIエラー:' + responseText);
  }

  const json = JSON.parse(responseText);
  const text = json.candidates?.[0]?.content?.parts?.[0]?.text;

  if (!text) {
    throw new Error('AIの回答を取得できませんでした。');
  }

  return text;
}

function saveHistory_(formData, result) {
  const sheet = SpreadsheetApp
    .getActiveSpreadsheet()
    .getSheetByName(SHEET_NAME);

  sheet.appendRow([
    new Date(),
    formData.title,
    formData.target,
    formData.message,
    formData.tone,
    formData.scene || '',
    formData.action || '',
    result
  ]);
}

function getRecentScripts() {
  setupSheet_();

  const sheet = SpreadsheetApp
    .getActiveSpreadsheet()
    .getSheetByName(SHEET_NAME);

  const lastRow = sheet.getLastRow();

  if (lastRow <= 1) {
    return [];
  }

  const values = sheet.getRange(2, 1, lastRow - 1, 8).getValues();

  return values
    .map((row) => {
      return {
        createdAt: formatDate_(row[0]),
        title: row[1],
        target: row[2],
        tone: row[4],
        result: row[7]
      };
    })
    .reverse()
    .slice(0, 5);
}

function setupSheet_() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = ss.getSheetByName(SHEET_NAME);

  if (!sheet) {
    sheet = ss.insertSheet(SHEET_NAME);
  }

  if (sheet.getLastRow() === 0) {
    sheet.appendRow([
      '作成日時',
      '企画タイトル',
      'ターゲット',
      '伝えたいこと',
      '雰囲気',
      '使いたい場面',
      '行動',
      '動画台本'
    ]);
  }
}

function validateFormData_(formData) {
  if (!formData) {
    throw new Error('入力データがありません。');
  }

  const requiredFields = [
    ['title', '企画タイトル'],
    ['target', 'ターゲット'],
    ['message', '伝えたいこと'],
    ['tone', '雰囲気']
  ];

  requiredFields.forEach(([key, label]) => {
    if (!formData[key] || String(formData[key]).trim() === '') {
      throw new Error(label + 'を入力してください。');
    }
  });
}

function formatDate_(date) {
  if (!date) return '';

  return Utilities.formatDate(
    new Date(date),
    Session.getScriptTimeZone(),
    'yyyy/MM/dd HH:mm'
  );
}

6. index.htmlを作る

Apps Scriptの左側にある「+」を押します。

+
↓
HTML

ファイル名は次にします。

index

index.html の中身をすべて消して、次のコードを貼り付けます。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <base target="_top" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>AI動画台本メーカー</title>

    <style>
      :root {
        --bg: #f8fafc;
        --card: #ffffff;
        --text: #111827;
        --muted: #6b7280;
        --line: #dbe3ee;
        --primary: #f97316;
        --primary-dark: #ea580c;
        --soft: #fff7ed;
        --success: #16a34a;
        --danger: #dc2626;
      }

      * {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        background: linear-gradient(180deg, #fffaf5 0%, #eef2f8 100%);
        color: var(--text);
        font-family:
          system-ui,
          -apple-system,
          BlinkMacSystemFont,
          "Segoe UI",
          sans-serif;
        line-height: 1.7;
      }

      .page {
        width: min(1100px, calc(100% - 32px));
        margin: 0 auto;
        padding: 32px 0 48px;
      }

      .hero {
        padding: 28px;
        margin-bottom: 20px;
        border: 1px solid var(--line);
        border-radius: 24px;
        background: var(--card);
        box-shadow: 0 18px 40px rgba(15, 23, 42, 0.08);
      }

      .badge {
        display: inline-flex;
        padding: 6px 12px;
        border-radius: 999px;
        background: var(--soft);
        color: var(--primary);
        font-size: 13px;
        font-weight: 800;
      }

      h1 {
        margin: 18px 0 8px;
        font-size: clamp(28px, 5vw, 44px);
        line-height: 1.15;
        letter-spacing: -0.04em;
      }

      .lead {
        margin: 0;
        color: var(--muted);
      }

      .grid {
        display: grid;
        grid-template-columns: 0.85fr 1.15fr;
        gap: 20px;
      }

      .card {
        border: 1px solid var(--line);
        border-radius: 24px;
        background: var(--card);
        box-shadow: 0 14px 32px rgba(15, 23, 42, 0.06);
        overflow: hidden;
      }

      .card-header {
        padding: 20px 22px;
        border-bottom: 1px solid var(--line);
        background: #fbfdff;
      }

      .card-header h2 {
        margin: 0;
        font-size: 20px;
      }

      .card-header p {
        margin: 4px 0 0;
        color: var(--muted);
        font-size: 14px;
      }

      form {
        padding: 22px;
      }

      label {
        display: block;
        margin-bottom: 14px;
        font-weight: 800;
      }

      .hint {
        display: block;
        margin: 2px 0 6px;
        color: var(--muted);
        font-size: 13px;
        font-weight: 500;
      }

      input,
      textarea,
      select {
        width: 100%;
        border: 1px solid var(--line);
        border-radius: 14px;
        padding: 12px 14px;
        background: #ffffff;
        color: var(--text);
        font: inherit;
        outline: none;
      }

      textarea {
        min-height: 92px;
        resize: vertical;
      }

      input:focus,
      textarea:focus,
      select:focus {
        border-color: var(--primary);
        box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.13);
      }

      .actions {
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        margin-top: 20px;
      }

      button {
        border: 0;
        border-radius: 14px;
        padding: 12px 18px;
        cursor: pointer;
        font: inherit;
        font-weight: 900;
      }

      button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
      }

      .primary {
        background: var(--primary);
        color: #ffffff;
      }

      .primary:hover {
        background: var(--primary-dark);
      }

      .secondary {
        background: #eef2f7;
        color: #111827;
      }

      .message {
        margin: 0 22px 22px;
        padding: 12px 14px;
        border-radius: 14px;
        display: none;
        font-weight: 800;
      }

      .message.success {
        display: block;
        background: #ecfdf5;
        color: var(--success);
      }

      .message.error {
        display: block;
        background: #fef2f2;
        color: var(--danger);
      }

      .loading {
        display: none;
        padding: 14px;
        margin: 0 22px 22px;
        border-radius: 14px;
        background: #fffbeb;
        color: #92400e;
        font-weight: 800;
      }

      .loading.show {
        display: block;
      }

      .result {
        padding: 22px;
      }

      .result-box {
        min-height: 440px;
        padding: 20px;
        border-radius: 18px;
        background: #111827;
        color: #f9fafb;
        white-space: pre-wrap;
        overflow-wrap: anywhere;
        font-size: 15px;
      }

      .result-empty {
        color: #9ca3af;
      }

      .recent {
        padding: 0 22px 22px;
      }

      .recent-item {
        padding: 14px 0;
        border-top: 1px solid var(--line);
      }

      .recent-item:first-child {
        border-top: 0;
      }

      .recent-title {
        margin: 0;
        font-weight: 900;
      }

      .recent-meta {
        margin: 2px 0 0;
        color: var(--muted);
        font-size: 13px;
      }

      .small-button {
        margin-top: 8px;
        padding: 8px 12px;
        border-radius: 10px;
        background: #eef2f7;
        color: #111827;
        font-size: 13px;
      }

      .empty {
        color: var(--muted);
        font-size: 14px;
      }

      @media (max-width: 920px) {
        .grid {
          grid-template-columns: 1fr;
        }

        .hero {
          padding: 22px;
        }
      }
    </style>
  </head>

  <body>
    <main class="page">
      <section class="hero">
        <span class="badge">6-6 15秒動画台本メーカー</span>
        <h1>短い動画の<br />流れを作る</h1>
        <p class="lead">
          つかみ・紹介・魅力・案内の4分割で、15秒PR動画の設計図を作ります。
        </p>
      </section>

      <section class="grid">
        <div class="card">
          <div class="card-header">
            <h2>動画の材料を入力</h2>
            <p>まだ動画がなくても大丈夫。まずは流れを作ります。</p>
          </div>

          <form id="scriptForm">
            <label>
              企画タイトル
              <span class="hint">例:放課後カフェマップ</span>
              <input
                id="title"
                type="text"
                placeholder="企画タイトル"
                required
              />
            </label>

            <label>
              ターゲット
              <span class="hint">例:学校帰りに寄れる場所を探している学生</span>
              <input
                id="target"
                type="text"
                placeholder="誰に届けたいか"
                required
              />
            </label>

            <label>
              伝えたいこと
              <span class="hint">例:放課後に使える居場所を紹介する</span>
              <textarea
                id="message"
                placeholder="何を伝えたいか"
                required
              ></textarea>
            </label>

            <label>
              雰囲気
              <span class="hint">動画全体の印象を決めます</span>
              <select id="tone" required>
                <option value="">選択してください</option>
                <option value="明るい・親しみやすい">明るい・親しみやすい</option>
                <option value="おしゃれ・落ち着いた">おしゃれ・落ち着いた</option>
                <option value="かわいい・やさしい">かわいい・やさしい</option>
                <option value="かっこいい・テンポが良い">かっこいい・テンポが良い</option>
                <option value="シンプル・分かりやすい">シンプル・分かりやすい</option>
              </select>
            </label>

            <label>
              使いたい画像・場面
              <span class="hint">例:夕方のカフェ、スマホを見る手元</span>
              <textarea
                id="scene"
                placeholder="動画で見せたい場面"
              ></textarea>
            </label>

            <label>
              見た人にしてほしい行動
              <span class="hint">例:サイトを見る、参加する、申し込む</span>
              <input
                id="action"
                type="text"
                placeholder="最後にしてほしい行動"
              />
            </label>

            <div class="actions">
              <button class="primary" type="submit" id="generateButton">
                動画台本を作る
              </button>
              <button class="secondary" type="button" id="sampleButton">
                サンプル入力
              </button>
              <button class="secondary" type="button" id="clearButton">
                クリア
              </button>
            </div>
          </form>

          <div id="loadingBox" class="loading">
            AIが動画台本を作っています。少し待ってください。
          </div>

          <p id="messageBox" class="message"></p>
        </div>

        <div class="card">
          <div class="card-header">
            <h2>生成された動画台本</h2>
            <p>この台本をもとに、Veo・AudioLM・スライド動画へ進めます。</p>
          </div>

          <div class="result">
            <div id="resultBox" class="result-box">
              <span class="result-empty">ここに15秒動画台本が表示されます。</span>
            </div>

            <div class="actions">
              <button class="secondary" type="button" id="copyButton">
                台本をコピー
              </button>
            </div>
          </div>

          <div class="card-header">
            <h2>最近作った動画台本</h2>
            <p>最新5件を表示します。</p>
          </div>

          <div class="recent" id="recentScripts">
            <p class="empty">まだ履歴がありません。</p>
          </div>
        </div>
      </section>
    </main>

    <script>
      const form = document.getElementById('scriptForm');
      const generateButton = document.getElementById('generateButton');
      const sampleButton = document.getElementById('sampleButton');
      const clearButton = document.getElementById('clearButton');
      const copyButton = document.getElementById('copyButton');
      const loadingBox = document.getElementById('loadingBox');
      const messageBox = document.getElementById('messageBox');
      const resultBox = document.getElementById('resultBox');

      const fields = {
        title: document.getElementById('title'),
        target: document.getElementById('target'),
        message: document.getElementById('message'),
        tone: document.getElementById('tone'),
        scene: document.getElementById('scene'),
        action: document.getElementById('action')
      };

      form.addEventListener('submit', (event) => {
        event.preventDefault();

        const formData = getFormData();

        setLoading(true);
        showMessage('', '');

        google.script.run
          .withSuccessHandler((result) => {
            setLoading(false);
            resultBox.textContent = result.text;
            showMessage(result.message || '15秒動画台本を作成しました。', 'success');
            loadRecentScripts();
          })
          .withFailureHandler((error) => {
            setLoading(false);
            showMessage(error.message || '作成に失敗しました。', 'error');
          })
          .generateVideoScript(formData);
      });

      sampleButton.addEventListener('click', () => {
        fields.title.value = '放課後カフェマップ';
        fields.target.value = '学校帰りに友達と寄れる場所を探している学生';
        fields.message.value =
          '勉強にも休憩にも使える、放課後の居場所を分かりやすく紹介する。';
        fields.tone.value = '明るい・親しみやすい';
        fields.scene.value =
          '夕方のカフェ、ノートとドリンク、スマホを見る手元、学校帰りの雰囲気。';
        fields.action.value = 'サイトを見て、行ってみたいカフェを探す';

        showMessage('サンプルを入力しました。自由に書き換えてください。', 'success');
      });

      clearButton.addEventListener('click', () => {
        form.reset();
        resultBox.innerHTML =
          '<span class="result-empty">ここに15秒動画台本が表示されます。</span>';
        showMessage('', '');
      });

      copyButton.addEventListener('click', async () => {
        const text = resultBox.textContent.trim();

        if (!text || text === 'ここに15秒動画台本が表示されます。') {
          showMessage('コピーする台本がありません。', 'error');
          return;
        }

        try {
          await navigator.clipboard.writeText(text);
          showMessage('台本をコピーしました。', 'success');
        } catch (error) {
          showMessage('コピーできませんでした。手動で選択してコピーしてください。', 'error');
        }
      });

      function getFormData() {
        return {
          title: fields.title.value.trim(),
          target: fields.target.value.trim(),
          message: fields.message.value.trim(),
          tone: fields.tone.value.trim(),
          scene: fields.scene.value.trim(),
          action: fields.action.value.trim()
        };
      }

      function setLoading(isLoading) {
        generateButton.disabled = isLoading;
        generateButton.textContent = isLoading
          ? '作成中...'
          : '動画台本を作る';
        loadingBox.classList.toggle('show', isLoading);
      }

      function showMessage(text, type) {
        if (!text) {
          messageBox.textContent = '';
          messageBox.className = 'message';
          return;
        }

        messageBox.textContent = text;
        messageBox.className = 'message ' + type;
      }

      function loadRecentScripts() {
        google.script.run
          .withSuccessHandler(renderRecentScripts)
          .withFailureHandler(() => {
            document.getElementById('recentScripts').innerHTML =
              '<p class="empty">履歴を読み込めませんでした。</p>';
          })
          .getRecentScripts();
      }

      function renderRecentScripts(scripts) {
        const container = document.getElementById('recentScripts');

        if (!scripts || scripts.length === 0) {
          container.innerHTML =
            '<p class="empty">まだ履歴がありません。</p>';
          return;
        }

        container.innerHTML = scripts
          .map((item) => {
            return `
              <div class="recent-item">
                <p class="recent-title">${escapeHtml(item.title)}</p>
                <p class="recent-meta">
                  ${escapeHtml(item.createdAt)}${escapeHtml(item.tone)}
                </p>
                <button class="small-button" type="button" onclick="showScript('${encodeURIComponent(item.result || '')}')">
                  台本を見る
                </button>
              </div>
            `;
          })
          .join('');
      }

      function showScript(encodedText) {
        resultBox.textContent = decodeURIComponent(encodedText);
      }

      function escapeHtml(value) {
        return String(value)
          .replaceAll('&', '&amp;')
          .replaceAll('<', '&lt;')
          .replaceAll('>', '&gt;')
          .replaceAll('"', '&quot;')
          .replaceAll("'", '&#039;');
      }

      loadRecentScripts();
    </script>
  </body>
</html>

7. 保存する

コードを貼り付けたら保存します。

Command + S

Windowsの場合は、

Ctrl + S

です。


8. Webアプリとして公開する

右上の「デプロイ」を押します。

デプロイ
↓
新しいデプロイ

種類は、次を選びます。

ウェブアプリ

設定は次の通りです。

項目設定
説明6-6 AI動画台本メーカー
次のユーザーとして実行自分
アクセスできるユーザー自分のみ

デプロイ後に表示されるURLを開くと、動画台本メーカーが表示されます。


9. 動かしてみる

画面が開いたら、まず「サンプル入力」を押します。

そのあと、次のボタンを押します。

動画台本を作る

少し待つと、右側に15秒動画台本が表示されます。

スプレッドシートには、自動で 動画台本履歴 シートが作られ、生成結果が保存されます。


10. 生成結果の見方

生成された台本では、まずこの3つを確認します。

0〜3秒のつかみがあるか
テロップが短いか
最後に行動案内があるか

動画は、すべてを説明しようとすると長くなります。

15秒動画では、これくらいで十分です。

気になる
↓
何か分かる
↓
良さが分かる
↓
次の行動が分かる

11. うまく出ない時

テロップが長すぎる

プロンプトに次を追加します。

・テロップは10〜16文字くらいにする

動画の流れが分かりにくい

次を追加します。

・0〜3秒、3〜7秒、7〜12秒、12〜15秒で明確に分ける

映像にしにくい

次を追加します。

・実際に映像で見せられる場面にする
・抽象的な言葉だけにしない

ナレーションが長すぎる

次を追加します。

・ナレーションは15秒で読める長さにする
・60〜90文字程度にする

12. 動画生成ができない時の提出方法

VeoやAudioLMが使えない場合でも、この教材では問題ありません。

提出できる形はこれです。

15秒動画構成
テロップ4つ
ナレーション文
Veo用プロンプト

画像や動画が完成していなくても、動画の設計図があれば前に進めます。

無料アカウントで動画生成が難しい場合は、Googleスライドに4枚並べてもOKです。

スライド内容
1枚目つかみ
2枚目紹介
3枚目魅力
4枚目案内

これだけでも、動画の流れは伝わります。


13. 今日の提出物

この回の提出物は、次の3つです。

1. WebアプリURL
2. 生成された動画台本のスクリーンショット
3. スプレッドシートの履歴スクリーンショット

余裕がある人は、Googleスライドで4コマの動画絵コンテも作ってください。

最低限は、これでOKです。

15秒動画の流れが作れた

14. チェックリスト

チェック内容
スプレッドシートを作った
Apps Scriptを開いた
GEMINI_API_KEYを確認した
Code.gsを貼り付けた
index.htmlを作った
index.htmlを貼り付けた
Webアプリとしてデプロイした
WebアプリURLを開いた
サンプル入力を押した
動画台本を作るを押した
15秒動画台本が表示された
履歴がスプレッドシートに保存された

まとめ

今回は、15秒動画の台本を自動で作るアプリを作りました。

動画づくりで大切なのは、いきなり映像を作ることではありません。

まずは、流れを決めることです。

つかみ
↓
紹介
↓
魅力
↓
案内

この4つがあるだけで、短い動画は作りやすくなります。

今日作ったアプリは、動画生成AIを使う前の大切な準備です。

企画書
↓
画像プロンプト
↓
15秒動画台本
↓
動画生成
↓
LP制作

15秒は短いですが、短いからこそ伝える力が育ちます。

次の回では、この企画を1ページのLP構成にまとめていきます。

FAQ

よくある質問

15秒動画台本メーカーを作るは医療関係者向けだけの内容ですか。
医療分野の例が含まれる場合もありますが、医療関係者だけに限定した内容ではありません。生成AI、AI活用、DX、業務改善、プロトタイプ開発など、一般的なAI学習の事例として読める内容です。
AI初心者でも読めますか。
はい。AIをこれから学ぶ方、数学が苦手な方、仕事でAIを使いたい方にも読み進めやすいように、教材の章と節の流れに沿って整理しています。
サムネイル画像は必ず表示されますか。
はい。教材にcoverUrlが設定されている場合はその画像を表示し、未設定の場合は代替サムネイル画像を表示します。
AI活用概論のほかの章も読めますか。
はい。教材トップから章立てを確認でき、前後の節へもページ下部のナビゲーションから移動できます。