• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Scripting apply a preset - How to tell what type of layer from a ffx file? Need some Scripting help

Participant ,
Jul 27, 2024 Jul 27, 2024

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.

TOPICS
Scripting

Views

218

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 27, 2024 Jul 27, 2024

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");
}
	

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 28, 2024 Jul 28, 2024

Copy link to clipboard

Copied

Thanks a Lot Dan.
Yep, that is the way i managed to use that method to let AE workout the layer type when adding a preset. A fantastic little trick.
I'll attach my script for anyone to use. Basically if you want to create a composition or append to an existing a bunch of demo layers for a folder of saved presets this can do it. I used it to quickly make previews on a bunch of presets that I had created years ago. 

 

/**
 * 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.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 28, 2024 Jul 28, 2024

Copy link to clipboard

Copied

LATEST

Nice! I'm glad you were able to flesh it out into something that meets your needs. Thanks for posting the result!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines