// ISO_PopulateCompsWithUniqueFootage.jsx // Artist: https://www.jasonfletcher.info/ // Code generated by GPT-5.6 Luna // Date: August 14, 2026 // Description: This After Effects script will look in the "FootageTarget" folder (and its subfolders) and then assign one unique video clip per comp. The footage will be stretched to fit the comp. This operation will be executed for the selected comps in the Project window. (function () { var undoGroupStarted = false; try { // ------------------------------------------------------------ // VALIDATE PROJECT // ------------------------------------------------------------ if (app.project === null) { alert("No After Effects project is currently open."); return; } // ------------------------------------------------------------ // GET SELECTED COMPOSITIONS FROM PROJECT WINDOW // ------------------------------------------------------------ var selectedItems = app.project.selection; if (selectedItems === null || selectedItems.length === 0) { alert("Please select one or more compositions in the Project window."); return; } var selectedComps = []; var i; for (i = 0; i < selectedItems.length; i++) { if (selectedItems[i] !== null && selectedItems[i] instanceof CompItem) { selectedComps.push(selectedItems[i]); } } if (selectedComps.length === 0) { alert("No compositions were selected in the Project window."); return; } // ------------------------------------------------------------ // FIND FOOTAGETARGET FOLDER // ------------------------------------------------------------ var footageTargetFolder = null; for (i = 1; i <= app.project.numItems; i++) { var projectItem = app.project.item(i); if (projectItem !== null && projectItem instanceof FolderItem && projectItem.name === "FootageTarget") { footageTargetFolder = projectItem; break; } } if (footageTargetFolder === null) { alert('Could not find a folder named "FootageTarget" in the Project panel.'); return; } // ------------------------------------------------------------ // RECURSIVELY COLLECT FOOTAGE // ------------------------------------------------------------ var footageItems = []; function collectFootageFromFolder(folder) { var j; if (folder === null) { return; } for (j = 1; j <= folder.numItems; j++) { var child = folder.item(j); if (child === null) { continue; } if (child instanceof FootageItem) { footageItems.push(child); } else if (child instanceof FolderItem) { collectFootageFromFolder(child); } } } collectFootageFromFolder(footageTargetFolder); if (footageItems.length === 0) { alert('No footage clips were found inside "FootageTarget" or its subfolders.'); return; } // ------------------------------------------------------------ // START UNDO GROUP // ------------------------------------------------------------ app.beginUndoGroup("Populate Comps With Footage"); undoGroupStarted = true; // ------------------------------------------------------------ // PROCESS SELECTED COMPS SEQUENTIALLY // ------------------------------------------------------------ var processedCount = 0; var skippedCount = 0; var clipsToUse = selectedComps.length; if (footageItems.length < clipsToUse) { clipsToUse = footageItems.length; } for (i = 0; i < clipsToUse; i++) { var comp = selectedComps[i]; var footage = footageItems[i]; if (comp === null || footage === null) { skippedCount++; continue; } // Add footage as the top layer. var footageLayer = comp.layers.add(footage); if (footageLayer === null) { skippedCount++; continue; } // -------------------------------------------------------- // STRETCH FOOTAGE INDEPENDENTLY IN X/Y // -------------------------------------------------------- // // The footage is deliberately NOT scaled proportionally. // X and Y are calculated independently so the entire // footage frame exactly matches the comp dimensions. // if (footage.width > 0 && footage.height > 0) { var scaleX = (comp.width / footage.width) * 100; var scaleY = (comp.height / footage.height) * 100; footageLayer.property("Transform").property("Scale").setValue([ scaleX, scaleY ]); } else { skippedCount++; continue; } // Center the footage in the composition. footageLayer.property("Transform").property("Position").setValue([ comp.width / 2, comp.height / 2 ]); processedCount++; } // ------------------------------------------------------------ // FEEDBACK // ------------------------------------------------------------ var remainingComps = selectedComps.length - processedCount; if (remainingComps < 0) { remainingComps = 0; } var message = "Footage placement complete.\n\n" + "Selected compositions: " + selectedComps.length + "\n" + "Footage clips found: " + footageItems.length + "\n" + "Compositions processed: " + processedCount + "\n" + "Compositions left untouched: " + remainingComps; if (skippedCount > 0) { message += "\nSkipped items: " + skippedCount; } if (footageItems.length < selectedComps.length) { message += "\n\nThere were not enough unique footage clips to populate every selected composition."; } alert(message); } catch (err) { alert( "The script encountered an error:\n\n" + err.toString() ); } finally { if (undoGroupStarted) { app.endUndoGroup(); } } })();