After Effectsのスクリプトの可能性を広げる。

AEで処理を自動化する際によく用いられるのはスクリプトであるが、プラグインにもエフェクト以外にAEGPというスクリプト的なことも出来るタイプのものがある。大概のことはスクリプトでも出来るので、あまり出る幕がないのだが、AEGPのAPIを用いないと出来ないこともままあって、そういう場合は渋々C++を書かざるを得ない。しかし、特に単機能のものを書くためだけに、わざわざC++を持ち出すのも面倒であるし、何とかしたい。そこで、スクリプト側からAEGPのAPIを呼び出せるようなラッパー的なライブラリを作ることを考える。

つまり、斜線部を考える。こういうのをあらかじめ作ってさえおけば、あとはC++に立ち入ることなしに、スクリプトだけで完結するので、非常に便利である。ということで作った。


github.com

初代はどういうことが出来るのか色々実験した結果、迷走し複雑化したので、それを糧に二代目をあらためて作り直したものの、今度は別の構想がなかなか煮詰まらず、遅れに遅れた。

at_script/script/types/@script.d.ts at main · atarabi/at_script · GitHub

主なAPIはここを参照すればよいが、 document は まだ整備されてない (@script — documentation 最低限の体裁は整えた。(2023/9/6))。

Releases · atarabi/at_script · GitHub

必要なものは上から @script.zip をダウンロードすれば手に入る*1


最低限必要なものは、 プラグイン*2と Startup 内の !@script_initializer.jsx。後者は必要不可欠というわけではないが、Startup時にAPIを使用したい場合に、初期化処理のために用いる。というのも、AEのスクリプト機能自体がプラグインで実装されているため、こちらのプラグインがそれより先に読み込まれた場合に、スクリプト周りの初期化処理が出来ない。したがって、初期化処理を遅延させているのだが、そのタイミングがStartupのスクリプトの実行タイミングより遅れるため、こちらから促す必要がある。頭に ! をつけているのは、Startupスクリプトの中でも最初に実行してほしいため*3

いにしえより伝わる global にオブジェクトをひとつ作り*4、その下に根を生やしていく方式で構築している。使い方としては、

(() => {
    const comp = app.project.activeItem;
    if (!(comp instanceof CompItem)) {
        return;
    }
    const layers = comp.selectedLayers;
    const isAVLayer = (layer: Layer): layer is AVLayer => {
        return layer instanceof TextLayer || layer instanceof ShapeLayer || layer instanceof AVLayer;
    };
    for (const layer of layers) {
        if (!isAVLayer(layer)) {
            continue;
        }
        Atarabi.layer.setNull(layer, true); // ココ!
    }
})();

といった感じで利用する。上のは選択レイヤーにヌル属性を設定する例である。スクリプトではヌル属性が read-only であるが、AEGPのAPIだと書き込みもできる。


同梱物

APIを使用したスクリプト(使用していないものもある)を同梱してある。

Startup

!@script_initializer

上記通り、StartupにおいてAPIを利用したい場合は、これをStartupのフォルダに入れる必要がある。

@command_set_null

選択レイヤーにヌル属性を追加するSet NullコマンドをLayerメニューに追加する。

(() => {
    Atarabi.register.insertCommand('Layer', 'AtTop', 'Set Null', () => {
        // 内容は上の例とほぼ同じ
    }, 'LayerSelected');
})();

このように簡単にコマンドを差し込むことができる。'LayerSelected'は、レイヤーを選択しているときだけコマンドを有効化させることを意味する。当初はここも関数で指定できるようにしてあったが、Mac環境だとエラーが出るため、ご破産となった。

@fold_layers

一時期話題になっていたGM_FoldLayersのクローン。LayerメニューにAdd Separatorコマンドが追加され、実行すると、▾ Separatorという名前のシェイプレイヤーが出来る。これが区切り線がわりとなっており、ダブルクリックをすると、シャイレイヤーの機能によって開閉がなされる。が区切り線用のレイヤーか否かのマーカーになっているので、Separator部分の名前は変更可能。

Atarabi.register.hookCommand(3974/* レイヤーをダブルクリックする際に発行されるコマンド */, ctx => {
    const markedLayer = getMarkedLayer();
    if (!markedLayer) {
        ctx.fallback = true;
        return;
    }
    // メインの処理
});

既存のコマンドを上記のように簡単にhook出来るようにしてある。マーカー以外のレイヤーではデフォルトの挙動をして欲しい。そこで、渡されたctxfallbacktrueにすることで、デフォルトのコマンドの挙動にフォールバックするようにしてある。ここらへんはTJ氏がWQというプラグインを作る際にも、色々試行錯誤されていた。

@hook_adjustment, @hook_null, @hook_solid

調整レイヤーにしろヌルレイヤーにしろ平面レイヤーにしろ、既存の平面レイヤーを出来るだけ使いまわして欲しい。そこで、そこらへんのコマンドをフックすることで使い回すようにしてある。平面レイヤーは白色固定なので、カラフルにしたい場合は各自、改造してみるのもいいかもしれない。

昔、ここらへんの平面レイヤーを用いるコマンドをシェイプレイヤーに置き換えるAEGPプラグインを作ったことがあったが、このときは基本の処理はスクリプトで書き、プラグイン内でそれを実行し、ヌル属性を付加することろだけは、別途C++で書いていた。今は全てスクリプトで書けるので、いい時代になったものである。

@hook_fit_to_comp

Fit to Compコマンドは、縦横比を無視してコンポサイズに拡大縮小する。個人的に、縦横比を維持して欲しいなということが多いので、フックして縦横比を維持するようにしている。現状、background-size: contain;的な処理をしているが、background-size: cover;的な処理も欲しいなと思うことが時たまあるので、いずれ選択式にしたい。

@script_UI

私がScriptUIのGUIを楽して作る用のライブラリ。下記のScriptUI系のスクリプトは全てこれを用いて作っている。

ScriptUI

上記通り、@script_UIが必須。

選択しているエフェクトのドロップダウンリスト系のパラメータのテキストを羅列する。ダブルクリックすることで値を変更でき、さらにReal Timeをチェックすれば、クリックするだけで値が変わる。

@paramdefs_viewer

選択しているエフェクトのパラメータの詳細を得る。APIを通じて、Custom Propertyの値もBase64エンコードされた形で取得できる。これを用いて、Custom Propertyの書き込みも出来るが、内部的にポインタを用いてるものに対しては、無理に書き込もうとすると落ちる*5

エフェクトをFuzzy Search出来る。

@effect_preset

気軽にエフェクトのプリセットを作れる。ffxよりもうちょっと気軽にプリセット的なのを扱いたい用。上で書いたように、CurvesのようなCustom Propertyの値も扱えるので割りと使える。

@still_maker

コンポないし選択レイヤーの現在フレームを静止画として保存できる。

@movie_maker

コンポないし選択レイヤーをGIF, APNGとして保存出来る。最適化されているわけではないので、適当に扱ってもよいとき用。

@rpp_loader

REAPERのプロジェクトをAEに読み込む - Atarabi-Tumblr

これの移植、修正版。


余談。

{
    "compilerOptions": {
        "jsx": "preserve",
        "rootDir": "./src",
        "outDir": "./dst"
    },
    "include": [
        "./types/aftereffects.d.ts/ae.d.ts",
        "./src/**/*.tsx"
    ]
}

TypeScriptでスクリプトを書く際は、設定を上記のようにするのが楽。味噌は、スクリプトのファイルの拡張子をtsxにすることと、"jsx": "preserve"にすること。こうすることで、トランスパイルした際に自動的に拡張子がjsxになるので、リネームせずに済む。

*1:スクリプト関連は自分でトランスパイルしてもよい。

*2:Windowsの場合は@script.aex、Macの場合は@script.plugin。

*3:スクリプトはおそらくASCII順で実行される。そうでなかった場合は、ただのおまじないと化す。

*4:このライブラリに関してはAtarabi

*5:Mesh Warp、 Reshapeなど。