複数のスライドの内容をまとめて書き出す


プロンプトからスライドをAIで自動作成というのが話題ですが、反対にすでに作成したスライドの内容にさっと目を通したり、そこから教案やシラバスを作成したりしたい場合、どうしますか?

スライドファイルが一つだったらそれほどの手間ではないでしょうが、たくさんのスライドファイルがある場合いちいちスライドをひとつずつ開いたり、PDFに書き出したものを参照するのは面倒ですよね?

Amazon

私はスライドを作成しながら授業展開を考えるので、1レッスン1スライドファイルという形式になっています。次に使う機会がある場合(いわゆる再利用)、そのスライドファイルを開いてちょっと変更したりしながら使っているのですが、まとめて30レッスン分のスライドの内容をまとめたい(そしてGeminiかChatGPTに改善点があれば聞いてみたい)と思い立ちました。そこで、登場するのがGAS (Google Apps Script)です。GASでフォルダーの中にあるスライドファイルの内容をGoogle Documentに一括に書き出して!と、Geminiさんにお願いしました。

私はスライドファイルをコースごとにフォルダにまとめて、さらに授業ごとにサブフォルダに入れてあるので、指定したフォルダ(親フォルダ)とサブフォルダに入っているすべてのスライドGoogle Documentに書き出してもらうことから始めました。もっと頑張れば画像も書き出せるかもしれませんが、テキスト(タイトルも含める)だけを書き出してもらうGASコードを作成しました。


次のコードをコピーして使えます。

注意: このスクリプトを実行する前に、コード内の'フォルダーのID'を、対象のGoogleフォルダーのIDに書き換えてください。

(一定のファイル名を指定する場合は『GASコード2』を参照)
GASコード1:フォルダからスライドを書き出す

function exportSlidesFromFolderAndSubfolders() {
  const folderId = '1F3-0ZtHRr2WraadUCMa2UX195kDR-kvn'; // 親フォルダのID
  const doc = DocumentApp.create('フォルダ内のスライド内容');
  const body = doc.getBody();

  const processedFolders = new Set();
  const foldersToProcess = [folderId];

  while (foldersToProcess.length > 0) {
    const currentFolderId = foldersToProcess.shift();
    if (processedFolders.has(currentFolderId)) continue;
    
    processedFolders.add(currentFolderId);

    try {
      const folder = DriveApp.getFolderById(currentFolderId);
      
      const files = folder.getFilesByType(MimeType.GOOGLE_SLIDES);
      while (files.hasNext()) {
        const file = files.next();
        processSlide(file, body);
      }

      const subfolders = folder.getFolders();
      while (subfolders.hasNext()) {
        const subfolder = subfolders.next();
        foldersToProcess.push(subfolder.getId());
      }
    } catch (e) {
      Logger.log(`エラー: フォルダID ${currentFolderId} を見つけることができませんでした。` + e.message);
      body.appendParagraph(`エラー: フォルダID ${currentFolderId} の処理中に問題が発生しました。`).setHeading(DocumentApp.ParagraphHeading.NORMAL_TEXT);
    }
  }

  Logger.log(`ドキュメントが作成されました: ${doc.getUrl()}`);
}

// スライドを処理し、ドキュメントに書き出すヘルパー関数(修正版)
function processSlide(file, body) {
  try {
    const presentation = SlidesApp.openById(file.getId());
    body.appendParagraph(`=== ${presentation.getName()} ===`).setHeading(DocumentApp.ParagraphHeading.HEADING1);

    const slides = presentation.getSlides();
    slides.forEach((slide, index) => {
      // スライドタイトルを取得
      const pageElements = slide.getPageElements();
      let titleFound = false;
      
      for (let i = 0; i < pageElements.length; i++) {
        const element = pageElements[i];
        if (element.getPageElementType() === SlidesApp.PageElementType.SHAPE) {
          const shape = element.asShape();
          if (shape.getShapeType() === SlidesApp.ShapeType.TEXT_BOX || shape.getShapeType() === SlidesApp.ShapeType.TEXT_BOX) {
            const text = shape.getText().asString().trim();
            if (text !== '') {
              body.appendParagraph(`スライド ${index + 1}: ${text}`).setHeading(DocumentApp.ParagraphHeading.HEADING2);
              titleFound = true;
              break; // タイトルが見つかったらループを抜ける
            }
          }
        }
      }
      if (!titleFound) {
        body.appendParagraph(`スライド ${index + 1}: タイトルなし`).setHeading(DocumentApp.ParagraphHeading.HEADING2);
      }
      
      // テキストボックスのコンテンツを抽出
      for (let i = 0; i < pageElements.length; i++) {
        const element = pageElements[i];
        if (element.getPageElementType() === SlidesApp.PageElementType.SHAPE) {
          const shape = element.asShape();
          const text = shape.getText().asString().trim();
          if (text !== '') {
            body.appendParagraph(text);
          }
        }
      }
      
      body.appendHorizontalRule();
    });
  } catch (e) {
    Logger.log(`エラー: スライド ${file.getName()} (${file.getId()}) の処理中に問題が発生しました。` + e.message);
    body.appendParagraph(`エラー: スライド ${file.getName()} の処理中に問題が発生しました。`).setHeading(DocumentApp.ParagraphHeading.NORMAL_TEXT);
  }
}

指定したフォルダーの中に他にもたくさんのスライドファイルがある場合、どうするか。私の場合、生徒と活動で使うスライドファイルとか、これはやらなかったスライドファイル、みたいなのもごちゃごちゃ入っていましたので、必要なスライドファイルの名前の前にIM01タイトル、IM02タイトル、のように、IM+数字を入れました。そして、IMで始まるタイトルのスライドファイルを番号順に書き出す、というGASコードを作りました。

注意: このスクリプトを実行する前に、コード内の'フォルダーのID'を、対象のGoogleフォルダーのIDに書き換えてください。

GASコード2:IMスライドを番号順に書き出す

function exportIMSlidesFromFolderAndSubfolders() {
  const folderId = 'フォルダーのID';
  const doc = DocumentApp.create('IMスライド内容の書き出し (番号順)');
  const body = doc.getBody();

  // 抽出したスライドの情報を保存する配列
  const imSlides = [];

  const processedFolders = new Set();
  const foldersToProcess = [folderId];

  // フォルダとサブフォルダを巡回してスライドを抽出
  while (foldersToProcess.length > 0) {
    const currentFolderId = foldersToProcess.shift();
    if (processedFolders.has(currentFolderId)) continue;
    
    processedFolders.add(currentFolderId);

    try {
      const folder = DriveApp.getFolderById(currentFolderId);
      const files = folder.getFilesByType(MimeType.GOOGLE_SLIDES);
      
      while (files.hasNext()) {
        const file = files.next();
        const fileName = file.getName();
        
        // ファイル名が "IM" で始まり、その後に数字が続くかチェック
        const match = fileName.match(/^IM(\d+)/);
        if (match) {
          const number = parseInt(match[1], 10);
          imSlides.push({
            id: file.getId(),
            name: fileName,
            number: number
          });
        }
      }

      const subfolders = folder.getFolders();
      while (subfolders.hasNext()) {
        const subfolder = subfolders.next();
        foldersToProcess.push(subfolder.getId());
      }
    } catch (e) {
      Logger.log(`エラー: フォルダID ${currentFolderId} を見つけることができませんでした。` + e.message);
      body.appendParagraph(`エラー: フォルダID ${currentFolderId} の処理中に問題が発生しました。`).setHeading(DocumentApp.ParagraphHeading.NORMAL_TEXT);
    }
  }

  // 番号順にソート
  imSlides.sort((a, b) => a.number - b.number);
  
  // ソートされた順にスライドを処理
  imSlides.forEach(slideInfo => {
    processSlide(slideInfo.id, slideInfo.name, body);
  });

  Logger.log(`ドキュメントが作成されました: ${doc.getUrl()}`);
}

// スライドを処理し、ドキュメントに書き出すヘルパー関数
function processSlide(slideId, slideName, body) {
  try {
    const presentation = SlidesApp.openById(slideId);
    body.appendParagraph(`=== ${slideName} ===`).setHeading(DocumentApp.ParagraphHeading.HEADING1);

    const slides = presentation.getSlides();
    slides.forEach((slide, index) => {
      const pageElements = slide.getPageElements();
      let titleFound = false;
      
      for (let i = 0; i < pageElements.length; i++) {
        const element = pageElements[i];
        if (element.getPageElementType() === SlidesApp.PageElementType.SHAPE) {
          const shape = element.asShape();
          if (shape.getShapeType() === SlidesApp.ShapeType.TEXT_BOX) {
            const text = shape.getText().asString().trim();
            if (text !== '') {
              body.appendParagraph(`スライド ${index + 1}: ${text}`).setHeading(DocumentApp.ParagraphHeading.HEADING2);
              titleFound = true;
              break; 
            }
          }
        }
      }
      if (!titleFound) {
        body.appendParagraph(`スライド ${index + 1}: タイトルなし`).setHeading(DocumentApp.ParagraphHeading.HEADING2);
      }
      
      for (let i = 0; i < pageElements.length; i++) {
        const element = pageElements[i];
        if (element.getPageElementType() === SlidesApp.PageElementType.SHAPE) {
          const shape = element.asShape();
          const text = shape.getText().asString().trim();
          if (text !== '') {
            body.appendParagraph(text);
          }
        }
      }
      
      body.appendHorizontalRule();
    });
  } catch (e) {
    Logger.log(`エラー: スライド ${slideName} (${slideId}) の処理中に問題が発生しました。` + e.message);
    body.appendParagraph(`エラー: スライド ${slideName} の処理中に問題が発生しました。`).setHeading(DocumentApp.ParagraphHeading.NORMAL_TEXT);
  }
}

Office系のPowerPointだと、PowerPointを文書に書き出すと言う方法もありますが、それだと一つずつの作業となります。GASと同じくVBAを使えば可能だと思います。スライドはPowerPointだけどGASでやってみようかな、という人はGoogle Driveにアップロードして(設定でデフォルトで自動的にGoogle Slidesに変換されるようになっているはずですが、なっていない場合はGoogle Driveの設定で『自動的に変換』にチェックしてください。Google Driveで同期されている場合は自動で変換されません。アップロードが必要です。)やってみてください。

Amazon

そして書き出されたDocumentをLM Notebookなどでまとめてもらうことも可能です。

参考になりましたでしょうか?皆さんがどのようにスライド管理(特にスライドのタイトル)をされているか分からないのですが、とりあえず文書に書き出したら生成AIサービスを利用して、その文書をアップロードして「添付書類を授業計画としてまとめて」「シラバスとしてまとめて」「各レッスンの目標と文型、トピックをまとめて」などのプロンプトでそれなりにまとまってくると思います。

こうしてGASが気軽に使えるようになったのは、すべて@8_toriさんのおかげです。感謝🙏

コメント