# Выгрузка в Premiere и фотомозаика

## Premiere: почему FCP7 XML, а не fcpxml

```bash
python scripts/export_premiere_xml.py timeline_full.json edit.xml \
  --cold-open cold_open.json --broll-plan broll_plan.json \
  --caption-map caption_map.json --retention-plan retention_plan.json
```

Формат выбран не любой. **Premiere не импортирует `.fcpxml`** — тот, что отдаёт
современный Final Cut. Он импортирует **Final Cut Pro 7 XML** (`xmeml version=5`):
формат старый, но живой, и его же понимают DaVinci Resolve и Vegas. Целиться в
fcpxml значило бы заставить человека прогонять файл через сторонний конвертер.

Что попадает в проект:

| Дорожка | Что |
|---|---|
| V1 | холодный старт + куски исходника с точками входа и выхода |
| V2 | перебивки, каждая на своём кадре |
| A1 | звук исходника, **связанный** с картинкой через `link` |
| маркеры | блоки субтитров, события графики, склейки, скоростные рампы |

### Три вещи, на которых это ломается молча

**Холодный старт живёт отдельно от списка клипов.** `clips` в таймлайне
описывает уже сдвинутый корпус, а хук вырезан из исходника по своим точкам и
приклеен спереди. Без него V1 начинается не с нуля, а с кадра сдвига — первые
секунды в Premiere пустые, и выглядит это как сломанный экспорт.

**Связка картинки со звуком.** Без узла `link` они разъедутся при первом же
перетаскивании клипа, и вся точность пословной нарезки пропадёт.

**`pathurl` на Windows.** Обратные слэши и кириллица в пути ломают импорт
молча: клип встаёт в таймлайн со статусом «media offline», и причину ищут в
Premiere, а не в экспорте. Путь собирается через `pathname2url`.

### Что не переносится

Субтитры прожжены в картинку — рядом кладётся `.srt` для редактируемого текста.
Грейд, эффекты на стыках и слой графики это ffmpeg-фильтры, а не свойства клипа;
в XML их нет. Скоростные рампы помечены маркерами с точным множителем: FCP7 XML
переносит `timemap` не всеми приложениями одинаково, и честная пометка лучше
молча потерянного ретайминга.

---

## Фотомозаика

```bash
python scripts/plan_photo_mosaic.py --video cut.mp4 \
  --caption-map caption_map.json --timeline timeline_full.json \
  --out mosaic --count 24 --style cascade

python scripts/render_photo_mosaic.py mosaic/mosaic_plan.json --out mosaic.mp4
```

Приём, который читается как «много фоток»: десятки снимков влетают сеткой,
накладываются, схлопываются в один кадр. Он делает две вещи сразу — показывает
объём материала и даёт всплеск движения там, где по речи нужен акцент.

### Фотографии не обязаны быть фотографиями

Если снимков нет, их даёт сам ролик. И это **лучше** стоковых картинок: кадры,
вырезанные из исходника, зритель уже видел, поэтому мозаика читается как «вот
всё, о чём мы говорили», а не как случайный набор.

Моменты для нарезки берутся осмысленные — начала блоков субтитров и точки
склейки, — а не равномерная сетка по времени. Прореживание до нужного числа идёт
равномерным шагом **по списку**: взять первые N подряд значило бы собрать
мозаику из одного начала ролика.

### Сетка считается, а не берётся из шаблона

Пропорция подгоняется под пропорцию кадра: на вертикальном канвасе 4 столбца на
6 рядов выглядят сеткой, а 6 на 4 — обрезанной лентой. Размер ячейки кратен
двум и делит канвас нацело, иначе по краям остаются полосы в один-два пикселя,
которые на градиенте видно.

| Стиль | Порядок появления |
|---|---|
| `assemble` | по индексу, сетка собирается слева направо |
| `cascade` | волной по диагонали от верхнего левого угла |
| `shuffle` | детерминированная перетасовка |

Перетасовка именно детерминированная: `Math.random()` дал бы разный порядок на
каждом прогоне, а повторный рендер обязан совпасть с уже собранным монтажом.

### Почему один проход ffmpeg

Каждая ячейка — отдельный вход. Двадцать четыре входа звучит много, но это один
декод на картинку и одно кодирование. Покадровая сборка в Python была бы на два
порядка медленнее и держала бы все кадры в памяти.

В конце — общий наезд на собранную сетку. Он нужен: статичная мозаика через
полсекунды перестаёт читаться как движение и становится обоями.
