サクッとプレゼンテーション動画の作成


この記事では、PythonのライブラリであるPyMuPDF、gTTS（Google Text-to-Speech）、およびMoviePyを使用して、PDFファイルからプレゼンテーション動画を自動生成するコードについて解説します。

この記事ではトヨタ自動車の決算説明会資料を使っています。

https://global.toyota/pages/global_toyota/ir/financial-results/2025_1q_presentation_jp.pdf

https://global.toyota/pages/global_toyota/ir/financial-results/2025_1q_presentation_2_jp.pdf


完成動画
https://x.com/y81018469/status/1828382365594661065?s=46

### 使用するライブラリ
PyMuPDF (fitz): PDFファイルを読み込んでページごとに画像として保存します。

gTTS: テキストを音声ファイルに変換します。

MoviePy: 画像クリップと音声クリップを組み合わせて動画を生成します。

## 1. PDFファイルのパスと出力動画の設定
まず、PDFファイルと出力する動画ファイルのパスを指定します。

```python
pdf_path = ''
output_video_path = ''
```

### 2. 音声内容の準備
各ページの内容をひらがなでリストに記載します。このリストの要素がそれぞれのページに対応する音声になります。

```python
texts = [
    "とよたじどうしゃのにせんにじゅうごねんさんがつきだいいちしはんきけっさんをおつたえします。",
    ...
    "いじょうでせつめいをおわります。"
]
```

### 3. PDFファイルの読み込みとページごとの処理
fitz.openを使用してPDFファイルを読み込みます。各ページは画像として保存され、対応する音声が生成されます。

```python
doc = fitz.open(pdf_path)
clips = []

for i in range(len(doc)):
    page = doc[i]
    image_path = f'page_{i + 1}.png'
    pix = page.get_pixmap()
    pix.save(image_path)
```
### 4. 音声の生成と画像クリップの作成
gTTSを使用してテキストを音声に変換し、その音声をMoviePyのAudioFileClipとして読み込みます。さらに、ImageClipを作成して画像に音声を追加します。

```python
if texts[i]:
    tts = gTTS(text=texts[i], lang='ja')
    audio_path = f'page_{i + 1}.mp3'
    tts.save(audio_path)

    audio_clip = AudioFileClip(audio_path).fx(speedx, 1.5)
    img_clip = ImageClip(image_path).set_duration(audio_clip.duration)
    img_clip = img_clip.set_audio(audio_clip)
```
### 5. クリップの連結と動画の出力
すべてのクリップを連結し、指定したフレームレート（24fps）で動画ファイルを出力します。

```python
video = concatenate_videoclips(clips)
video.write_videofile(output_video_path, fps=24)
```

### 6. 一時ファイルの削除
処理が終わった後に、画像と音声の一時ファイルを削除します。

```python
os.remove(image_path)
if texts[i]:
    os.remove(audio_path)
```
### コード全体
このコードを使用することで、PDFファイルから簡単にプレゼンテーション動画を作成できます。PyMuPDFでページを画像に変換し、gTTSで音声を生成し、MoviePyでそれらを組み合わせることで、手間をかけずに動画を作成できます。
```python
import fitz  # PyMuPDF
import os
from gtts import gTTS
from moviepy.editor import ImageClip, concatenate_videoclips, AudioFileClip
from moviepy.video.fx.speedx import speedx

# PDFファイルのパス
pdf_path = ''
output_video_path = ''

# 各ページの音声内容
texts = [
    "とよたじどうしゃのにせんにじゅうごねんさんがつきだいいちしはんきけっさんをおつたえします。",
    "",
    "にほんでせいさん・はんばいだいすうがげんしょうするなか、かわせやげんかかいぜんのどりょくにより、とうきのえいぎょうりえきじっせきはいちてんさんちょうえんとなりました。とよたのくるまをごあいこいただいているせかいじゅうのおきゃくさまにあらためてかんしゃいたします。また、にんしょうもんだいやりこーるとうにより、にほんであんていしたせいさんがいじできないなかでのぞうえきは、しいれさき・はんばいてんをふくめたすてーくほるだーのみなさまのごしえんのおかげです。ひきつづき、しっかりとあしばをかため、ひとへのとうしやみらいへのとうしはいしをもってじっこうし、こんごもかつどうをかそくしてまいります。なお、つうきみとおしは、ぜんかいをすえおきました。",
    "にせんにじゅうごねんさんがつきだいいちしはんきけっさんのじっせきです。",
    "とうきのれんけつはんばいだいすうは、ぜんねんどうきにたいしてきゅうじゅうろくてんはちぱーせんととなる、にひゃくにじゅうごまんにせんだい、とよた・れくさすはんばいだいすうは、ぜんねんどうきにたいしてきゅうじゅうはちてんいちぱーせんととなる、にひゃくよんじゅうきゅうまんいっせんだいとなりました。あしばかためやにんしょうもんだい、りこーるとうのえいきょうにより、にほんのせいさん・はんばいだいすうはげんしょうしました。また、ほくべい・おうしゅう・あじあでははんばいだいすうがぞうかしました。でんどうしゃひりつはかいがいでこうちょうなえいちいーぶいをちゅうしんにぞうかし、よんじゅうさんてんにぱーせんととなりました。",
    "とうきのれんけつけっさんは、えいぎょうしゅうえき じゅういちちょうはっせんさんびゃくななじゅうはちおくえん、えいぎょうりえき いっちょうさんせんはちじゅうよんおくえん、ぜいひきまえりえき いっちょうはっせんななひゃくにじゅうにおくえん、しはんきりえき いっちょうさんせんさんびゃくさんじゅうさんおくえんとなりました。",
    "えいぎょうりえきのぞうげんよういんです。かわせへんどうのえいきょうにより、さんせんななひゃくおくえんのぞうえき、げんかかいぜんのどりょくは、ぐろすできゅうひゃくごじゅうおくえん、しいれさききばんきょうかしざいかかくでよんひゃくおくえん、ねっとでごひゃくごじゅうおくえんのぞうえきとなりました。えいぎょうめんのどりょくは、えいちいーぶいひりつじょうしょうによるこうせいのかいぜん、ばりゅーちぇーんしゅうえきのかくだいやかかくかいていにより、ななひゃくおくえんのぞうえきとなりました。しょけいひのぞうげん・ていげんどりょくは、ろうむひやけんきゅうかいはつひとうのひようがぞうかし、にせんにひゃくごじゅうおくえんのげんえき、その他については、はっぴゃくにじゅうごおくえんのげんえきとなりました。このけっか、かわせへんどう・すわっぷひょうかそんえきなどのえいきょうをのぞいたえいぎょうりえきは、せんおくえんのげんえきとなりました。じどうしゃさんぎょうぜんたいのみりょくをたかめるための、しいれさき・はんばいてんのろうむひふたん、じゅうぎょういんのかんきょうかいぜんなどにたいするとうしとみらいにむけたせいちょうとうしをちゃくじつにすいしんしてまいります。",
    "しょざいちべつについては、ごらんのとおりです。",
    "ちゅうごくじぎょうならびにきんゆうせぐめんとです。ちゅうごくじぎょうは、だいすうのげんしょうやはんばいひのぞうかとうにより、げんえきとなりました。きんゆうせぐめんとは、ゆうしざんだかのぞうかとうにより、ぞうえきとなりました。",
    "こちらはその他ざいむしひょうです。",
    "いじょうでせつめいをおわります。"
]

# PDFを開く
doc = fitz.open(pdf_path)
clips = []

# ページごとに処理
for i in range(len(doc)):
    page = doc[i]

    # ページを画像として保存
    image_path = f'page_{i + 1}.png'
    pix = page.get_pixmap()
    pix.save(image_path)

    # ページ数を音声として生成
    if texts[i]:  # 空でない場合
        tts = gTTS(text=texts[i], lang='ja')
        audio_path = f'page_{i + 1}.mp3'
        tts.save(audio_path)

        # 画像クリップを作成し、音声を追加
        audio_clip = AudioFileClip(audio_path).fx(speedx, 1.5)  # 1.5倍速に調整
        img_clip = ImageClip(image_path).set_duration(audio_clip.duration)
        img_clip = img_clip.set_audio(audio_clip)
    else:
        # 音声がないページのクリップを作成
        img_clip = ImageClip(image_path).set_duration(1)  # 音声なしで1秒表示

    clips.append(img_clip)

    # 一時ファイルを削除
    os.remove(image_path)
    if texts[i]:  # 音声が生成された場合のみ削除
        os.remove(audio_path)

# クリップを連結してビデオを作成
video = concatenate_videoclips(clips)
video.write_videofile(output_video_path, fps=24)

doc.close()

```