AKUI_settings_v2.txt

Opacity [0,5] [0,100]
Opacity [0,5] [100,0]
Scale [0,5] [100,100, 0,0]
Scale [0,5] [0,0, 100,100]
Scale [0,5,7] [0,0, 110,110, 100,100]
Rotation [0,5] [0,25]
Rotation [0,5] [-15,15]
Position [0,5] [0,0, 0,100]
Position [0,5] [0,0, 100,0]
Position [0,5,10] [0,0, 100,0, -100,0]
Position [0,5,10] [100,0, -100,0, 0,0]
Position [0,5,10] [0,0, 100,100, 0,0]

ny_AddKeyframeUI_v4.2.jsx

// AddKeyframeUI_v4.2.jsx
(function (thisObj) {

    var settingsFile = null;

    function readSettingsFile() {
        if (!settingsFile) {
            settingsFile = File.openDialog("Select AKUI_settings.txt", "*.txt");
        }
        if (!settingsFile || !settingsFile.exists) return null;
        if (!settingsFile.open("r")) {
            alert("Failed to open file.");
            return null;
        }
        var lines = [];
        while (!settingsFile.eof) {
            var line = settingsFile.readln();
            if (line.replace(/^\s+|\s+$/g, "") !== "") lines.push(line);
        }
        settingsFile.close();
        return lines;
    }

    function parseSettingLine(line) {
        var pattern = /(\w+) \[([-\d,\s]+)\] \[([-\d,\s]+)\]/;
        var match = line.match(pattern);
        if (!match) return null;
        var prop = match[1];

        var rawFrames = match[2].split(/,\s*/);
        var frames = [];
        for (var i = 0; i < rawFrames.length; i++) {
            frames.push(parseFloat(rawFrames[i]));
        }

        var rawValues = match[3].split(/,\s*/);
        var values = [];
        for (var j = 0; j < rawValues.length; j++) {
            values.push(parseFloat(rawValues[j]));
        }

        return { property: prop, frames: frames, values: values, raw: line };
    }

    function applyKeyframes(layer, setting, repeatCount) {
        var comp = layer.containingComp;
        var frameDuration = 1 / comp.frameRate;
        var currentTime = comp.time;
        if (!repeatCount || repeatCount < 1) repeatCount = 1;

        var prop = layer.property(setting.property);
        if (!prop) {
            alert("Layer \"" + layer.name + "\" does not have \"" + setting.property + "\".");
            return;
        }

        var baseKeyframes = [];
        var is2D = (setting.property === "Position" || setting.property === "Scale" || setting.property === "Anchor Point");

        for (var i = 0; i < setting.frames.length; i++) {
            var frame = setting.frames[i];
            var t = frame * frameDuration;
            var v;
            if (setting.property === "Position") {
                var base = prop.value;
                v = [
                    base[0] + setting.values[i * 2],
                    base[1] + setting.values[i * 2 + 1]
                ];
            } else if (is2D) {
                v = [
                    setting.values[i * 2],
                    setting.values[i * 2 + 1]
                ];
            } else {
                v = setting.values[i];
            }
            baseKeyframes.push({ frame: frame, value: v });
        }

        var keyframes = [];
        var minFrame = Math.min.apply(null, setting.frames);
        var maxFrame = Math.max.apply(null, setting.frames);
        var chunkSpan = maxFrame - minFrame;
        var offsetSpan = chunkSpan * 2;

        for (var r = 0; r < repeatCount; r++) {
            var offset = r * offsetSpan;
            for (var k = 0; k < baseKeyframes.length; k++) {
                var newTime = currentTime + ((baseKeyframes[k].frame + offset) * frameDuration);
                keyframes.push({ time: newTime, value: baseKeyframes[k].value });
            }
        }

        for (var j = 0; j < keyframes.length; j++) {
            prop.setValueAtTime(keyframes[j].time, keyframes[j].value);
        }
    }

    function buildUI(thisObj, settings) {
        var win = (thisObj instanceof Panel)
            ? thisObj
            : new Window("palette", "AddKeyframeUI v4", undefined, { resizeable: true });

        win.orientation = "column";
        win.alignChildren = ["fill", "top"];

        var reloadBtn = win.add("button", undefined, "🔄 Reload Settings File");
        reloadBtn.onClick = function () {
            if (!settingsFile || !settingsFile.exists) {
                alert("No file loaded or file missing.");
                return;
            }
            var lines = readSettingsFile();
            if (!lines) return;
            var parsed = [];
            for (var i = 0; i < lines.length; i++) {
                var s = parseSettingLine(lines[i]);
                if (s) parsed.push(s);
            }
            win.close();
            buildUI(thisObj, parsed);
        };

        for (var i = 0; i < settings.length; i++) {
            (function (setting) {
                var group = win.add("group");
                group.orientation = "row";

                var text = group.add("statictext", undefined, setting.raw);
                text.characters = 40;

                var applyBtn = group.add("button", undefined, "apply");
                applyBtn.onClick = function () {
                    var comp = app.project.activeItem;
                    if (!(comp && comp instanceof CompItem)) {
                        alert("Please open a composition.");
                        return;
                    }
                    var layers = comp.selectedLayers;
                    if (layers.length === 0) {
                        alert("Please select at least one layer.");
                        return;
                    }

                    app.beginUndoGroup("Apply Keyframes");
                    for (var j = 0; j < layers.length; j++) {
                        applyKeyframes(layers[j], setting, 1);
                    }
                    app.endUndoGroup();
                };

                var repeatInput = group.add("edittext", undefined, "");
                repeatInput.characters = 4;

                var repeatBtn = group.add("button", undefined, "repeat");
                repeatBtn.onClick = function () {
                    var repeatCount = parseInt(repeatInput.text, 10);
                    if (isNaN(repeatCount) || repeatCount < 1) {
                        alert("Please enter a valid repeat count.");
                        return;
                    }

                    var comp = app.project.activeItem;
                    if (!(comp && comp instanceof CompItem)) {
                        alert("Please open a composition.");
                        return;
                    }
                    var layers = comp.selectedLayers;
                    if (layers.length === 0) {
                        alert("Please select at least one layer.");
                        return;
                    }

                    app.beginUndoGroup("Repeat Keyframes");
                    for (var j = 0; j < layers.length; j++) {
                        applyKeyframes(layers[j], setting, repeatCount);
                    }
                    app.endUndoGroup();
                };

            })(settings[i]);
        }

        if (win instanceof Window) win.show();
        else win.layout.layout(true);

        return win;
    }

    var rawLines = readSettingsFile();
    if (!rawLines) return;
    var parsedSettings = [];
    for (var i = 0; i < rawLines.length; i++) {
        var setting = parseSettingLine(rawLines[i]);
        if (setting) parsedSettings.push(setting);
    }
    buildUI(thisObj, parsedSettings);

})(this);