Library / SDK
Zulko/moviepy avatar
Zulko/moviepy

moviepy's manifest says 2.2.0 while 2.2.1 is published, and the maintainers are asking for help

Video editing with Python

15,150 stars2,156 forksPythonMIT

At a glance

What is it?
MoviePy is a Python video editing library that loads media into arrays so every pixel is reachable from code, released under the MIT licence and classified as production stable. Two things in the repository undercut that framing. The package manifest declares a version that is behind the newest published release, and the same front page that carries the stability classifier ends with an appeal for maintainers saying there are times when nobody has enough bandwidth. Its own headline example references a media file that is not in the repository.
Who is it for?
This is the right tool when you need to reach pixels and samples rather than hand a filter graph to a command line, because the array representation is the whole point and it is what lets you write an effect in a few lines. It is the wrong tool for throughput, because the front page says plainly that it is slower than driving the encoder directly and gives the reason.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 81 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • Zulko/moviepy15,150 ★ · +235
Star history
Repository2026-09-212026-11-14
Zulko/moviepy14.9k15.2k

235 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The manifest declares a version behind the one already published

The package manifest and the release list do not agree, and the direction of the disagreement is unusual.

The manifest declares the version as 2.2.0. The most recent published releases are two tags from the same day in May 2025: one for 2.2.0 and one for a patch above it.

So the newest published version is a patch release higher than what the source tree declares. The default branch has taken pushes since, most recently in August 2026, more than a year after that release.

This is the opposite of the common case, where the source tree runs ahead of the last tag. Here the tree appears to have been rewound or reset after the release, or the patch was cut from a branch that was never merged forward.

The consequence is about what you get from each route. A package index install gives you the patch release. A source install from the default branch gives you whatever the manifest says, which is nominally older.

So if you are comparing behaviour between a deployed environment and a fresh checkout, you may be running different builds. And if you maintain a fork, the version in the manifest is the one your users will report when something breaks, which may not be the code they are running.

Check the installed package metadata rather than assuming the branch matches what you have.

The stability classifier and the maintainers appeal appear on the same page

The front page makes a claim about this project's maturity and then, several paragraphs later, asks for help maintaining it. Both are true and they sit together without comment.

The package metadata classifies the project as a production and stable release in the standard development-status scheme. That is a strong claim, and it is the one a package index will display.

Then the maintainers section, after listing four active maintainers and crediting six past ones, carries an appeal headed with an exclamation. It says the library has only been kept afloat by the involvement of its maintainers and that there are times when none of them have enough bandwidth. It asks for developers interested in helping, specifically mentioning solving issues that affect them or reviewing pull requests.

Read together, the picture is a widely used library with real users, a stable public API, and a small number of people doing the work.

The maintainer list names the version 2 rewrite explicitly: one credited maintainer is listed as having led that development, with a link to the merge request.

So the consequence is that the stability classifier is a statement about the API surface and the install experience, not about response time or about how many maintainers are watching the tracker. If you are adopting this, the number to look at is the issue count, and the front page is telling you the bus factor is small.

The version 2 rewrite removed a module that most examples on the internet import

There is a note at the top of the front page about the current major version, and it is the most important thing on the page for anyone with existing code.

The library recently moved to version 2 with major breaking changes. The previous version's documentation is still reachable, with a warning that it is no longer maintained. There is a separate guide for updating code from the previous version to the current one.

What is not spelled out on the front page is the specific break that catches people with old code.

The single-module import path from version 1 no longer exists in version 2. Code and tutorials written against version 1 import from that path, and on version 2 that import fails immediately.

The example on the front page shows the version 2 style: the library is imported from its top-level name and the clip classes come from there, with effects applied by chained methods.

Compare that with the older tutorials and you can see the whole migration: a namespace module became a top-level import.

So the consequence is that upgrading is a real code change rather than a version bump, and any snippet you find by searching may be for a version you cannot install without rewriting it.

Every pixel becomes a Python array, and the front page names that as the reason it is slow

The architecture section explains the design in one sentence and then names its cost in the next paragraph, which is rarer than it should be.

The description is that under the hood the library imports media, video frames, images and sounds, and converts them into Python objects, specifically arrays, so that every pixel becomes accessible and effects can be defined in a few lines.

So a video is not a stream that an effect consumes. It is a sequence of arrays in memory that Python code indexes. That is what makes a custom effect a few lines rather than a filter graph, and it is also why the library can do things that a wrapper around a command-line tool cannot.

The cost is stated immediately after. Because of the heavier data import and export operations, it is slower than using the encoder directly.

And the library provides the mixing operations separately: concatenation, playing clips side by side, playing them on top of each other with transparency. The final result is then encoded back into a file.

So the round trip through arrays happens on both ends. Decode into memory, work on it, encode out.

The consequence is a clear dividing line. If your job is a handful of cuts and titles on a small file, the flexibility is worth it. If your job is transcoding a library of video, you are paying for a representation you do not need, and the front page has told you so before you measured.

The container recipe installs the package four times in one instruction

The container recipe is short and has a line in it that does more work than it needs to.

It starts from an unversioned Python base image, so the interpreter floats to whatever is newest. Then it installs the encoder from the system package manager, and a font package with a comment saying the project uses them in testing.

Then it sets up a UTF-8 locale explicitly, which matters for a library that renders text into video frames, and upgrades the package installer.

And then the line: it changes into the source directory and installs the package, then installs it again with the test extra, then again with the documentation extra, then again with the lint extra. Four install invocations.

Each of those resolves the same package from the same directory and differs only in which optional dependencies it pulls. The result is correct and the work is three times more than it needs to be, which on a layer that is cached in most pipelines is invisible until someone invalidates it.

The extras themselves are worth noting. The test extra pins the test runner to a range below a major version boundary that is several years old. The lint extra is eight packages, four of which are plugins for a single linter.

The offending line is a single instruction:

code
RUN cd /moviepy && pip install . && pip install .[test] && pip install .[doc] && pip install .[lint]

So the consequence is that this is a developer image rather than a production one, which the front page never claims otherwise, but it is worth knowing before you base something on it.

The headline example loads a media file that the repository does not contain

The example at the top of the front page cannot be run as written, and that is worth knowing before you copy it.

It opens a file with a path pointing into a directory of long examples, and that path is the input to the first call in the snippet.

The repository's top level contains a media directory and an examples directory. The examples directory holds soundtrack.py. There is no directory of long examples at the top level.

So the file the example opens is not there. Which means the one piece of code a newcomer is most likely to copy fails on the first line with a file-not-found error rather than anything to do with the library.

The rest of the snippet is fine and is worth reading on its own merits, because it shows the version 2 idiom throughout. It chains a subclip onto a loaded clip, scales the audio volume, builds a text clip with a font, a size and a colour, gives it a duration and a position, and overlays it. Every operation returns a value rather than mutating.

So the consequence is to substitute your own file before you conclude anything about whether the example works.

Documentation is built by two different systems on two different platforms

The repository carries configuration for several automation services.

The front page says the online documentation is built automatically on every push to the default branch. For a local build, you install the documentation extra, which brings in a documentation generator and a theme package plus a design extension, then change into the docs directory and run the make command.

Now look at the top of the repository. There is a configuration file for a hosted documentation platform, a continuous integration configuration for a Windows build service, and a directory for the platform that runs on pull requests.

So there are three automation systems configured: one for hosted documentation, one for Windows builds, and one for pull request checks. The front page describes only the hosted documentation build.

The documentation extra pins the generator to a major version and the theme to an exact version.

So if you are looking for why a continuous integration job runs on a Windows service, the answer is in the repository rather than the front page.

Editorial conclusion

This is the right tool when you need to reach pixels and samples rather than hand a filter graph to a command line, because the array representation is the whole point and it is what lets you write an effect in a few lines. It is the wrong tool for throughput, because the front page says plainly that it is slower than driving the encoder directly and gives the reason. Two things to check first. Whether your code is on version 1 or 2, because version 2 removed a module that most published examples import and version 1 is no longer maintained, so there is no fix that leaves you where you started. And whether your platform has a working encoder available, since the dependencies include imageio_ffmpeg and the container recipe installs ffmpeg from a package manager.

Frequently asked questions

What is MoviePy used for?

Video editing in Python: cuts, concatenations, title insertions, video compositing which it calls non-linear editing, video processing, and custom effects. It can read and write the most common audio and video formats including animated images, runs on Windows, macOS and Linux, and requires Python 3.9 or later. It also provides operations for mixing clips together, such as concatenation, side by side playback, and layering with transparency.

Why is the MoviePy.editor module not found in Python?

Because version 2 removed it. The front page carries a note saying the library upgraded to version 2 with major breaking changes, that the previous version's documentation is still reachable but that version is no longer maintained, and that there is a separate guide for updating code. Most tutorials and snippets on the internet still import from the old path, so code written against version 1 fails on import after upgrading.

Is MoviePy slower than using the encoder directly?

Yes, and the front page says so. The reason given is the heavier data import and export operations, which follow from the architecture: media is imported and converted into array objects so every pixel is accessible, and the final clip is encoded back into a file. That representation is what makes custom effects writable in a few lines, and it is also the cost.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. Zulko/moviepy on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zulko-moviepy.svg)](https://hysenlabs.com/projects/zulko-moviepy)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/zulko-moviepy.svg)](https://hysenlabs.com/projects/zulko-moviepy)

Was this useful?