Googleスライドを自動で動画化するツールを作ってみた。


私は発話出来ないのですが、プレゼンする機会がそこそこあり、その度に代読してもらったり、スライドを一枚ずつ手動で動画化していました。
しかしながら、これはとても手間なので、Googleスライドを自動で動画化するツールを作ってみました。

今回は、スライドを動画化する際に単発で実行したら良いので、Google colabを使用します。

手順は以下です。
1. 各Google APIの有効化、及び、サービスアカウントのメールアドレスを動画化するスライドに入れる
2. スライドを動画にするプログラムを実行する

# 1. 各Google APIの有効化、及び、サービスアカウントのメールアドレスを動画化するスライドに入れる
## 1.1. 各Google APIの有効化
各Google APIの有効化の手順は以下です。
1. [Google Cloud Console](https://console.cloud.google.com/) にアクセスし、プロジェクトを作成または選択します。
2. [Google Slides APIページ](https://console.cloud.google.com/apis/library/slides.googleapis.com)に移動し、「有効にする」をクリックして API を有効化します。
3. [Google Drive API ページ](https://console.cloud.google.com/apis/library/drive.googleapis.com) に移動し、「有効にする」をクリックして API を有効化します。
4. [Cloud Text-to-Speech APIページ](https://console.cloud.google.com/apis/library/texttospeech.googleapis.com)に移動し、「有効にする」をクリックして API を有効化します。この際、Cloud Text-to-Speech APIの有効化にはクレジットカードの登録が必要です。
5. 必要に応じて同意画面を設定します。
6. 画面左側のメニューから「認証情報」をクリックし、「認証情報を作成」を選択して「サービスアカウント」をクリックして、必要事項を記入してサービスアカウントを作成します。
7. 作成したサービスアカウントの編集ページを開いて、詳細からサービスアカウントのメールアドレスを取得してメモします。次にキーのタブを開いて、JSONのキーを作成してダウンロードします。
8. 作成したサービスアカウントのキーをGoogle colabで使用するアカウントのGoogleドライブに入れます。

## 1.2. サービスアカウントのメールアドレスを動画化するスライドに入れる
サービスアカウントのメールアドレスを動画化するスライドに入れるの手順は以下です。
1. GoogleドライブかGoogleスライドを開いて、動画化するスライドの共有設定に先程のサービスアカウントのメールアドレスを追加します。
2. 動画化するスライドの共有設定を誰でも閲覧可能にします。(スライドを画像としてダウンロードする際に必要な設定です。)

# 2. スライドを動画にするプログラムを実行する
まず、Google colabで新規ノートブックを開き、Google colabからGoogle ドライブにアクセスして、サービスアカウントのキーを読み込めるように以下の操作を行います。

ファイルを開く
写真の赤枠のマークを押す。
![ECEC3A64-9001-4920-BBB8-BB2D6042E2F0.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/621e164a-9581-d55e-b1f2-700416311558.jpeg)

Googleドライブをマウントする
写真の赤枠のマークを押す。メニューが開いたら、ドライブに接続を押す。
![75EE7CC2-6328-4BFC-99DB-EE8025D72621.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287642/0bf7e31c-6f23-1b30-b897-72924a71809b.jpeg)

次に、以下をインストールします。
```
!pip install google-api-python-client google-auth google-auth-httplib2 google-auth-oauthlib google-cloud-texttospeech moviepy python-pptx requests
```

最後に、key_pathには先程Googleドライブに入れたサービスアカウントのキーのパス、presentation_idには動画化するスライドのIDを入れてから、以下のコードを実行します。
※スライドのIDはスライドのURLのpresentation/d/←ここの部分→/editです。
※スライド画像のダウンロードのwidth=1920&height=1080には、動画化するスライドのアスペクト比で指定してください。こちらでは、一般的なGoogleスライドのアスペクト比で指定しています。
``` python
import os
import time
import requests
from moviepy.editor import *
import numpy as np
from google.cloud import texttospeech
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
from google.oauth2 import service_account
from pptx import Presentation

# 各種パスと設定を指定
key_path = "/path/to/your/credentials.json"
presentation_id = 'your_presentation_id'

# 動画ファイルの仮置き
output_path = "output_video.mp4"

# 認証用情報を読み込み
credentials = service_account.Credentials.from_service_account_file(
    key_path,
    scopes=["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/cloud-platform"]
)

# Google Slides API クライアントを構築
slides_service = build('slides', 'v1', credentials=credentials)
drive_service = build('drive', 'v3', credentials=credentials)

# スピーカーノートを音声ファイルにする
def save_speech_to_file(text, output_path):
    client = texttospeech.TextToSpeechClient(credentials=credentials)
    synthesis_input = texttospeech.SynthesisInput(text=text)
    voice = texttospeech.VoiceSelectionParams(
        language_code="ja-JP",
        ssml_gender=texttospeech.SsmlVoiceGender.FEMALE
    )
    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.LINEAR16
    )
    response = client.synthesize_speech(
        input=synthesis_input, voice=voice, audio_config=audio_config
    )
    with open(output_path, "wb") as out:
        out.write(response.audio_content)

def download_presentation_as_pptx(presentation_id, output_path):
    request = drive_service.files().export_media(fileId=presentation_id, mimeType='application/vnd.openxmlformats-officedocument.presentationml.presentation')
    with open(output_path, 'wb') as f:
        downloader = MediaIoBaseDownload(f, request)
        done = False
        while done is False:
            status, done = downloader.next_chunk()
            print("Download %d%%." % int(status.progress() * 100))

def ppt_to_video(output_path, slide_duration=5):
    # Google Slides APIでスライドの情報を取得
    presentation = slides_service.presentations().get(presentationId=presentation_id).execute()
    slides = presentation.get('slides')
    presentation_title = presentation.get('title')

    # Google SlidesをPowerPointに変換してダウンロード
    ppt_path = "temp.pptx"
    download_presentation_as_pptx(presentation_id, ppt_path)

    # PowerPointファイルを開く
    prs = Presentation(ppt_path)

    # 一時ディレクトリを作成する
    os.makedirs("temp_audio", exist_ok=True)
    os.makedirs("temp_images", exist_ok=True)

    slide_audios = []
    images = []

    for i, slide in enumerate(slides):
        # スライド画像をダウンロード
        img_url = f'https://docs.google.com/presentation/d/{presentation_id}/export/png?id={presentation_id}&pageid={slide["objectId"]}&width=1920&height=1080'
        img_data = requests.get(img_url).content
        img_filename = f'temp_images/slide_{i + 1}.png'
        with open(img_filename, 'wb') as f:
            f.write(img_data)
        images.append(img_filename)

        # PowerPointからスピーカーノートを取得する
        speaker_notes = prs.slides[i].notes_slide.notes_text_frame.text

        # テキストを読み上げ、音声ファイルとして保存する
        audio_path = f"temp_audio/slide_{i}.wav"
        save_speech_to_file(speaker_notes, audio_path)

        slide_audios.append(audio_path)

    # 画像と音声を組み合わせて動画を作成する
    clips = []
    for img, audio_path in zip(images, slide_audios):
        audio = AudioFileClip(audio_path)
        img = ImageClip(img, duration=audio.duration)
        img = img.set_audio(audio)
        clips.append(img)

    # 動画を結合する
    final_video = concatenate_videoclips(clips, method="compose")
    final_video.fps = 24
    output_path = f"{presentation_title}.mp4"
    final_video.write_videofile(output_path, codec="libx264", audio_codec="aac", bitrate="8000k")

    # 一時ディレクトリを削除する
    for file in slide_audios:
        os.remove(file)
    os.rmdir("temp_audio")
    for file in images:
        os.remove(file)
    os.rmdir("temp_images")

    # パワーポイントファイルを削除
    os.remove(ppt_path)

ppt_to_video(output_path)
```

実行後、数分後にスライド名の動画ファイルが生成されます。(スライド数が25枚ほどでスピーカーノートが4000字ほどだと25分くらい動画生成にかかります。)

Google colabから動画をダウンロードして、上手く動画が再生出来たら、成功です！

# 最後に
スライドの動画化ツールはとても便利ですね。
今よりプレゼン機会の多かった院時代に欲しかった…なんて思います。
また何か作ってみます。