moviepy (Composição de Vídeo em Python)
Melhor para vídeos curtos e roteirizados e para colocar texto exato sobre gravações. Para vídeos longos, com foco em design e reutilizáveis, prefira Remotion.
| Use moviepy quando | Use Remotion quando |
|---|---|
| O projeto é um único script Python | Você precisa de componentes React e um sistema de design |
| Sobrepor texto em clipes existentes ou gerados por IA | Gráficos animados e diagramas complexos |
| Edições em lote de muitos arquivos | A visualização com recarga automática é importante |
| Sem disponibilidade de ferramentas Node | Vídeos longos e baseados em templates |
Configuração
pip install "moviepy>=2" pillow
# ou: uv add moviepy pillow
Esta skill tem como alvo moviepy 2.x. Sua API difere da 1.x: as importações vêm diretamente de moviepy, e os métodos são with_*/subclipped/resized, não set_*/subclip/resize.
Conceitos Básicos
from moviepy import (VideoFileClip, AudioFileClip, ImageClip, TextClip,
CompositeVideoClip, CompositeAudioClip, concatenate_videoclips, vfx, afx)
clip = VideoFileClip("in.mp4").subclipped(5, 20) # trecho de 15 s
clip = clip.resized(height=1080) # manter proporção
clip = clip.with_effects([vfx.FadeIn(0.5), vfx.FadeOut(0.5)])
joined = concatenate_videoclips([clip, VideoFileClip("b.mp4")], method="compose")
joined.write_videofile("out/resultado.mp4", fps=30, codec="libx264", audio_codec="aac")
Sempre chame .close() nos clipes em scripts longos, ou use-os em um bloco with, para liberar os identificadores de arquivo.
Sobreposição de texto (texto determinístico sobre gravações)
titulo = (TextClip(font="C:/Windows/Fonts/arial.ttf", # caminho real do arquivo de fonte
text="Dra. Jane Doe\nCientista Chefe",
font_size=56, color="white",
stroke_color="black", stroke_width=2,
method="caption", size=(900, None))
.with_start(1.0).with_duration(4.0)
.with_position(("left", 860))
.with_effects([vfx.CrossFadeIn(0.3), vfx.CrossFadeOut(0.3)]))
final = CompositeVideoClip([clip, titulo])
Notas:
TextClipprecisa de um caminho de arquivo de fonte (TTF/OTF). Inclua a fonte da marca no projeto.- Os tempos dos clipes estão em segundos, relativos ao início do composto.
- Para texto com muito estilo (pílulas arredondadas, gradientes), renderize um PNG com Pillow e use
ImageClip. - Mantenha o texto dentro da área segura: pelo menos 5% de cada borda.
Áudio
voz = AudioFileClip("narracao.mp3")
musica = AudioFileClip("musica.mp3").with_effects([afx.MultiplyVolume(0.12), afx.AudioFadeOut(2)])
musica = musica.with_duration(voz.duration + 1)
final = clip.with_audio(CompositeAudioClip([musica, voz.with_start(0.5)]))
Deixe a narração definir a linha do tempo: defina a duração do clipe como voz.duration + margem, não o contrário.
Padrão ancorado em áudio
cenas = [("cena1.mp3", "intro.png"), ("cena2.mp3", "grafico.png")]
partes = []
for caminho_audio, caminho_imagem in cenas:
a = AudioFileClip(caminho_audio)
v = ImageClip(caminho_imagem).with_duration(a.duration + 0.6).with_audio(a)
partes.append(v)
concatenate_videoclips(partes).write_videofile("out/explicativo.mp4", fps=30)
Efeito Ken Burns em imagens estáticas
img = ImageClip("foto.jpg").with_duration(6)
img = img.resized(lambda t: 1.0 + 0.04 * t).with_position("center")
CompositeVideoClip([img], size=(1920, 1080)).write_videofile("out/kb.mp4", fps=30)
Desempenho
- Visualize com
fps=12epreset="ultrafast"; renderize o final a 30 fps. write_videofile(..., threads=4).- Evite lambdas Python por quadro em clipes longos. Pré-processe com FFmpeg quando possível.
Problemas Comuns
| Problema | Causa e solução |
|---|---|
ImportError: cannot import name 'editor' | moviepy 2 removeu moviepy.editor; importe de moviepy |
AttributeError: set_duration | Use with_duration; o mesmo para with_start, with_position |
| O clipe de texto trava | Passe um caminho de arquivo de fonte real |
| Áudio fora de sincronia após junção | Garanta que todos os clipes compartilhem o mesmo fps; aplique áudio após a concatenação |
| Renderização lenta | Reduza a resolução para rascunhos; evite lambdas grandes em resized |