# MoviePy 영상 품질 개선 — easing + 전환 효과 + 인코딩 최적화

## 레벨: Lv.1

## 목표
ThreadAuto MoviePy 영상의 "끊기는 느낌"을 해결한다.
4가지 원인을 모두 수정하고, 수정 전후 비교 영상을 생성한다.

## 프로젝트 경로
- `/home/jay/projects/ThreadAuto/`

## ★ 수정 사항 (정확한 위치 지정)

### 1. 타이핑 애니메이션에 easing 적용
- 파일: `video/animations.py`
- 함수: `render_typing_frames()` (line 138~210)
- 현재: line 193 `chars_to_show = round(total_chars * frame_idx / (typing_frames - 1))` → 선형 보간
- 수정: `_ease_out_cubic(t)` 적용
```python
# 수정 전
chars_to_show = round(total_chars * frame_idx / (typing_frames - 1))

# 수정 후
t = frame_idx / (typing_frames - 1)
eased = _ease_out_cubic(t)
chars_to_show = round(total_chars * eased)
```

### 2. 페이드인 애니메이션에 easing 적용
- 파일: `video/animations.py`
- 함수: `render_fade_in_text_frames()` (line 236~287)
- 현재: line 278 `alpha = int(255 * (frame_idx / max(fade_frames - 1, 1)))` → 선형 보간
- 수정: easing 적용 + 슬라이드업 모션 추가
```python
# 수정 전
alpha = int(255 * (frame_idx / max(fade_frames - 1, 1)))

# 수정 후
t = frame_idx / max(fade_frames - 1, 1)
eased = _ease_out_cubic(t)
alpha = int(255 * eased)
```
- 추가로 Y축 슬라이드업 모션 (30px → 0px, easing 적용):
```python
# 페이드인 중 Y 오프셋 (위로 슬라이드)
slide_offset = int(30 * (1 - eased))  # 30px에서 시작하여 0으로
adjusted_position = (position[0], position[1] + slide_offset)
```

### 3. 장면 간 크로스페이드 전환 추가
- 파일: `video/video_builder.py`
- 함수: `build_video()` (line 55~83)
- 현재: `transition_type`, `transition_duration` 파라미터 있으나 미구현
- 수정: 장면 간 크로스페이드 전환 구현
```python
# 장면 프레임 리스트를 병합할 때, 마지막 N프레임과 다음 장면 처음 N프레임을 알파블렌딩
transition_frames = round(transition_duration * fps)
# 이전 장면 마지막 프레임들과 현재 장면 처음 프레임들을 블렌딩
```

### 4. 인코딩 preset 변경
- 파일: `video/video_builder.py`
- line 28: `preset="ultrafast"` → `preset="medium"`
- line 49: `preset="ultrafast"` → `preset="medium"`
- "medium"은 인코딩 시간 약간 증가하나 화질/압축 품질 현저히 향상

### 5. (선택) ease_in_out_cubic 추가
- 현재 `_ease_out_cubic`만 있음
- `_ease_in_out_cubic` 추가 (시작과 끝 모두 부드럽게):
```python
def _ease_in_out_cubic(t: float) -> float:
    """easeInOutCubic: 시작과 끝 모두 부드러움."""
    if t < 0.5:
        return 4 * t * t * t
    else:
        return 1 - (-2 * t + 2) ** 3 / 2
```
- 페이드인은 `_ease_out_cubic`, 전환은 `_ease_in_out_cubic` 사용

## 테스트

### 비교 영상 생성
수정 후 동일 콘텐츠로 **수정 전/후 영상 2개** 생성:
- Before: 현재 코드로 영상 1개 (수정 전에 먼저 생성)
- After: 수정 후 영상 1개

### 콘텐츠 (하드코딩 OK)
```python
scenes = [
    {
        "duration": 5.0,
        "elements": [
            {"type": "title", "text": "실손보험 알고 계신가요?", "animation": "typing"},
            {"type": "body", "text": "실손보험은 5년마다 재가입해야 하는 상품입니다", "animation": "fade_in"},
        ]
    },
    {
        "duration": 5.0,
        "elements": [
            {"type": "title", "text": "가입 시 주의사항", "animation": "typing"},
            {"type": "checklist", "items": ["면책기간 확인", "자기부담금 비교", "보장 범위 체크"]}
        ]
    },
    {
        "duration": 4.0,
        "elements": [
            {"type": "title", "text": "보험료 비교", "animation": "typing"},
            {"type": "counter", "start_value": 0, "end_value": 35000, "suffix": "원", "prefix": "월 "},
        ]
    }
]
```

### 영상 출력 경로
- Before: `/home/jay/projects/ThreadAuto/output/videos/benchmark_before.mp4`
- After: `/home/jay/projects/ThreadAuto/output/videos/benchmark_after.mp4`

## 산출물
1. 수정된 파일: `video/animations.py`, `video/video_builder.py`
2. 비교 영상 2개 (before/after)
3. 보고서: 수정 전후 차이 설명

## 주의사항
- 기존 테스트가 있다면 통과해야 함
- `_ease_out_cubic` 함수는 이미 존재하므로 새로 만들지 말 것
- fps=30 유지 (변경 금지)
- 기존 API 시그니처 변경 금지 (새 파라미터 추가는 OK, 기본값 유지)