GASを使ってパワポカラオケのスライドを自動生成する

# パワポカラオケとは？

何も言わず下記の動画を見てください。私はオードリーが大好きです。(すべて「【公式】オードリーさん、ぜひ会ってほしい人がいるんです。」の公式YouTubeチャンネルからです)

* [新感覚大喜利ゲーム！パワポカラオケ](https://www.youtube.com/watch?v=fNVkmb9apYA)
* [【さよなら中内さん】構成作家の本気のパワポカラオケ披露 中内こもる編](https://www.youtube.com/watch?v=_tVh4fNn7io)
* [【さよなら中内さん】構成作家の本気のパワポカラオケ披露 山口トンボ編](https://www.youtube.com/watch?v=TvEKZvBqpv0)

...みんなすごいセンスですよね。チャレンジしてみたくなりますよね。


# きっかけ

社内でパワポカラオケをやろうと思うのですが、**画像をランダムで選んだり、スライドを作ったりするのって自動化できるんじゃないの？**

→ できました。

# GASでやっていること

* Googleドライブの `images` フォルダに画像を適当に入れておく
* スプレッドシートに必要事項を入れてボタンを押す
* 指定した枚数の画像がランダムで選ばれたスライドが出来上がる

# フォルダ構成

![folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/0a2c0ec3-e58e-0905-d7c7-21451b0958cb.png)

# imagesフォルダの中

![images.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/031f1911-4453-4422-0172-8e8322dcc1ad.png)

今回は [ぱくたそ](https://www.pakutaso.com/)さんから画像をいただきました。
ぱくたそ の何がスゴイって、**サイトの右上に「ガチャ」というボタンがありまして、これを押すと膨大な画像の中から一枚をランダムで表示してくれる**んですよね。

…あれ？だったらこのサイトだけでパワポカラオケが成立するんじゃないの？って言っちゃダメ！ (>_<)



# スプレッドシートの設定

![sheet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/250f5624-f3b6-b648-28b0-f77d8aba9835.png)

黄色いところに入力して、ボタンを押すだけ。
ボタンを押すと GASの `main()` が動くように設定してあります。
1.のフォルダは `images` フォルダで、
3.のフォルダは `パワポカラオケジェネレーター` フォルダ のことです。

# GASの設定

このスプレッドシートのスクリプトエディタに下記を書いてあります。

```js:main
const SLIDE_FOLDER_ID  = "xxxxxxxxxxxxxxx"; // 「パワポカラオケジェネレータ」フォルダのID
const IMAGES_FOLDER_ID = "xxxxxxxxxxxxxxx"; // 「images」フォルダのID

/**
 * スプレッドシートのボタンが押されたらここが起動
 */
function main() {
  const sheet = SpreadsheetApp.getActive().getSheetByName("使い方");
  const imageCount = sheet.getRange("D14").getValue();
  const fileName = sheet.getRange("D15").getValue();
  const theme = sheet.getRange("D13").getValue();

  if(!imageCount || !fileName || !theme) throw new Error("入力に不備があります");

  generateSlide(fileName, theme, imageCount);
}
```

```js:generateSlide
/**
 * 本体はここ
 */
function generateSlide(fileName, theme, imageCount) {
  // 新規スライド作成
  const presentation = createNewPresentation(fileName);

  // 1ページ目の設定
  setTopSlide(presentation, theme, imageCount);

  // 画像を選ぶ
  const selectedImages = selectImages(imageCount);

  // スライドを追加し、画像を一枚配置する
  for (let i = 0; i < imageCount; i++) {
    // 空白スライドを追加する
    const appendedSlide = presentation.appendSlide(SlidesApp.PredefinedLayout.BLANK);

    // 画像を追加する
    const image = appendedSlide.insertImage(selectedImages[i].getBlob());

    // 画像を中央揃えにする
    const imageWidth = image.getWidth();
    const imageHeight = image.getHeight();
    const pageWidth = presentation.getPageWidth();
    const pageHeight = presentation.getPageHeight();
    const x = (pageWidth / 2) - (imageWidth / 2);
    const y = (pageHeight / 2) - (imageHeight / 2);
    image.setLeft(x).setTop(y);
  }
}
```



```js:createNewPresentation
/**
 * 新規プレゼンテーションを作成
 * @param {string} fileName
 * @return {object} プレゼンテーションオブジェクト
 */
function createNewPresentation(fileName) {
  // 新規スライドをマイドライブに作る
  const presentation = SlidesApp.create(fileName);
  const presentationId = presentation.getId();

  // 指定したフォルダに移動する
  const file = DriveApp.getFileById(presentationId);
  const folder = DriveApp.getFolderById(SLIDE_FOLDER_ID);
  file.moveTo(folder);

  return presentation;
}
```

```js:setTopSlide
/**
 * 1ページ目の設定
 * @param {object} presentation プレゼンテーションオブジェクト
 * @param {string} theme パワポカラオケのテーマ
 * @param {number} imageCount 画像の枚数
 */
function setTopSlide(presentation, theme, imageCount) {
  const slide = presentation.getSlides()[0];
  const shapes = slide.getShapes();

  const titleShape = shapes[0];
  const titleTextRange = titleShape.getText();
  titleTextRange.setText(`テーマ: ${theme}`);

  const subTitleShape = shapes[1];
  const subTitleTextRange = subTitleShape.getText();
  subTitleTextRange.setText(`${imageCount}枚のスライドでプレゼンします！`);
}
```

```js:selectImages
/**
 * imagesフォルダにある画像から指定された枚数の画像をランダムで選ぶ
 * @param {number} imageCount 何枚選ぶか
 * @return {array} file の配列
 */
function selectImages(imageCount) {
  const imageFolder = DriveApp.getFolderById(IMAGES_FOLDER_ID);
  const fileIterator = imageFolder.getFiles();

  // imagesフォルダ内のファイルを配列にする
  const images = [];
  while (fileIterator.hasNext()) {
    images.push(fileIterator.next());
  }

  if (imageCount > images.length) throw new Error("imagesフォルダに入っている画像が不足しています");

  // ランダムで画像を選ぶ
  const selectedImages = [];
  for (let i = 0; i < imageCount; i++) {
    const x = Math.floor(Math.random() * images.length);
    selectedImages.push(images.splice(x, 1)[0]);
  }
  return selectedImages;
}
```

# 実行結果

こんな感じでスライドが生成されます。
実際に使う時は、発表者とは別な人がこのスライドを作成して、発表者のプレゼンに合わせてスライド送りをしてくださいね。事前に発表者に見せちゃダメですよ！

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8612/d5be0537-4d8e-4c30-a44b-2120945f8f88.png)

# 感想

GoogleスライドをGASで操作する、を初めてやってみました。
オブジェクトがいっぱいあって大変でした。


```
SlideApp
 └ Presentation
　  └Slide
　　  └Shape
　　　  └TextRange
```

という構造のようなのです。



# 参考

- [GASでGoogleスライドのSlideオブジェクトの概要とその取得方法](https://tonari-it.com/gas-slides-getslides/)
- [\[Google Apps Script\]Google ドライブにある画像をスライドに一括挿入する \| 初心者備忘録](https://www.ka-net.org/blog/?p=10883)