Демо на вымышленных данных← Все программы

Монтажёр трейлеров

Из сборки игры — трейлер 1080p и три вертикальных ролика по одному сценарию. Программа сама проходит игру по сценарию нажатий, записывает геймплей, монтирует, ставит подписи, музыку и звуки и выравнивает громкость. Ниже — настоящий результат на нашей детской игре «Светляндия».

трейлер 39 с · 1920×1080 3 вертикальных по 18–21 с громкость -14 LUFS правка — перезапуск сценария

До и после

Слева — сырая запись из сборки игры (снята программой, без звука). Справа — готовый трейлер из этих записей.

До · запись
Запись 1080×1920, 30 к/с, каждый кадр снят по шагам сценария нажатий
После · трейлер
Трейлер 38.6 с: подписи, переходы, своя музыка и звуки игры, -14.0 LUFS. Здесь веб-копия 720p, оригинал — 1920×1080, H.264, 19 МБ.

3 вертикальных ролика — 3 разных крючка

Первые 1–2 секунды у каждого свои. Подписи стоят выше нижних 20% кадра и левее правой колонки кнопок — их не закроет интерфейс ленты.

Лучший моментвесь пруд вспыхивает · 17.9 с

Вопрос«Зажжёшь все фонарики?» · 20.8 с

До → послетёмный сад и сад в огнях · 18.8 с

Сценарий монтажа — один файл на всё

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

{
  "project": "Светляндия — трейлер и 3 вертикальных ролика",
  "fps": 30,
  "style": {
    "accent": "#ffd54a",
    "text": "#ffffff",
    "outline": "#170f35",
    "box": "#6a3cc8",
    "card_bg": ["#0a1838", "#2b3a78"]
  },
  "logo": "client/logo.png",
  "sources": {
    "garden": "capture/garden_portrait.mp4",
    "pond": "capture/pond_portrait.mp4",
    "aurora": "capture/aurora_portrait.mp4"
  },
  "audio": {
    "music": {
      "auto": {"bpm": 100, "melody": "svet", "seed": 3},
      "volume": -5,
      "fade_in": 0.3,
      "fade_out": 1.8
    },
    "sfx_events": {
      "volume": -9,
      "min_gap": 0.07,
      "map": {
        "tap": ["sfx/tap_1.wav", "sfx/tap_2.wav", "sfx/tap_3.wav", "sfx/tap_4.wav", "sfx/tap_5.wav"],
        "lamp": {"file": "sfx/lamp.wav", "volume": 4},
        "zone": {"file": "sfx/zone.wav", "volume": 4},
        "buy": "sfx/buy.wav",
        "combo": {"file": "sfx/combo.wav", "volume": 3},
        "crack": "sfx/crack.wav",
        "hatch": {"file": "sfx/hatch.wav", "volume": 4},
        "coin": "sfx/coin.wav",
        "ui": {"file": "sfx/ui.wav", "volume": -4}
      }
    },
    "loudness": -14,
    "true_peak": -1.5
  },
  "contact_sheet": {"every": 2.0},
  "outputs": {
    "log": "out/montage_log.json",
    "trailer": {
      "file": "out/trailer_16x9_1080p.mp4",
      "size": [1920, 1080],
      "font_sizes": {"cap": 82, "hook": 96},
      "segments": [
        {
          "src": "garden",
          "in": 0.0,
          "out": 1.9,
          "hold": 0.6,
          "frame": {"fit": "blur", "place": 0.68},
          "fade_in": 0.5,
          "captions": [{"text": "Помоги бабушке\nзажечь *сад*", "pos": "left", "from": 0.3}]
        },
        {
          "src": "garden",
          "in": 2.1,
          "out": 4.9,
          "frame": {"fit": "blur", "place": 0.68},
          "transition": "fade",
          "tdur": 0.35,
          "captions": [{"text": "Тапай —\nлови *светлячков*", "pos": "left"}]
        },
        {
          "src": "garden",
          "in": 5.4,
          "out": 8.4,
          "frame": {"fit": "blur", "place": 0.32},
          "transition": "slideleft",
          "tdur": 0.35,
          "sfx": [{"file": "sfx/whoosh.wav", "at": -0.25, "volume": -12}],
          "captions": [{"text": "Зажигай\n*фонарики*", "pos": "right"}]
        },
        {
          "src": "garden",
          "in": 12.4,
          "out": 15.5,
          "frame": {"fit": "crop", "focus": [0.5, 0.43], "push": 0.12},
          "transition": "fadewhite",
          "tdur": 0.3,
          "captions": [{"text": "Комбо *ЯРКО ×3*!", "pos": "top", "style": "hook", "pop": true, "from": 1.1}]
        },
        {
          "src": "garden",
          "in": 15.5,
          "out": 18.9,
          "frame": {"fit": "blur", "place": 0.68},
          "transition": "fade",
          "tdur": 0.3,
          "captions": [{"text": "Сад засиял —\nидём *дальше*", "pos": "left", "from": 0.4}]
        },
        {
          "src": "pond",
          "in": 0.0,
          "out": 2.4,
          "frame": {"fit": "blur", "place": 0.32},
          "transition": "fadeblack",
          "tdur": 0.5,
          "captions": [{"text": "Новое место —\n*новые фонарики*", "pos": "right", "from": 0.3}]
        },
        {
          "src": "pond",
          "in": 2.4,
          "out": 6.6,
          "frame": {"fit": "crop", "focus": [0.5, 0.4], "push": 0.08},
          "transition": "fade",
          "tdur": 0.35,
          "captions": [{"text": "*Шесть* волшебных мест", "pos": "top", "style": "hook", "pop": true}]
        },
        {
          "src": "aurora",
          "in": 0.0,
          "out": 4.9,
          "frame": {"fit": "blur", "place": 0.68},
          "transition": "fade",
          "tdur": 0.35,
          "captions": [{"text": "От пруда\nдо *северного\nсияния*", "pos": "left", "from": 0.3}]
        },
        {
          "src": "pond",
          "in": 13.4,
          "out": 17.2,
          "frame": {"fit": "blur", "place": 0.32},
          "transition": "slideleft",
          "tdur": 0.35,
          "sfx": [{"file": "sfx/whoosh.wav", "at": -0.25, "volume": -12}],
          "captions": [{"text": "Выводи\n*светящихся\nдрузей*", "pos": "right", "from": 0.3}]
        },
        {
          "src": "pond",
          "in": 18.6,
          "out": 23.0,
          "frame": {"fit": "blur", "place": 0.68},
          "transition": "fade",
          "tdur": 0.35,
          "captions": [{"text": "Наряжай\nгероя", "pos": "left", "from": 0.3}]
        },
        {
          "src": "aurora",
          "in": 5.6,
          "out": 9.6,
          "frame": {"fit": "crop", "focus": [0.5, 0.4], "push": 0.1},
          "transition": "fade",
          "tdur": 0.4,
          "captions": [{"text": "Звёздный причал *ждёт*", "pos": "top", "style": "hook", "pop": true, "from": 1.9}]
        },
        {
          "type": "end",
          "dur": 4.2,
          "bg": {"src": "pond", "at": 6.2, "blur": 36, "dim": 0.42},
          "title": "Светляндия",
          "sub": "Айдл-кликер для детей",
          "cta": "Играй в браузере телефона",
          "transition": "fade",
          "tdur": 0.5,
          "sfx": [
            {"file": "sfx/impact.wav", "at": 0.15, "volume": -6},
            {"file": "sfx/sparkle.wav", "at": 0.9, "volume": -8}
          ],
          "fade_out": 0.6
        }
      ]
    },
    "verticals": [
      {
        "file": "out/vertical_1_luchshiy_moment.mp4",
        "hook": {
          "type": "best_moment",
          "src": "pond",
          "in": 5.95,
          "out": 7.55,
          "text": "Весь пруд\n*сияет!*",
          "pos": "upper",
          "focus": [0.5, 0.32],
          "push": 0.14,
          "sfx": "sfx/impact.wav",
          "sfx_volume": -8
        },
        "segments": [
          {
            "src": "garden",
            "in": 2.1,
            "out": 5.2,
            "transition": "fadewhite",
            "tdur": 0.25,
            "captions": [{"text": "Тапай и лови\n*светлячков*", "pos": "scene"}]
          },
          {
            "src": "garden",
            "in": 5.4,
            "out": 8.4,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [{"text": "Фонарик разгоняет\n*темноту*", "pos": "scene"}]
          },
          {
            "src": "garden",
            "in": 12.4,
            "out": 16.4,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [
              {
                "text": "Комбо *ЯРКО ×3*!",
                "pos": "scene",
                "style": "hook",
                "pop": true,
                "from": 1.1,
                "to": 3.2
              }
            ]
          },
          {
            "src": "aurora",
            "in": 0.6,
            "out": 4.9,
            "transition": "fadeblack",
            "tdur": 0.4,
            "captions": [{"text": "*6 мест* — от сада\nдо северного сияния", "pos": "scene", "from": 0.2, "to": 2.1}]
          },
          {
            "type": "end",
            "dur": 3.6,
            "bg": {"src": "pond", "at": 6.2, "blur": 36, "dim": 0.42},
            "title": "Светляндия",
            "sub": "Айдл-кликер для детей",
            "cta": "Играй в браузере телефона",
            "transition": "fade",
            "tdur": 0.5,
            "sfx": [
              {"file": "sfx/impact.wav", "at": 0.15, "volume": -6},
              {"file": "sfx/sparkle.wav", "at": 0.9, "volume": -8}
            ],
            "fade_out": 0.6
          }
        ]
      },
      {
        "file": "out/vertical_2_vopros.mp4",
        "hook": {
          "type": "question",
          "src": "garden",
          "in": 2.2,
          "out": 3.9,
          "text": "Зажжёшь\n*все фонарики?*",
          "dim": 0.5,
          "sfx": "sfx/pop.wav",
          "sfx_volume": -4
        },
        "segments": [
          {
            "src": "garden",
            "in": 3.0,
            "out": 8.4,
            "transition": "fade",
            "tdur": 0.25,
            "captions": [
              {"text": "Тапай — свет копится", "pos": "scene", "to": 2.6},
              {"text": "Хватило — *зажигай!*", "pos": "scene", "from": 2.8}
            ]
          },
          {
            "src": "garden",
            "in": 10.4,
            "out": 15.5,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [{"text": "Ещё быстрее —\nкомбо *ЯРКО ×3*", "pos": "scene", "pop": true, "from": 2.9}]
          },
          {
            "src": "garden",
            "in": 15.5,
            "out": 19.0,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [{"text": "Получилось! *Сад сияет*", "pos": "panel", "box": true, "pop": true, "from": 1.2}]
          },
          {
            "src": "pond",
            "in": 0.2,
            "out": 3.4,
            "transition": "fadeblack",
            "tdur": 0.4,
            "captions": [{"text": "А впереди —\n*Кувшинковый пруд*", "pos": "scene", "from": 0.3}]
          },
          {
            "type": "end",
            "dur": 3.6,
            "bg": {"src": "pond", "at": 6.2, "blur": 36, "dim": 0.42},
            "title": "Светляндия",
            "sub": "Айдл-кликер для детей",
            "cta": "Играй в браузере телефона",
            "transition": "fade",
            "tdur": 0.5,
            "sfx": [
              {"file": "sfx/impact.wav", "at": 0.15, "volume": -6},
              {"file": "sfx/sparkle.wav", "at": 0.9, "volume": -8}
            ],
            "fade_out": 0.6
          }
        ]
      },
      {
        "file": "out/vertical_3_do_posle.mp4",
        "hook": {
          "type": "before_after",
          "dur": 2.0,
          "labels": ["ДО", "ПОСЛЕ"],
          "before": {"src": "garden", "at": 2.3, "roi": [0, 0.12, 1, 0.5]},
          "after": {"src": "garden", "at": 15.45, "roi": [0, 0.12, 1, 0.5]},
          "sfx": "sfx/whoosh.wav",
          "sfx_at": 0.0,
          "sfx_volume": -8
        },
        "segments": [
          {
            "src": "garden",
            "in": 13.0,
            "out": 15.6,
            "transition": "fadewhite",
            "tdur": 0.25,
            "captions": [{"text": "Зажги весь сад", "pos": "scene"}]
          },
          {
            "src": "pond",
            "in": 13.4,
            "out": 17.3,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [
              {"text": "Открывай коконы —\nтам *новые друзья*", "pos": "scene", "to": 1.6},
              {"text": "Новый друг!", "pos": "scene", "style": "hook", "pop": true, "from": 2.1}
            ]
          },
          {
            "src": "pond",
            "in": 18.6,
            "out": 23.2,
            "transition": "fade",
            "tdur": 0.3,
            "captions": [{"text": "Пижама, узор —\n*наряжай героя*", "pos": "scene"}]
          },
          {
            "src": "aurora",
            "in": 5.6,
            "out": 9.4,
            "transition": "fadeblack",
            "tdur": 0.4,
            "captions": [{"text": "И зажигай\n*новые места*", "pos": "scene"}]
          },
          {
            "type": "end",
            "dur": 3.6,
            "bg": {"src": "pond", "at": 6.2, "blur": 36, "dim": 0.42},
            "title": "Светляндия",
            "sub": "Айдл-кликер для детей",
            "cta": "Играй в браузере телефона",
            "transition": "fade",
            "tdur": 0.5,
            "sfx": [
              {"file": "sfx/impact.wav", "at": 0.15, "volume": -6},
              {"file": "sfx/sparkle.wav", "at": 0.9, "volume": -8}
            ],
            "fade_out": 0.6
          }
        ]
      }
    ]
  }
}
  1. Запись. node capture.mjs capture_garden.json — Chromium без звука проходит игру: тапы, покупки, вкладки. Время в игре идёт ровно по 1/30 с на кадр.
  2. Монтаж. python3 montage.py scenario.json — трейлер, 3 вертикальных, листы кадров и журнал.
  3. Правка. Поменяли подпись или отрезок → тот же запуск. Неизменённые куски берутся из кэша.

Как прошла правка

Правка 1: что поменяли в scenario.json
  outputs.trailer.segments.0.captions.0.text:
    было:  "Ночь опустилась\nна бабушкин сад…"
    стало: "Помоги бабушке\nзажечь *сад*"
  outputs.trailer.segments.10.captions.0.from:
    было:  0.4
    стало: 1.9
  outputs.verticals.1.hook.text:
    было:  "Сможешь зажечь\n*все фонарики?*"
    стало: "Зажжёшь\n*все фонарики?*"

$ python3 montage.py scenario.json
Монтажёр трейлеров 1.0 · сценарий scenario.json (1c8f4eb845825405) · шрифт Inter
▶ trailer: 12 сегментов, 38.60 с, 1920×1080
  сегменты готовы (из кэша 12, заново 0)
  звук: -14.0 LUFS, пик -1.4 dBTP, эффектов 147
  ✓ out/trailer_16x9_1080p.mp4: 38.60 с, 1920×1080, h264/aac, -14.0 LUFS, 19.2 МБ, 146.8 с работы
▶ v1: 6 сегментов, 17.85 с, 1080×1920
  сегменты готовы (из кэша 6, заново 0)
  = out/vertical_1_luchshiy_moment.mp4: без изменений, не пересобираю (0.0 с)
▶ v2: 6 сегментов, 20.75 с, 1080×1920
  сегменты готовы (из кэша 6, заново 0)
  звук: -14.0 LUFS, пик -1.5 dBTP, эффектов 95
  ✓ out/vertical_2_vopros.mp4: 20.77 с, 1080×1920, h264/aac, -14.0 LUFS, 8.5 МБ, 23.1 с работы
▶ v3: 6 сегментов, 18.75 с, 1080×1920
  сегменты готовы (из кэша 6, заново 0)
  = out/vertical_3_do_posle.mp4: без изменений, не пересобираю (0.0 с)
Готово за 176 с. Журнал: out/montage_log.json

Лист кадров

Кадр каждые 2 с (трейлер) и 1,5 с (вертикальные) с отметкой времени — для согласования с заказчиком без просмотра всего ролика.

Лист кадров трейлера

Запуск и проверка

$ node capture.mjs capture_garden.json
$ node capture.mjs capture_pond.json
$ node capture.mjs capture_aurora.json
  ✓ capture/garden_portrait.mp4 — 1080×1920, 24.567 с, 737 кадров, событий игры 128, работа 157.5 с
  ✓ capture/garden_landscape.mp4 — 1920×1080, 24.567 с, 737 кадров, событий игры 128, работа 85.3 с
  ✓ capture/pond_portrait.mp4 — 1080×1920, 26.733 с, 802 кадров, событий игры 104, работа 176.7 с
  ✓ capture/aurora_portrait.mp4 — 1080×1920, 12.6 с, 378 кадров, событий игры 55, работа 81.2 с
$ python3 synth.py sfx sfx/
  ✓ 19 эффектов в sfx/
$ python3 montage.py scenario.json
Монтажёр трейлеров 1.0 · сценарий scenario.json (1c8f4eb845825405) · шрифт Inter
▶ trailer: 12 сегментов, 38.60 с, 1920×1080
  сегменты готовы (из кэша 0, заново 12)
  звук: -14.0 LUFS, пик -1.4 dBTP, эффектов 147
  ✓ out/trailer_16x9_1080p.mp4: 38.60 с, 1920×1080, h264/aac, -14.0 LUFS, 19.2 МБ, 216.9 с работы
▶ v1: 6 сегментов, 17.85 с, 1080×1920
  сегменты готовы (из кэша 0, заново 6)
  звук: -14.0 LUFS, пик -1.5 dBTP, эффектов 82
  ✓ out/vertical_1_luchshiy_moment.mp4: 17.87 с, 1080×1920, h264/aac, -14.0 LUFS, 7.9 МБ, 88.0 с работы
▶ v2: 6 сегментов, 20.75 с, 1080×1920
  сегменты готовы (из кэша 0, заново 6)
  звук: -14.0 LUFS, пик -1.5 dBTP, эффектов 95
  ✓ out/vertical_2_vopros.mp4: 20.77 с, 1080×1920, h264/aac, -14.0 LUFS, 8.5 МБ, 77.7 с работы
▶ v3: 6 сегментов, 18.75 с, 1080×1920
  сегменты готовы (из кэша 0, заново 6)
  звук: -13.9 LUFS, пик -1.5 dBTP, эффектов 68
  ✓ out/vertical_3_do_posle.mp4: 18.77 с, 1080×1920, h264/aac, -13.9 LUFS, 6.9 МБ, 72.8 с работы
Готово за 473 с. Журнал: out/montage_log.json

Весь путь (сборка → запись → звуки → монтаж): 16 мин 20 с
ФайлДлит.КадрЗвук
Трейлер38.6 с1920×1080, H.264 High-14.0 LUFS
Вертикальный 117.9 с1080×1920-14.0 LUFS
Вертикальный 220.8 с1080×1920-14.0 LUFS
Вертикальный 318.8 с1080×1920-13.9 LUFS

Музыка и звуки рассчитаны нашим кодом (мелодия — из самой игры), чужих сэмплов нет. Громкость выровнена двухпроходным loudnorm.

Что нужно от вас

  • сборка игры (HTML5) или свои записи геймплея;
  • логотип;
  • 2–3 главные фишки игры — из них подписи.
Хочу такую программу