Как стать автором
Обновить

ExtendScript Работа с композициями

Время на прочтение 5 мин
Количество просмотров 1.5K

<- Предыдущая статья ExtendScript Работа с файлами

Следующая статья ExtendScript + Expression ->

В прошлых двух статьях я рассказал, как сделать скрипт, запускающийся в After Effects. Наш скрипт создавал окно в интерфейсе программы, в котором мы можем писать и выполнять код ExtenScript. Но это игрушка, полезна она может быть только новичку. Давайте же сделаем нечто более полезное, применимое в реальной жизни.

Одной из самых распространенных задач, которые встречались мне в работе, была динамичная расстановка титров в видеороликах. Видео сегодня, один из самых популярных форматов контента. Одно из ограничений накладываемых браузером, невозможность запустить в ролике звук без действия пользователя. Естественный выход из данной ситуации, заменить звук титрами. Если это 5 - 10 титров, то можно сделать их и руками. Ну, а что, если вам надо выпускать по пять роликов в день и в них не 10, а 50 - 70 титров? Да еще каждый ролик в 2 - 3 форматах? Да, плюс ко всему, еще и на нескольких языках? Если вам кажется такая ситуация необычной, то я сталкиваюсь с ней ежедневно.

Так давайте же упростим эту задачу на сколько это возможно. Мы сделаем более менее универсальный скрипт, который будет интерпретировать текст в титры на сцене After Effects. Хоть я и решал эту задачу уже много раз, буду писать свой скрипт прямо во время написания статьи, чтобы ничего не упустить и сделать свой рассказ максимально подробным. Приступим.

Текст титров мы будем вводить следующим образом

#simple Текст первого титра
Тип данного титра simple

#simple Титры simple могут быть и в одну строку

#double Текст третьего титра
Тип этого титра double

Титры отделены друг от друга двойным переносом. Строки одного титра разделены переносом. Первое слово с символом # является тегом, определяющим тип титра. По этому тегу мы впоследствии будем определять какой макет нам нужен для создания данного титра. Но об этом чуть позже, пока примите такую структуру текста как должное.

За основу нашего скрипта можно взять скрипт из первой статьи. Мы оставим в интерфейсе те же элементы только немного подредактируем. Во первых, я убрал весь код в самовызывающуюся функцию init, просто для удобства, чтобы он не мешал в общей массе остального кода.

{
(function init(){
   
// Тут весь код из скрипта первой статьи

})();
}

Далее мы исправим дефолтный текст в текстовом поле

var editText = win.add(
   'edittext',
   [0, 0, 300, 300],
   'Введите текст титров',
   {multiline: true}
);

Ну а в обработчике нажатия кнопки run мы удалим строку выполнения кода, заменив ее на вызов метода createTitres

btnRun.onClick = function () {
   try {
       createTitres(editText.text.getTitresData());
   } catch (err) {
       alert(err)
   }
};

Соответственно, нам надо создать метод createTitres, принимающий аргументом некий массив данных.

function createTitres(data) {

}

И расширить класс String, добавив ему метод getTitresData, который будет парсить текст, создавая из него массив объектов. Это, в свою очередь, влечет небольшое расширение класса Array, ему мы добавим метод map. Приведу оба расширения вместе.

String.prototype.getTitresData = function() {
   return this
       .replace(/(^\n|^ |^"|\n+$| +$|"+$)/g, "")
       .split("\n\n")
       .map(function(d) {
           var str = d.replace(/(^\n|^ |\n+$| +$)/g, "")
           var tag = str.split(" ")[0];
           var text = str.replace(tag + ' ', '');
           return {
               type: tag.replace('#', ''),
               text: text,
           }
       });
}
Array.prototype.map = function(callback) {
   var arr = [];
   for (var i = 0; i < this.length; i++) {
       arr.push(callback(this[i]));
   }
   return arr;
}

Давайте посмотрим, что у нас тут происходит. В методе getTitresData мы первым делом удаляем из строки часто встречающиеся артефакты в начале и в конце всего текста. Такими артефактами являются переносы, пробелы и, что случается, если копировать текст из гугл таблицы, кавычки.

this.replace(/(^\n|^ |^"|\n+$| +$|"+$)/g, "")

Далее мы разбиваем текст по разделителю двойной перенос

.split("\n\n")

И формируем из получившегося массива новый, отделяя от каждого титра тег

.map(function(d) {
  var str = d.replace(/(^\n|^ |^"|\n+$| +$|"+$)/g, "")
  var tag = str.split(" ")[0];
  var text = str.replace(tag + ' ', '');
  return {
    type: tag.replace('#', ''),
    text: text,
  }
});

Я запустил скрипт. В метод createTitres приходит массив объектов. Пока все верно. Можно двигаться дальше.

Теперь нам следует поработать над макетом. Открываем After Effects. Создаем новый проект. В окне Project  добавим папку, назовем ее 1x1. В этой папке создадим композицию с именем ModelScene_1x1

По префиксу ModelScene мы будем определять, что это макет сцены, по настройкам этого макета  мы будем ориентироваться далее. Суффикс 1x1 означает соотношение сторон, мне так удобнее понимать о какой сцене идет речь. Вы же можете выбрать другой суффикс, главное в дальнейшем следить, чтобы макеты титров имели тот же суффикс в имени. Префикс с суффиксом отделены нижним подчеркиванием.

Настраиваем нашу сцену.

Указываем ширину, высоту, частоту кадров, продолжительность сцены. Значение Duration лучше сделать побольше, чтобы влезли все титры. Все эти параметры мы позже скопируем в нашу рабочую сцену.

Макет сцены готов. Вернемся к скрипту.В методе createTitres первым действием мы получим данные всех сцен имеющихся в нашем макете.

function createTitres(data) {
   var scenesData = getScenesData();
}

function getScenesData() {
   var doc = app.project;
   var data = [];
   for (var i = 1; i <= doc.numItems; i++) {
       var item = doc.item(i);

       if (item instanceof CompItem &&
           /^ModelScene/.test(item.name)) {
           data.push({
               type: item.name.split('_')[1],
               width: item.width,
               height: item.height,
               frameRate: item.frameRate,
               duration: item.duration
           });
       }
   }
   return data;
}

В новом методе getScenesData мы проходимся по всем элементам в нашем проекте. Обратите внимание, что индексация элементов в списке проекта начинается с единицы. Общее число элементов хранится в свойстве проекта numItems.

for (var i = 1; i <= doc.numItems; i++)

Мы смотрим, является ли элемент композицией и есть ли в его имени префикс ModelScene.

if (item instanceof CompItem &&
    /^ModelScene/.test(item.name))

Если условие выполнено, добавляем данные о сцене в массив.

data.push({
    type: item.name.split('_')[1],
    width: item.width,
    height: item.height,
    frameRate: item.frameRate,
    duration: item.duration
});

Нас интересует тип сцены, отделяем суффикс от ее имени. Ширина, высота, частота кадров и продолжительность сцены.

Теперь пройдемся по массиву, поищем наши сцены в проекте, и если таковых нет, создадим их.

function createTitres(data) {
   var scenesData = getScenesData();

   for(var i = 0; i < scenesData.length; i++) {
       var scene = getScene(scenesData[i]);
   }
}

function getScene(data) {
   var sceneName = 'scene-' + data.type;
   return getItem(sceneName, CompItem) ||
       app.project.items.addComp(
           sceneName,
           data.width,
           data.height,
           1,
           data.duration,
           data.frameRate
       );
}

function getItem(name, type) {
   var doc = app.project;

   for (var i = 1; i <= doc.numItems; i++) {
       if (doc.item(i).name === name) {
           if (type) {
               if (doc.item(i) instanceof type) {
                   return doc.item(i);
               }
           } else {
               return doc.item(i);
           }
       }
   }
   return null;
}

Метод getScene сначала ищет композицию в проекте по имени

getItem(sceneName, CompItem)

И если не находит такой, то создает ее, используя параметры макета

app.project.items.addComp(
    sceneName,
    data.width,
    data.height,
    1,
    data.duration,
    data.frameRate
);

Можете проверить работу скрипта. Если вы все сделали правильно, в проекте должна появиться композиция scene-1x1. Сюда мы будем добавлять наши титры. Но об этом в следующий раз.

Полный скрипт к этой статье с комментариями вы можете найти тут

В следующей статье я расскажу, как добавлять титры на сцену.

<- Предыдущая статья ExtendScript Работа с файлами

Следующая статья ExtendScript + Expression ->

Теги:
Хабы:
+3
Комментарии 0
Комментарии Комментировать

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн