Movie Narrator: one command, sixteen pipeline steps, and a pinned pillow the audit job ignores
🎬 Generate narrated movie recap videos from a single prompt.
At a glance
- What is it?
- Movie Narrator renders narrated movie recaps through a fixed 16 step pipeline on top of a local Ollama model. The packaging is unusually candid about its own limits, including a pillow pin that the project's own CI suppresses advisories for.
- Who is it for?
- Worth it if you already have a machine with Ollama and want recap videos end to end, since the 16 step pipeline and the artifact layout are finished work rather than a demo. Read the pillow comment and the Python 3.14 marker before trusting a batch run, and use the worker endpoints rather than two processes on one storage directory.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 17 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 October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The install is small, and the model server is the real prerequisite
pip install movie-narratorThe package pulls typer, httpx, openai, pydantic, edge-tts, moviepy, pydub, pyyaml and tqdm, plus a conditional `audioop-lts` on Python 3.13 and newer. What it does not pull is a model. The default prerequisite is a local Ollama server, started with `ollama serve`, and the shipped values in `.env.example` point at `http://localhost:11434/v1` with model `qwen2.5:7b` and API key `ollama`. A first run on a clean machine therefore fails at the LLM stage, not at the video stage.
Five provider guides are offered for people without a GPU: Ollama, Zhipu GLM with unlimited free `glm-4-flash`, Alibaba Bailian at 1M tokens per model, Xiaomi MiMo, and SiliconFlow. All five are reached through the same OpenAI-compatible client, and the only provider names the env file registers are `openai` and `remote`. Switching provider means editing the base URL, not installing an adapter.
On Python 3.14 the ml extra installs nothing and the align step degrades
The `[ml]` extra is four dependencies, each carrying a `python_version < "3.14"` marker: whisperx, faster-whisper, sentence-transformers and funasr. On Python 3.14 and newer the install succeeds and no ML package is installed, and the README says the align and match steps automatically soft-degrade. The pin is described as a packaging gap rather than a design choice: PyTorch itself is 3.14 ready at 2.10 and newer, but WhisperX and FunASR have not shipped 3.14 wheels.
Two other version facts sit next to it. The project metadata declares `requires-python = ">=3.10"` with no upper bound while the classifier list stops at 3.13, so 3.14 is inside the declared range and outside the tested one. And the Dockerfile pins `ARG PYTHON_VERSION=3.12` on purpose, calling 3.13 coverage patchy across the PyTorch and CTranslate2 wheel matrix and 3.14 excluded by the pins outright. A container build and a local `pip install` can therefore disagree about what is present while both report success.
The pillow pin is documented, and the CI security job ignores the advisories
`pyproject.toml` carries `pillow>=11.3.0,<12.0` with an inline note. moviepy 2.x requires pillow below 12.0, the latest moviepy at 2.2.1 has no release allowing pillow 12.x yet, and the known pillow 11.x advisories are ignored in the CI security job through a `pip-audit --ignore-vuln` call. The note says to re-evaluate once moviepy supports 12.x, where the patched line is 12.1.1 and newer.
So the repository states in its own dependency file that it ships a library with known open advisories and that its audit is configured not to report them. That is a defensible trade for a video tool, and it is also the first thing to check when auditing this project, because the suppression is a committed setting rather than a local habit. The same file pins `setuptools>=83.0.0` in the build backend to satisfy PYSEC-2026-3447 and pins the same version in the dev extra so the environment pip-audit inspects is patched.
Scaling the cluster adds inference endpoints, not queue consumers
The compose header states the storage model and its one hard constraint. There is no database and no external broker, so artifacts live in a named volume `mn-output` shared by the api and every worker replica, and the authoritative task index, `tasks.json`, lives in `mn-tasks` on the api side. `LocalTaskQueue` is an in-process ThreadPoolExecutor, and `TaskStorage` keeps the whole index cached in memory and rewrites the file on every save. Two processes pointed at the same `--storage-dir` would clobber each other.
The consequence is written into the file: `--scale worker=4` adds capacity by starting more independent inference endpoints, not by adding consumers to one queue. Each worker replica gets a private anonymous volume for `/app/.mn_tasks` and is reached as `--remote http://worker:8765`. Two profiles sit behind opt-in flags, `s3` for MinIO, labelled experimental, and `gpu` for a CUDA worker that needs nvidia-ctk. Compose v2.24 or newer is required for the long `env_file` syntax, and there is no top-level `version:` key.
The build files are stamped 0.8.4 while the package is at 1.7.0
The Dockerfile header calls itself the multi-stage container image at v0.8.4 and its usage lines build `movie-narrator:0.8.4`, `movie-narrator:0.8.4-full` and `movie-narrator:0.8.4-gpu`. The compose header calls itself the local cluster at v0.8.4. The project metadata says 1.7.0, and the newest release tag is v1.7.0 from 2026-09-16. Anyone copying a build command from either header ends up with an image tag two minor series behind the code it builds, and the compose image name is derived from a version the project no longer uses.
The release titles show where the effort went after that stamp. v1.5.2 on 2026-08-30 covers deployment and media caching, with a Helm chart, a cache pool and a pilot decision named in its title. v1.6.0 on 2026-09-08 covers megafile splitting, CLI option aliasing, a settings ops view and metadata key gates. The last push on 2026-09-16 matches the v1.7.0 tag date, and the repository is not archived.
Settings split in two, and the operations knobs reuse the same names
Configuration is layered four deep: environment variables prefixed `MN_` win, then a project-level `.env` in the working directory, then `~/.movie-narrator/.env`, then built-in defaults pointing at local Ollama. The user-level file is created on first run and deliberately lives outside the package so that `pip install`, upgrade and uninstall never touch it. The prefix exists to avoid collisions with other tools on the same machine.
Infrastructure lives in `.env`, credentials and endpoints for LLM, TTS, VLM and TMDB. Pipeline behavior lives elsewhere, in a job yaml whose params keys cover scene detection, match, render, translate, background music, WhisperX and FunASR align, async handling and video sizes. A third group, operations and deployment, is meant to be injected from docker-compose or Helm under the same `MN_*` names: API-key auth, webhooks, rate limit, scheduler, circuit breaker, graceful shutdown and distributed rendering. The file is explicit that ops-only fields must not be added to `Settings` alone but folded into the read-only `get_server_ops()` view, which `cloud/daemon`, `reliability/circuit_breaker` and the `mn serve` auth fallback consume.
Sixteen ordered steps, and the QA gate runs before the render
The pipeline is a fixed sequence: resolve_video, prepare_assets, research_plot, generate_script, export_script_md, generate_voice, align_audio, detect_scenes, match_clips, mix_bgm, translate_subtitles, generate_subtitle, run_qa_gate, render_video, validate_deliverable, export_clips. Reading that order against the feature list produces a detail worth pausing on. Final-video QA is described as black-frame and slideshow-risk detection, but `run_qa_gate` sits before `render_video`. Whatever the gate inspects, it is not the finished mp4, and the step that runs after the render is `validate_deliverable`, a separate check with a different name.
The steps marked soft are research, align and scene detection, and a fourth name beginning with s that the page does not finish. Soft steps are the ones that degrade instead of failing, which is the same mechanism the Python 3.14 marker depends on: a missing capability turns into weaker output rather than an error. If you need to know whether a given run actually aligned its audio, the output list does not answer it, so `metadata.json` with its segment timings and pipeline status is the file to read.
The output directory is wider than the two files most runs check
Beyond `final.mp4`, one run writes `narration.mp3`, `mixed.mp3` when background music is enabled, `subtitle.srt` for the original narration, `subtitle.<lang>.srt` and `subtitle.bilingual.srt` when `--subtitle-lang` is set, `script.md`, `research.json` with `--research`, `metadata.json`, `matches.json` when a source video was supplied, and a `clips/` directory holding one standalone clip per segment unless `--no-clips` is passed. The clips are the stated point of secondary editing.
The CLI surface is wider than the two example commands, with `mn race --candidates 3` to run several variations and auto-pick one, `mn imitate --reference viral_ref.mp4` to extract style from a reference video first, `mn create --config examples/job.example.yaml` to drive a whole job from YAML, `mn submit -m <movie>` and `mn tasks` for the async path, and `mn serve` to start the remote inference server. Race and imitate both change what gets generated before any of the sixteen steps run, which is why they exist as separate commands rather than flags on `create`.
Editorial conclusion
Worth it if you already have a machine with Ollama and want recap videos end to end, since the 16 step pipeline and the artifact layout are finished work rather than a demo. Read the pillow comment and the Python 3.14 marker before trusting a batch run, and use the worker endpoints rather than two processes on one storage directory. Anyone treating it as production infrastructure should note that the queue lives in process memory and there is no database behind it.
Frequently asked questions
What does Movie Narrator need before mn create works?
A reachable LLM endpoint. The default is a local Ollama server started with `ollama serve`, and the shipped values point at http://localhost:11434/v1 with model qwen2.5:7b and API key `ollama`. The install command itself installs no model.
Does Movie Narrator work on Python 3.14?
The install succeeds but the ml extra installs nothing, because whisperx, faster-whisper, sentence-transformers and funasr are all marked `python_version < "3.14"`. The align and match steps then soft-degrade. The Dockerfile pins Python 3.12 as the target for that reason.
Why is Movie Narrator pinned to pillow 11.x?
moviepy 2.x requires pillow below 12.0 and the latest moviepy at 2.2.1 has no release allowing pillow 12.x. Known pillow 11.x advisories are ignored in the CI security job through a pip-audit --ignore-vuln call, with a note to re-evaluate once moviepy supports 12.x.
Can Movie Narrator workers share one task queue?
No. LocalTaskQueue is an in-process ThreadPoolExecutor and TaskStorage caches the whole index in memory, rewriting the file on every save, so two processes on the same storage directory would clobber each other. Worker replicas get a private volume for /app/.mn_tasks and act as independent inference endpoints reached over --remote.
Where should Movie Narrator settings be edited?
Infrastructure values such as LLM, TTS, VLM and TMDB endpoints go in .env, layered as environment variables, then cwd/.env, then ~/.movie-narrator/.env, then built-in defaults. Pipeline behavior is configured separately through the job yaml example rather than the env file.
Does Movie Narrator need a database to run tasks?
No. The project states there is no database and no external broker. Rendered artifacts live in a shared named volume, and the authoritative task index, tasks.json, lives with the api only. An experimental s3 profile adds MinIO as an opt-in.
Official sources
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.
[](https://hysenlabs.com/projects/zcbacxc-movie-narrator)