ChatGPTがスピーカーノートを作成して、Googleスライドを自動で動画化するツールを作ってみた。

[少し前にGoogleスライドのスピーカーノートを読み上げて動画にするツール](https://qiita.com/katsuki_ono/items/50112b805f6b9716e724)を作りました。

いつの日か、スピーカーノートを書くことが面倒になる日が来るかもしれないので、
今度はGoogleスライドのスライドからChatGPTがスピーカーノートを作成して、スピーカーノート入りのパワーポイントとプレゼン動画を生成するツールを作りました。

今回は、スライドを動画化する際に単発で実行したら良いので、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 --upgrade pip
!pip install setuptools wheel
!pip install google-api-python-client google-auth google-auth-httplib2 google-auth-oauthlib google-cloud-texttospeech moviepy python-pptx requests openai
```

最後に、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
import openai

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

# 動画ファイルの仮置き
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)

# スピーカーノートをChatGPTが生成
def create_speaker_notes(ppt_path):
    # Load the presentation from the specified file
    presentation = Presentation(ppt_path)

    # Get total number of slides
    total_slides = len(presentation.slides)

    # Iterate over each slide in the presentation
    for index, slide in enumerate(presentation.slides):

        # Gather all the text in the slide
        slide_text = ''
        for shape in slide.shapes:
            if shape.has_text_frame:
                for paragraph in shape.text_frame.paragraphs:
                    for run in paragraph.runs:
                        slide_text += run.text

        # Choose a different prompt for the first, last, and other slides
        if index == 0:
            system_message = "スライドに合わせて内容を拡張せずにスピーカーノートを作成してください。以下のテキストはプレゼンテーションの複数あるスライドの初めのスライドから取得したものです。"
        elif index == total_slides - 1:
            system_message = "スライドに合わせて内容を拡張せずにスピーカーノートを作成してください。以下のテキストはプレゼンテーションの複数あるスライドの最後のスライドから取得したものです。"
        else:
            system_message = "スライドに合わせて内容を拡張せずにスピーカーノートを作成してください。以下のテキストはプレゼンテーションの複数あるスライドの途中のスライドから取得したものです。"

        # Use the text as input for the GPT model
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": system_message},
                {"role": "user", "content": slide_text},
            ]
        )

        # Get the generated message from the response
        generated_message = response["choices"][0]["message"]["content"]

        # Add the generated message as speaker notes to the slide
        if slide.has_notes_slide:
            notes_slide = slide.notes_slide
        else:
            notes_slide = slide.notes_slide.add_notes_slide()
        notes_slide.notes_text_frame.text = generated_message

    # Save the updated presentation to a new file
    presentation.save(ppt_path)


# スピーカーノートを音声ファイルにする
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 = f"{presentation_title}.pptx"
    download_presentation_as_pptx(presentation_id, ppt_path)

    # Call the function with your PowerPoint file
    create_speaker_notes(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")

ppt_to_video(output_path)
```

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

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

# 最後に
最近はパワーポイント自体もAIが作ってくれるツールもあるようなので、人がプレゼンを用意する必要がなくなるかもしれませんね。(AIキャラクターがプレゼンもして)
不思議な感じがします。
また何か作ってみます。