Scripting apply a preset - How to tell what type of layer from a ffx file? Need some Scripting help
Copy link to clipboard
Copied
Hi Guys,
I've written a script for myself to output a folder of presets into a comp, staggered at the current CTI point. SO for example I get a comp with 3s of each preset. Then I can render it out for a preview type file. Amongst other uses. It works great apart form one problem.
If i have a mix of presets that use either a text layer or shape layer or solid, i cannot work out a way for the script to know which is which whilst creating the layer for the preset. Currently I am grouping the presets that I know are shape layers, solids/adjjustments, etc and text layers. But I donlt think this is ideal.
Is there a way we can parse the ffx to find what layer type it was built to use. Or can you apply the preset in some way that that in applying the layer type is determined, just as if you did so in the GUI.
Does anyone know how to solve this, I'm interested to hear a solution if possible.
Copy link to clipboard
Copied
If there's not a more direct way (and I can't think of one, off hand) I think you can take advantage of the fact that if you add a preset but don't have any layers selected, AE will create the correct type of layer for you. So if you start with an empty comp (no layers) then have your script add a null, deselect all layers, then apply your preset to the null (without selecting it first), then delete the null, you'll be left with a single layer of the correct type. If you run this test script against an empty comp, applying the preset will create a text layer and report that the preset is a "Text Preset". I haven't tested it any further than that, but I think it will work.
var myComp = app.project.activeItem;
// create a null
var myLayer = myComp.layers.addNull();
// deselect all layers
for (var i = 1; i <= myComp.numLayers; i++){
myComp.layer(i).selected = false;
}
// add preset to null
var myPreset = File("C:/Program Files/Adobe/Adobe After Effects 2024/Support Files/Presets/Text/Animate In/Typewriter.ffx");
myLayer.applyPreset(myPreset);
// delete the null
myLayer.remove();
// layer 1 must be the layer created by preset
var myPresetLayer = myComp.layer(1);
// report result
if (myPresetLayer instanceof TextLayer){
alert("Text Preset");
}else if (myPresetLayer instanceof ShapeLayer){
alert("Shape Preset");
}else{ // must be a solid (you could test for a layer with a SolidSource)
alert("Solid Preset");
}
Copy link to clipboard
Copied
/**
* Apply Presets from a folder - into a comp staggered
* =========================================================
* - Checks if a composition is active; prompts to create one if not.
* - Asks the user whether to append to the current composition if active.
* - Determines the starting time for new layers based on CTI or last layer's outPoint.
* - Prompts for layer duration and selects a folder containing .ffx presets.
* - Applies each preset to new layers, using a temporary null layer to ensure proper application.
* - Sets text for empty text layers to the preset's name.
* - Adjusts composition duration to fit all layers, with user confirmation.
* - Restores the CTI to its original position after processing.
*
* ========================================================= */
(function () {
// Store the initial CTI position
var initialCTI = app.project.activeItem ? app.project.activeItem.time : 0;
// Check if there's an active composition
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) {
// Ask the user if they want to create a new composition
var createComp = confirm("No active composition found. Do you want to create a new composition?");
if (createComp) {
// Create a new composition
comp = app.project.items.addComp("PresetComp", 1920, 1080, 1.0, 10, 30); // Initial duration of 10s
} else {
alert("No composition selected or created. Script will exit.");
return; // Exit the script
}
// Set the CTI to 0 for the new composition
initialCTI = 0;
} else {
// Ask if the user wants to append to the current composition
var appendComp = confirm("An active composition is selected. Do you want to append to the current composition?");
if (!appendComp) {
alert("Operation cancelled. Script will exit.");
return; // Exit the script
}
}
app.beginUndoGroup("Apply Presets to Layers");
// Determine the starting time for new layers
var initialStartTime = comp.time; // Default to the CTI
if (comp.numLayers > 0) {
// Find the greatest outPoint
var maxOutPoint = 0;
for (var i = 1; i <= comp.numLayers; i++) {
var layer = comp.layer(i);
if (layer.outPoint > maxOutPoint) {
maxOutPoint = layer.outPoint;
}
}
// Ask user whether to use CTI or max outPoint
var useMaxOutPoint = confirm("Existing layers detected. Do you want to start adding new layers at the end of the last layer (at " + maxOutPoint.toFixed(2) + "s) instead of the CTI at " + initialStartTime.toFixed(2) + "s?");
var maxOutPointOrCTI = "CTI";
if (useMaxOutPoint) {
initialStartTime = maxOutPoint;
maxOutPointOrCTI = "Max Layer Out Point";
}
}
// Prompt the user for the layer duration, defaulting to 3.5 seconds
var durationInput = prompt("Enter the duration for each preset layer (in seconds):", "3.5");
if (durationInput === null || durationInput === "") {
alert("No duration entered. Script will exit.");
app.endUndoGroup();
return; // Exit the script
}
var layerDuration = parseFloat(durationInput);
if (isNaN(layerDuration) || layerDuration <= 0) {
alert("Invalid duration entered. Script will exit.");
app.endUndoGroup();
return; // Exit the script
}
var presetFolder = Folder.selectDialog("Select the folder containing your presets");
if (presetFolder == null) {
alert("No folder selected. Script will exit.");
app.endUndoGroup();
return; // Exit the script
}
var presets = presetFolder.getFiles("*.ffx"); // Get all .ffx files in the folder
var presetCount = presets.length;
if (presetCount > 0) {
var totalDuration = initialStartTime + (presetCount * layerDuration);
var totalPresetDuration = presetCount * layerDuration;
// Ask user if they want to set the composition duration to fit the layers - Show workings to user
if (totalDuration != comp.duration) {
var suffixPrompt = "";
if (initialStartTime != 0) {
suffixPrompt = " (= " + totalPresetDuration.toFixed(2) + " s) + " + maxOutPointOrCTI + " time at " + initialStartTime.toFixed(2) + " s";
}
var trimComp = confirm("Set the composition duration to " + totalDuration.toFixed(2) + " s\n(" + presetCount + " presets x " + layerDuration + "s" + suffixPrompt + ") \nto fit all layers?");
if (trimComp) {
comp.duration = totalDuration;
}
}
var createdLayers = []; // Track created layers
for (var i = 0; i < presets.length; i++) {
var presetFile = presets[i];
// Set the CTI to the start time for the new layer
comp.time = Math.max(0, initialStartTime + ((i - 1) * layerDuration));
// Create a null layer temporarily
var tempNull = comp.layers.addNull();
tempNull.name = "TempNull_" + i;
// Deselect all layers
for (var j = 1; j <= comp.numLayers; j++) {
comp.layer(j).selected = false;
}
// Apply the preset to the null layer
tempNull.applyPreset(presetFile);
// Identify the new layer created by the preset
var newLayer = comp.layers[1]; // The new layer is usually the topmost layer
// Set the new layer's in and out points
newLayer.startTime = initialStartTime + (i * layerDuration);
newLayer.outPoint = newLayer.startTime + layerDuration;
// Remove the temporary null layer
tempNull.remove();
// Rename the new layer to match the preset file name, without URL encoding and extension
newLayer.name = decodeURIComponent(presetFile.name.replace(/\+/g, " ")).replace(/\.ffx$/, "");
// Store the created layer for further processing
createdLayers.push(newLayer);
// Move the CTI to the start of the new layer's out point for the next iteration
comp.time = newLayer.outPoint;
}
// Post-process text layers to set source text if empty
for (var k = 0; k < createdLayers.length; k++) {
var layer = createdLayers[k];
if (layer instanceof TextLayer) {
var textProp = layer.property("Source Text");
if (textProp.numKeys == 0 && textProp.value.text == "") {
// Set the text to the preset name if it's empty
textProp.setValue(new TextDocument(layer.name));
}
}
}
} else {
alert("No presets found in the selected folder.");
}
// Restore the initial CTI position
comp.time = initialCTI;
app.endUndoGroup();
})();
It is semi tested. Worked for what I needed. And I tried a few situations out of interest and to make the script more useful. e.g. appending to a existing comp, Using the CTI as the start of the insertion point, etc.
Copy link to clipboard
Copied
Nice! I'm glad you were able to flesh it out into something that meets your needs. Thanks for posting the result!

