// Isosceles_PrerenderedFootageIntoMatchingComps.jsx
// Code generated by ChatGPT-5.5
// Date: June 9, 2026
// Description: This After Effects script will search for a footage item with the same name as the original source comp and then add it as the top-most layer in the comp. This is known as pre-rendering (or freezing). Works with both video footage and image sequences. This operation will be executed for the selected comps in the Project window.
// IMPORTANT NOTE: You should import the footage into After Effects before executing this script.

(function () {

    app.beginUndoGroup("Add Matching Rendered Footage");

    var project = app.project;

    if (!project) {
        alert("No project is open.");
        return;
    }

    var selectedItems = project.selection;

    if (!selectedItems || selectedItems.length === 0) {
        alert("Please select one or more comps in the Project panel.");
        return;
    }

    function isFootageItem(item) {
        return (item instanceof FootageItem);
    }

    function isCompItem(item) {
        return (item instanceof CompItem);
    }

    function normalizeName(name) {

        var result = name;

        // Remove common extensions.
        result = result.replace(/\.[^\.]+$/i, "");

        // Remove image sequence ranges.
        result = result.replace(/_\[[^\]]+\]$/i, "");

        // Remove trailing frame numbers.
        result = result.replace(/[_\- ]\d+$/i, "");

        return result;
    }

    function findBestMatchingFootage(compName) {

        var exactMatch = null;
        var prefixMatch = null;

        var normalizedCompName = normalizeName(compName).toLowerCase();

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

            var item = project.item(i);

            if (!isFootageItem(item))
                continue;

            if (isCompItem(item))
                continue;

            var itemName = item.name;
            var normalizedItemName = normalizeName(itemName).toLowerCase();

            // Exact normalized match
            if (normalizedItemName === normalizedCompName) {
                exactMatch = item;
                break;
            }

            // Starts with comp name
            if (
                normalizedItemName.indexOf(normalizedCompName) === 0 &&
                prefixMatch === null
            ) {
                prefixMatch = item;
            }
        }

        return exactMatch || prefixMatch;
    }

    function compContainsSource(comp, sourceItem) {

        for (var i = 1; i <= comp.numLayers; i++) {

            var layer = comp.layer(i);

            if (layer.source === sourceItem) {
                return true;
            }
        }

        return false;
    }

    var addedCount = 0;
    var missingMatches = [];
    var alreadyPresent = [];

    for (var s = 0; s < selectedItems.length; s++) {

        var comp = selectedItems[s];

        if (!(comp instanceof CompItem))
            continue;

        var footage = findBestMatchingFootage(comp.name);

        if (!footage) {
            missingMatches.push(comp.name);
            continue;
        }

        if (compContainsSource(comp, footage)) {
            alreadyPresent.push(comp.name);
            continue;
        }

        var layer = comp.layers.add(footage);

        // Make top-most layer.
        layer.moveToBeginning();

        addedCount++;
    }

    app.endUndoGroup();

    var report = "";

    report += "Matching footage added into " + addedCount + " comps";

    if (alreadyPresent.length > 0) {
        report += "\n\nAlready contained matching footage:\n";
        report += alreadyPresent.join("\n");
    }

    if (missingMatches.length > 0) {
        report += "\n\nNo matching footage found:\n";
        report += missingMatches.join("\n");
    }

    alert(report);

})();
