---
title: "MoviePyの使い方｜v2対応の動画編集コードとv1からの移行ポイント"
url: "https://www.issoh.co.jp/tech/details/6079/"
published: 2025-03-28
updated: 2026-09-27
categories: ["Python"]
publisher: "株式会社一創"
---

# MoviePyの使い方｜v2対応の動画編集コードとv1からの移行ポイント

MoviePyはPythonから動画のカット・結合・字幕合成・音声差し替えを行うライブラリで、最新版は2.2.1（2025年5月21日リリース）です。バージョン2.0で`moviepy.editor`が廃止され、`set_*`系メソッドと`fx()`によるエフェクト適用も置き換わったため、ネット上に残る大半のサンプルコードは`pip install moviepy`で入る現行版では動きません。ここでは現行版で実際に動く書き方を軸に、v1コードの移行手順、インストールとFFmpegの扱い、テキスト合成・音声処理・GIF書き出し・一括処理までをまとめます。 

## まとめ

- **最新版は2.2.1**（2025年5月21日）。`pip install moviepy`で入るのはv2系で、Python 3.9以上が前提（pip側で下限が宣言されていないため、3.8以下でもインストールは通り実行時に壊れる）。
- **v1のコードはそのままでは動かない**。`from moviepy.editor import *`は`from moviepy import *`へ、`subclip()`は`subclipped()`へ、`set_position()`など`set_*`は`with_*`へ変わった。
- **エフェクトはクラスになった**。`clip.fx(vfx.speedx, 2)`は`clip.with_effects([vfx.MultiplySpeed(2)])`と書く。`fadein`／`fadeout`も`vfx.FadeIn`／`vfx.FadeOut`に置き換わった。
- **FFmpegは手動導入しなくてよい**。依存の`imageio-ffmpeg`にバイナリが同梱されており、`pip install moviepy`だけで書き出しまで動く。`FFMPEG_BINARY`の設定はシステム側のFFmpegに差し替えるときだけ。
- **日本語字幕はフォント指定が必須**。`TextClip`の`font`を省略するとPillowの既定フォントになり、日本語は正しく描画されない。
- 向くのは**定型処理の自動化**（大量ファイルの一括変換・字幕焼き込み）。1本の動画を凝った演出で仕上げる用途にはGUI編集ソフトの方が速い。

## MoviePyでできることと、向かない用途

MoviePyはFFmpegをPythonオブジェクトの背後に隠すラッパーです。動画を`VideoFileClip`として読み込み、切り出し・結合・合成・エフェクト・音声操作をメソッドチェーンで記述し、最後に`write_videofile()`で書き出します。FFmpegのコマンドライン引数を組み立てる代わりに、Pythonの制御構文（ループ・条件分岐・ファイル走査）で編集手順そのものを書ける。[FFmpegとは？使い方とコマンド一覧を動画変換・音声抽出の実例で解説](/tech/details/5023/)で扱うコマンド単発の処理を、繰り返しと分岐つきで再現できます。 

一方で、MoviePyを選ぶべきでない場面もはっきりしています。MoviePyはフレームをPython側に取り出してから合成し直すため、その分のオーバーヘッドがFFmpegを直接叩く場合より必ず乗ります。**解像度変更・コーデック変換・無劣化カットだけが目的なら、MoviePyを挟む理由はありません**。また、カットのタイミングを目で追いながら詰めるような編集はコードでの試行錯誤に向かず、GUIの編集ソフトの方が短時間で終わります。MoviePyが効くのは「同じ手順を何十本にも適用する」「動画生成をスクリプトやCIに組み込む」といった、人手の繰り返しをコードに移す場面です。 

## MoviePy v2で変わった書き方（v1のコードが動かない理由）

2024年11月のv2.0は破壊的変更を伴うメジャーアップデートで、記事やQiitaに残るv1系のサンプルは現行版では例外を投げます。検索結果の上位に「moviepy.editorのインストール方法」といった質問が並ぶのは、この移行が原因です。まず押さえるべきは次の3点です。 

### moviepy.editorの廃止とModuleNotFoundErrorの直し方

v2では`moviepy.editor`という名前空間そのものが削除されました。`from moviepy.editor import VideoFileClip`を実行すると`ModuleNotFoundError: No module named 'moviepy.editor'`になりますが、これは**インストールの失敗ではなく、v2にモジュールが存在しない**ためです。`moviepy.editor`という別パッケージがあるわけでもないので、再インストールでは直りません。インポート行そのものを書き換えます。 

```
# v1（動かない）
from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip

# v2（現行版）
from moviepy import VideoFileClip, TextClip, CompositeVideoClip
# まとめて読み込む場合
from moviepy import *
```

### set\_\*からwith\_\*へ、subclipからsubclippedへ

v2ではクリップを変更するメソッドが「元のオブジェクトを書き換えず、変更したコピーを返す」方式に統一され、名前も`with_`始まりに変わりました。既存コードの置換対象は次のとおりです。 

| v1の書き方                        | v2の書き方                         | 用途       |
| ----------------------------- | ------------------------------ | -------- |
| `clip.subclip(10, 20)`        | `clip.subclipped(10, 20)`      | 区間の切り出し  |
| `clip.set_duration(5)`        | `clip.with_duration(5)`        | 表示時間の指定  |
| `clip.set_start(3)`           | `clip.with_start(3)`           | 合成時の開始位置 |
| `clip.set_position("center")` | `clip.with_position("center")` | 合成時の座標   |
| `clip.set_audio(bgm)`         | `clip.with_audio(bgm)`         | 音声の差し替え  |
| `clip.set_fps(30)`            | `clip.with_fps(30)`            | フレームレート  |
| `clip.resize(width=640)`      | `clip.resized(width=640)`      | リサイズ     |
| `clip.crop(x1=0, y1=0)`       | `clip.cropped(x1=0, y1=0)`     | 切り抜き     |
| `clip.rotate(90)`             | `clip.rotated(90)`             | 回転       |

### エフェクトのクラス化とwith\_effects()での適用

v1では`clip.fx(vfx.speedx, 2)`や`clip.fadein(1)`のように関数・メソッドとして適用していましたが、v2ではエフェクトが`Effect`クラスの実装になり、`with_effects()`にインスタンスのリストを渡す形に変わりました。`moviepy.video.fx.all`というインポートパスも存在しません。 

```
from moviepy import VideoFileClip, vfx, afx

clip = VideoFileClip("input.mp4")

# v1: clip.fx(vfx.speedx, 2).fadein(1).fadeout(1)
# v2: エフェクトのインスタンスをリストで渡す
clip = clip.with_effects([
    vfx.MultiplySpeed(2),      # 2倍速（旧 speedx）
    vfx.FadeIn(1),             # 1秒かけてフェードイン（旧 fadein）
    vfx.FadeOut(1),            # 1秒かけてフェードアウト（旧 fadeout）
    afx.MultiplyVolume(0.5),   # 音量を半分に（旧 volumex）
])
```

v2で使える主な映像エフェクトは`Resize`、`Crop`、`Rotate`、`MultiplySpeed`、`AccelDecel`、`FadeIn`、`FadeOut`、`CrossFadeIn`、`CrossFadeOut`、`Loop`、`MirrorX`、`MirrorY`、`BlackAndWhite`、`InvertColors`、`MaskColor`、`Margin`など。音声側（`afx`）は`MultiplyVolume`、`AudioFadeIn`、`AudioFadeOut`、`AudioNormalize`、`AudioLoop`、`AudioDelay`、`MultiplyStereoVolume`が用意されています。クラス名はいずれもキャメルケースで、v1の小文字関数名（`speedx`、`volumex`、`fadein`）はもう通りません。 

なお、v2ではImageMagick・PyGame・OpenCV・SciPyへの依存が外れ、画像処理はPillowに一本化されました。v1でImageMagickの導入に手間取った環境でも、v2なら追加インストールなしでテキスト描画まで動きます。Pillow側の画像加工と組み合わせるなら[Pillowを用いた主要な画像加工テクニック](/tech/details/1343/)が参考になります。 

## インストール・FFmpeg・バージョン確認

### pipでの導入と仮想環境

導入は`pip install moviepy`だけです。プロジェクトごとに仮想環境を切っておくと、v1に依存した既存スクリプトと共存させられます。 

```
python -m venv .venv
source .venv/bin/activate      # Windowsは .venv\Scripts\activate
pip install moviepy
```

condaを使う環境では`conda install -c conda-forge moviepy`で導入できます。conda-forgeも2.2.1を提供しているためv2が入りますが、既存環境の依存解決の結果しだいで古い版に落ちることはあるので、導入後に次項の手順でバージョンを確認してください。pipとの使い分けは[pipとcondaの違いと使い分け｜Anaconda・Miniconda・商用ライセンスまで解説](/tech/details/7775/)で整理しています。 

### FFmpegの自動同梱と、差し替え時の設定

FFmpegの手動インストールとPATH設定は不要です。依存パッケージの`imageio-ffmpeg`にFFmpegバイナリが同梱されており、`pip install moviepy`の時点で一緒に入ります（公式ドキュメントには「初回利用時にImageIOがダウンロードする」と書かれていますが、現行のimageio-ffmpegはwheelに同梱済みで、実行時のダウンロードは発生しません）。社内のビルド済みFFmpegやハードウェアエンコード対応版に差し替えたい場合だけ、MoviePyのオブジェクトをインポートする前に環境変数を設定します。 

```
import os
# 差し替えるときだけ有効化する（既定は同梱バイナリ = "ffmpeg-imageio"）
# os.environ["FFMPEG_BINARY"] = "auto-detect"                  # PATH上のffmpegを使う
# os.environ["FFMPEG_BINARY"] = "/usr/local/bin/ffmpeg"   # 特定のバイナリを指定

from moviepy.config import check
check()   # 検出されたバイナリを表示
```

**`auto-detect`を安易に設定してはいけません**。PATH上に`ffmpeg`が無い環境ではこの値が`unset`に落ち、同梱バイナリも使われなくなって`MoviePy: can't find or access ffmpeg in 'unset'.`で書き出しが全て失敗します。FFmpegを手動導入していない読者は、この2行をコメントアウトしたままにしてください。なお`preview()`で再生確認をする場合だけは`ffplay`が別途必要です。バージョンごとの対応コーデックは[FFmpeg 8.1の新機能と8.0からの変更点｜インストール方法・最新版8.1.2まで解説](/tech/details/11447/)を確認してください。 

### バージョン確認と、v1に固定する場合の注意

バージョンは`pip show`で確認します。`moviepy.__version__`は使わないでください。2.2.1をインストールしても、同梱の`version.py`が更新されておらず`2.1.2`と表示されるため、「古い版が入った」と誤認します。 

```
pip show moviepy            # Version: 2.2.1 と表示される
# あるいは
python -c "import importlib.metadata as m; print(m.version('moviepy'))"
```

既存のv1コードを今すぐ書き換えられない場合は、`pip install "moviepy<2"`でv1.0.3に固定できます。ただしv1.0.3は2020年のリリースで開発が止まっており、新しいPythonでは依存パッケージの解決に失敗することがあります。固定するならPythonの版も併せて固定してください。**あくまで移行までの時間稼ぎと割り切り、上の対応表に沿った置換を進める方が結局は早く済みます**。 

## 動画の読み込み・カット・結合・書き出し

### 読み込みと動画情報の取得

```
from moviepy import VideoFileClip

clip = VideoFileClip("input.mp4")
print(clip.duration)   # 尺（秒・float）
print(clip.fps)        # フレームレート
print(clip.size)       # [幅, 高さ]
clip.close()           # 明示的に解放（ループ処理では必須）
```

大量のファイルをループで処理するときは`close()`を呼ぶか`with`文で扱ってください。開いたままだとFFmpegのサブプロセスとファイルハンドルが残り続け、OSのファイルディスクリプタ上限（macOSの既定は`ulimit -n`で256）に達すると`Too many open files`で落ちます。 

### 区間の切り出し（subclipped）と結合

```
from moviepy import VideoFileClip, concatenate_videoclips

clip = VideoFileClip("input.mp4")

# 10秒〜20秒を切り出す（v1のsubclipに相当）
part1 = clip.subclipped(10, 20)
# 末尾から5秒前まで（負の値は終端からの相対指定）
part2 = clip.subclipped(30, -5)

# 複数シーンを連結。解像度やfpsが違う素材はmethod="compose"を指定する
final = concatenate_videoclips([part1, part2], method="compose")
final.write_videofile("output.mp4")
```

`method="chain"`（既定）は素材の解像度が揃っている前提で高速に連結します。サイズの異なる動画を混ぜると出力が破綻するため、素材が不揃いなら`method="compose"`を使い、事前に`resized()`で幅を揃えておきます。 

### write\_videofileの主要引数と処理時間

```
final.write_videofile(
    "output.mp4",
    codec="libx264",       # MP4の標準的な選択
    audio_codec="aac",     # 音声が無音になる場合はここを明示
    fps=30,
    threads=4,             # エンコードのスレッド数
    preset="medium",       # ultrafast〜veryslow。速度と圧縮率のトレードオフ
    bitrate="4000k",
)
```

`preset`はx264に渡される設定で、`ultrafast`にすればエンコードは速くなりますが同じ画質でファイルサイズが膨らみます。確認用の書き出しは`ultrafast`、納品用は`medium`以上、という切り替えが現実的です。`threads`を増やしても、フレーム生成がPython側で行われる合成処理（テキスト重ね・エフェクト）はスレッド数に比例して速くはなりません。**ボトルネックはエンコードではなくフレーム合成側にあることが多く、まず解像度を落として試すのが確実です**。 

## テキストと画像の合成（TextClip / ImageClip）

### TextClipと日本語フォントの指定

v2の`TextClip`は`TextClip(font=None, text=None, font_size=None, color='black', ...)`という署名で、`font`を省略するとPillowの既定フォントが使われます。この既定フォントは日本語のグリフを持たないため、**日本語字幕では必ずTTF/OTFのパスを渡してください**（省略すると豆腐や空白になります）。 

```
from moviepy import VideoFileClip, TextClip, CompositeVideoClip

clip = VideoFileClip("input.mp4")

txt = TextClip(
    font="/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
    text="サンプル字幕",
    font_size=48,
    color="white",
    stroke_color="black",
    stroke_width=2,
    method="caption",      # sizeに合わせて自動改行する
    size=(clip.size[0] - 80, None),
).with_duration(5).with_start(2).with_position(("center", "bottom"))

CompositeVideoClip([clip, txt]).write_videofile("subtitled.mp4")
```

`method="label"`（既定）はテキスト幅に合わせた最小サイズのクリップを作り、改行はしません。長い字幕を折り返したいときは`method="caption"`と`size`をセットで指定します。なお`method="label"`で`size`を指定しない場合、`font_size`は省略できません（`Font size is mandatory when method is label and size is None`で例外になります）。 

### ロゴ画像の重ね合わせと位置指定

```
from moviepy import VideoFileClip, ImageClip, CompositeVideoClip

clip = VideoFileClip("input.mp4")

logo = (ImageClip("logo.png")
        .with_duration(clip.duration)
        .resized(height=80)
        .with_position((30, 30)))     # 左上から30pxずつ

CompositeVideoClip([clip, logo]).write_videofile("branded.mp4")
```

`with_position()`には座標のタプルのほか、`("right", "bottom")`のような文字列、`lambda t: (100 + 50 * t, 200)`のような時間関数を渡せます。時間関数を使うと、テロップを横に流すアニメーションが数行で書けます。重ねる順序は`CompositeVideoClip`のリスト順（後ろほど手前）です。 

## 音声の抽出・BGM差し替え

動画の音声は`clip.audio`として`AudioClip`の形で取り出せます。抽出・音量調整・差し替えはいずれもこの属性を経由します。 

```
from moviepy import VideoFileClip, AudioFileClip, afx

clip = VideoFileClip("input.mp4")

# 音声だけを書き出す
clip.audio.write_audiofile("voice.mp3")

# BGMを動画の尺に合わせ、音量を30%・末尾2秒でフェードアウト
bgm = (AudioFileClip("bgm.mp3")
       .subclipped(0, clip.duration)
       .with_effects([afx.MultiplyVolume(0.3), afx.AudioFadeOut(2)]))

clip.with_audio(bgm).write_videofile("with_bgm.mp4", audio_codec="aac")
```

元の音声を残したままBGMを重ねる場合は`CompositeAudioClip([clip.audio, bgm])`で合成してから`with_audio()`に渡します。BGMが動画より短いと途中で無音になるため、`afx.AudioLoop(duration=clip.duration)`でループさせるか、上のように`subclipped()`で尺を揃えます。書き出し時に`audio_codec="aac"`を省略すると環境によって音声が入らないことがあるので、MP4では明示しておくのが安全です。 

## GIF書き出しと一括処理の自動化

GIFは`write_gif()`で書き出します。切り出し・リサイズと組み合わせて、動画の一部をサムネイル代わりのアニメーションにするのが典型的な使い方です。 

```
from moviepy import VideoFileClip

clip = VideoFileClip("input.mp4")

# 冒頭3秒を幅480px・12fpsのGIFに（尺とfpsを削るのが容量対策の要）
(clip.subclipped(0, 3)
     .resized(width=480)
     .write_gif("preview.gif", fps=12))
```

GIFはフレームごとに画像を持つため、10秒を超える素材やフルHDのままの書き出しは容量が数十MBに膨らみます。尺を数秒に絞り、幅を480〜640px、fpsを10〜15に落とすのが実用的な範囲です。 

MoviePyの本領は、こうした処理をファイル単位のループに落とし込むところにあります。次のスクリプトは、フォルダ内の全MP4に同じロゴとフェードを適用して書き出します。 

```
from pathlib import Path
from moviepy import VideoFileClip, ImageClip, CompositeVideoClip, vfx

src_dir = Path("raw")
out_dir = Path("edited")
out_dir.mkdir(exist_ok=True)

for path in sorted(src_dir.glob("*.mp4")):
    with VideoFileClip(str(path)) as clip:
        logo = (ImageClip("logo.png")
                .with_duration(clip.duration)
                .resized(height=60)
                .with_position(("right", "top")))
        final = (CompositeVideoClip([clip, logo])
                 .with_effects([vfx.FadeIn(0.5), vfx.FadeOut(0.5)]))
        final.write_videofile(
            str(out_dir / f"{path.stem}_edited.mp4"),
            codec="libx264",
            audio_codec="aac",
            logger=None,        # 進捗バーを抑止
        )
```

定期実行に載せるなら、このスクリプトをcronやタスクスケジューラから呼びます。処理時間は素材の尺・解像度・エフェクト数に比例して伸びるので、実行間隔は実際の1本あたりの所要時間と本数から逆算し、前回の処理が終わらないうちに次が走らないようロックファイルを併用してください。 

## よくある質問

### moviepy.editorがModuleNotFoundErrorになるのはなぜですか

v2.0で`moviepy.editor`という名前空間が削除されたためです。インストールの失敗ではないので再インストールしても直りません。`from moviepy import VideoFileClip`のように直接インポートするか、どうしても既存コードを変えられない場合のみ`pip install "moviepy<2"`でv1.0.3に固定します。 

### pip install moviepyだけでFFmpegも使えるようになりますか

なります。依存の`imageio-ffmpeg`にFFmpegバイナリが同梱されているため、`pip install moviepy`だけで書き出しまで動きます。別途インストールが必要なのは、`preview()`で再生確認する場合の`ffplay`と、独自ビルドのFFmpegに差し替えたい場合だけです。 

### condaでインストールできますか

`conda install -c conda-forge moviepy`で導入できます。conda-forgeも2.2.1を提供しているのでv2が入りますが、既存環境の依存解決しだいで古い版に落ちることはあります。導入後に`pip show moviepy`でバージョンを確認してください（`moviepy.__version__`は2.2.1でも`2.1.2`を返すため判定に使えません）。 

### MoviePyのライセンスは商用利用できますか

MoviePyはMITライセンスで、商用利用も可能です。ただし内部で利用するFFmpegはビルド構成によってライセンス（LGPL／GPL）が変わります。製品に同梱する場合は、MoviePyではなく同梱するFFmpegバイナリのライセンス条件を確認してください。 

### write\_videofileが遅いのですが、threadsやpresetで速くなりますか

効果は限定的です。`threads`と`preset`はエンコード段（x264）に効きますが、テキスト合成やエフェクトを含む処理ではフレーム生成がPython側で走るため、そちらがボトルネックになります。まず`resized()`で解像度を落とす、エフェクトの数を減らす、確認用は`preset="ultrafast"`にする、という順で試すのが効率的です。 

## 関連記事

- [FFmpegとは？使い方とコマンド一覧を動画変換・音声抽出の実例で解説](/tech/details/5023/)
- [FFmpeg 8.1の新機能と8.0からの変更点｜インストール方法・最新版8.1.2まで解説](/tech/details/11447/)
- [Pillowを用いた主要な画像加工テクニック](/tech/details/1343/)
- [pipとcondaの違いと使い分け｜Anaconda・Miniconda・商用ライセンスまで解説](/tech/details/7775/)

---

出典: [MoviePyの使い方｜v2対応の動画編集コードとv1からの移行ポイント](<https://www.issoh.co.jp/tech/details/6079/>)（株式会社一創）
