moviepy (Python 비디오 합성)은(는) 무엇을 하나요?
짧고 스크립트 기반 비디오, 푸티지 위에 정확한 텍스트를 입히는 작업에 최적입니다. 길고 디자인이 복잡하며 재사용 가능한 비디오는 Remotion을 사용하세요.
| moviepy 사용 시 | Remotion 사용 시 |
|---|---|
| 프로젝트가 단일 Python 스크립트일 때 | React 컴포넌트와 디자인 시스템이 필요할 때 |
| 기존 또는 AI 생성 클립 위에 텍스트 오버레이 | 복잡한 애니메이션 그래픽 및 차트 |
| 여러 파일에 대한 배치 편집 | 핫 리로드 미리보기가 중요할 때 |
| Node 도구 체인을 사용할 수 없을 때 | 장기간 사용되는 템플릿 비디오 |
설정
pip install "moviepy>=2" pillow
# 또는: uv add moviepy pillow
이 스킬은 moviepy 2.x를 대상으로 합니다. 1.x와 API가 다릅니다: 임포트는 moviepy에서 직접 하고, 메서드는 set_*/subclip/resize 대신 with_*/subclipped/resized를 사용합니다.
기본
from moviepy import (VideoFileClip, AudioFileClip, ImageClip, TextClip,
CompositeVideoClip, CompositeAudioClip, concatenate_videoclips, vfx, afx)
clip = VideoFileClip("in.mp4").subclipped(5, 20) # 15초 구간
clip = clip.resized(height=1080) # 비율 유지
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/result.mp4", fps=30, codec="libx264", audio_codec="aac")
긴 스크립트에서는 항상 .close()를 호출하거나 with 블록을 사용하여 파일 핸들을 해제하세요.
텍스트 오버레이 (푸티지 위의 결정적 텍스트)
title = (TextClip(font="C:/Windows/Fonts/arial.ttf", # 실제 폰트 파일 경로
text="닥터 제인 도우\n수석 과학자",
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, title])
참고:
TextClip은 폰트 파일 경로(TTF/OTF)가 필요합니다. 프로젝트에 브랜드 폰트를 포함시키세요.- 클립 시간은 초 단위이며, 합성 클립의 시작을 기준으로 합니다.
- 스타일이 많이 들어간 텍스트(둥근 버튼, 그라데이션)는 Pillow로 PNG를 렌더링한 후
ImageClip을 사용하세요. - 텍스트는 안전 영역(각 가장자리에서 최소 5%) 안에 유지하세요.
오디오
voice = AudioFileClip("voiceover.mp3")
music = AudioFileClip("music.mp3").with_effects([afx.MultiplyVolume(0.12), afx.AudioFadeOut(2)])
music = music.with_duration(voice.duration + 1)
final = clip.with_audio(CompositeAudioClip([music, voice.with_start(0.5)]))
내레이션이 타임라인을 결정하도록 하세요: 클립 길이를 voice.duration + 패딩으로 설정하고, 반대 방향은 피하세요.
오디오 기반 패턴
scenes = [("scene1.mp3", "intro.png"), ("scene2.mp3", "chart.png")]
parts = []
for audio_path, image_path in scenes:
a = AudioFileClip(audio_path)
v = ImageClip(image_path).with_duration(a.duration + 0.6).with_audio(a)
parts.append(v)
concatenate_videoclips(parts).write_videofile("out/explainer.mp4", fps=30)
정지 이미지에 Ken Burns 효과
img = ImageClip("photo.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)
성능
- 미리보기는
fps=12와preset="ultrafast"로 작업하고, 최종 렌더링은 30fps로 하세요. write_videofile(..., threads=4)를 사용하세요.- 긴 클립에 대해 프레임당 Python 람다(lambda) 사용을 피하세요. 가능하면 FFmpeg로 전처리하세요.
주의 사항
| 문제 | 원인 및 해결 방법 |
|---|---|
ImportError: cannot import name 'editor' | moviepy 2에서 moviepy.editor가 제거됨; moviepy에서 직접 임포트 |
AttributeError: set_duration | with_duration 사용; with_start, with_position도 동일 |
| 텍스트 클립 충돌 | 실제 폰트 파일 경로를 전달하세요 |
| 연결 후 오디오 싱크 불일치 | 모든 클립의 fps가 동일한지 확인하고, 연결 후 오디오를 적용하세요 |
| 느린 렌더링 | 초안은 해상도를 낮추고, 큰 resized 람다를 피하세요 |