Process Folder

Script for Adobe Photoshop

This code is my template for whipping up quick scripts to attack the challenge of the day. It's open source so anyone is free to alter it however suits your needs. The script is the bare essentials to select a folder and do something to the files in it. No options for what is done, except the choice to include subfolders. I simply plug in the code I want run on each file, and get it done. Here I'm sharing it so you can do the same. Sometimes we find a nice snippet of code to solve a problem, but we want it done to more than one image. Replace the code between the markers in the 'processFile' function, and run the script on a folder of images. Or build on the template to make your own specialized solution.

  • Run script code on a folder of images
  • Option to include files in subfolders
  • Adapt open source to customize or create other scripts
Download
Process Folder

FREE to download
Please make a contribution

Many scripts are free to download thanks to the support of users. Help me keep developing new scripts by supporting my work. Click any one of the buttons below to make a contribution of any amount. Thank you.

× Stripe

Choose an amount to contribute.
Thank you for supporting my work.

How-to Video

NOTE: video is a programming tutorial that demonstrates how to use Adobe Photoshop Scripting Listener. During the tutorial, this script, Process Folder, is featured to show a practical use for code generated by Scripting Listener. The code used in this script may be from a trusted online source, or use Scripting Listener to record your own steps in Photoshop, to create a customized solution.

How to use the script

The interface is minimal. A button to select the folder, and the option to include subfolders. Click the OK button to begin. While processing, press ESC to cancel.

Example task

In the source code (below), between the markers I've added an example task. This adds a Levels adjustment layer, and sets it to 50-1.5-200. The unrealistic values can be changed to anything more meaningful. I just wanted something for demonstration purposes that clearly shows the images have been altered.

You don't have to be a programmer to customize this script. Edit the file in any text editor, and it's clear to see the begin and end markers, between which add code to run on each file. We often find snippets of code on the Adobe forum when searching for a solution to some challenge. Copy and paste and give it a go. Make sure to have backup copies of the images, just in case. I can't guarantee what code you might plug into the script, and what it might do to your images. Always make backups first!

Or use the script as a template, as I do. Copy the file to a new name, add the desired code to run on each file, and it's your own specialized solution. Near the top of the script is the variable "title." That is what shows in the title bar of the window and alerts. Change it to the name of your choice.

I've added many comments to the code to help make things clearer. Read through to get a feel for what's going on inside the script. Contact me any time for help, or ideas for new scripts.

Source code

(download button below)

/*

Process Folder
Copyright 2022 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact

Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

//@target photoshop

(function () {

    app.displayDialogs = DialogModes.ERROR;

    var title = "Process Folder";

    // Script variables.
    var abort;
    var count;
    var folder;
    var progress;
    var timeBegin;
    var timeEnd;

    // Reusable UI variables.
    var g; // group
    var p; // panel
    var w; // window

    // Permanent UI variables.
    var btnCancel;
    var btnFolder;
    var btnOk;
    var cbIncludeSubfolders;
    var txtFolder;

    // SET UP

    // CREATE PROGRESS WINDOW

    progress = new Window("palette", "Progress", undefined, {
        "closeButton": false
    });
    progress.t = progress.add("statictext");
    progress.t.preferredSize = [450, -1];
    progress.b = progress.add("progressbar");
    progress.b.preferredSize = [450, -1];
    progress.add("statictext", undefined, "Press ESC to cancel");
    progress.display = function (message) {
        message && (this.t.text = message);
        this.show();
        app.refresh();
    };
    progress.increment = function () {
        this.b.value++;
    };
    progress.set = function (steps) {
        this.b.value = 0;
        this.b.minvalue = 0;
        this.b.maxvalue = steps;
    };

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";
    p = w.add("panel");
    p.margins = [18, 18, 18, 18];
    p.alignChildren = "left";
    g = p.add("group");
    btnFolder = g.add("button", undefined, "Folder...");
    txtFolder = g.add("statictext", undefined, "", {
        truncate: "middle"
    });
    txtFolder.preferredSize = [350, -1];
    cbIncludeSubfolders = p.add("checkbox", undefined, "Include subfolders");

    // Action Buttons
    g = w.add("group");
    g.alignment = "center";
    btnOk = g.add("button", undefined, "OK");
    btnCancel = g.add("button", undefined, "Cancel");

    // Panel Copyright
    p = w.add("panel");
    p.add("statictext", undefined, "Copyright 2022 William Campbell");

    // UI ELEMENT EVENT HANDLERS

    btnFolder.onClick = function () {
        var f = Folder.selectDialog("Select folder");
        if (f) {
            txtFolder.text = Folder.decode(f.fsName);
            folder = f;
        }
    };
    btnOk.onClick = function () {
        if (!folder) {
            alert("Select folder", " ", false);
            return;
        }
        w.close(1);
    };
    btnCancel.onClick = function () {
        w.close(0);
    };

    // DISPLAY THE DIALOG

    if (w.show() == 1) {
        try {
            timeBegin = new Date();
            process();
            timeEnd = new Date();
            alert(abort || count + " files processed in " + ((timeEnd - timeBegin) / 1000) + " seconds", title, false);
        } catch (e) {
            if (/User cancel/.test(e.message)) {
                alert("Canceled", title, false);
            } else {
                alert("An error has occurred.\nLine " + e.line + ": " + e.message, title, true);
            }
        }
    }

    //====================================================================
    //               END PROGRAM EXECUTION, BEGIN FUNCTIONS
    //====================================================================

    function getFiles(folder, subfolders, extensions) {
        // folder = folder object, not folder name.
        // subfolders = true to include subfolders.
        // extensions = string, extensions to include.
        // Combine multiple extensions with RegExp OR i.e. jpg|psd|tif
        // extensions case-insensitive.
        // extensions undefined = any.
        // Ignores hidden files and folders.
        var f;
        var files;
        var i;
        var pattern;
        var results = [];
        if (extensions) {
            pattern = new RegExp("\." + extensions + "$", "i");
        } else {
            // Any extension.
            pattern = new RegExp(
                "\.8PBS|AFX|AI|ARW|BLZ|BMP|CAL|CALS|CIN|CR2|CRW|CT|DCM|DCR|DCS|DCX|DDS|" +
                "DIB|DIC|DNG|DPX|EPS|EPSF|EXR|FFF|FIF|GIF|HDP|HDR|HEIC|HEIF|ICB|ICN|ICO|" +
                "ICON|IIQ|IMG|J2C|J2K|JIF|JIFF|JP2|JPC|JPE|JPEG|JPF|JPG|JPS|JPX|JXR|KDK|" +
                "KMZ|KODAK|MOS|MRW|NCR|NEF|ORF|PAT|PBM|PCT|PCX|PDD|PDF|PDP|PEF|PGM|PICT|" +
                "PMG|PNG|PPM|PS|PSB|PSD|PSDC|PSID|PVR|PXR|RAF|RAW|RLE|RSR|SCT|SRF|TGA|TIF|" +
                "TIFF|TRIF|U3D|VDA|VST|WBMP|WDP|WMP|X3F$", "i");
        }
        files = folder.getFiles();
        for (i = 0; i < files.length; i++) {
            f = files[i];
            if (!f.hidden) {
                if (f instanceof Folder && subfolders) {
                    // Recursive (function calls itself).
                    results = results.concat(getFiles(f, subfolders, extensions));
                } else if (f instanceof File && pattern.test(f.name)) {
                    // Ignore EPS if not raster.
                    if (/\.(eps)$/i.test(f.name)) {
                        // Determine if creator is Photoshop.
                        f.open("r");
                        // If Photoshop, creator will be early.
                        // Save time and read only first 512 kb.
                        var data = f.read(512);
                        f.close();
                        if (!/Adobe Photoshop/.test(data)) {
                            // NOT Photoshop EPS.
                            continue;
                        }
                    }
                    results.push(f);
                }
            }
        }
        return results;
    }

    function process() {
        var files;
        var i;
        app.displayDialogs = DialogModes.NO;
        progress.display("Reading folder...");
        try {
            files = getFiles(folder, cbIncludeSubfolders.value);
            if (!files.length) {
                abort = "No files found in selected folder";
                return;
            }
            progress.set(files.length);
            count = 0;
            for (i = 0; i < files.length; i++) {
                progress.display(File.decode(files[i].name));
                processFile(files[i]);
                count++;
                progress.increment();
            }
        } finally {
            progress.close();
            app.displayDialogs = DialogModes.ERROR;
        }
    }

    function processFile(file) {
        var doc;
        try {
            // Open the file.
            doc = app.open(file);
        } catch (e) {
            if (/open options are incorrect/.test(e.message)) {
                // Photoshop can't open.
                // Ignore this file.
                return;
            } else {
                // Some other error. Throw it.
                throw e;
            }
        }
        //
        // Do something to the open 'doc'.
        // And because 'doc' was just opened, it is 'app.activeDocument',
        // the typical target of code generated by Scripting Listener.
        //
        // ====================== MARKER: BEGIN PER FILE TASK ==========================
        // Replace the code between the markers with any other for another task.

        // EXAMPLE TASK:
        // Create 'Levels' adjustment layer.
        // Set black=50, gamma=1.5, white=200
        // These are extreme values for demonstration purposes.
        // Change to more reasonable values suited to the project.

        var black = 50;
        var gamma = 1.5;
        var white = 200;

        var desc1;
        var desc2;
        var desc3;
        var list1;
        var list2;
        var ref1;
        var ref2;

        // Make Levels adjustment layer
        // It becomes 'app.activeLayer'
        desc1 = new ActionDescriptor();
        ref1 = new ActionReference();
        ref1.putClass(charIDToTypeID('AdjL'));
        desc1.putReference(charIDToTypeID('null'), ref1);
        desc2 = new ActionDescriptor();
        desc3 = new ActionDescriptor();
        desc3.putEnumerated(stringIDToTypeID("presetKind"), stringIDToTypeID("presetKindType"), stringIDToTypeID("presetKindDefault"));
        desc2.putObject(charIDToTypeID('Type'), charIDToTypeID('Lvls'), desc3);
        desc1.putObject(charIDToTypeID('Usng'), charIDToTypeID('AdjL'), desc2);
        executeAction(charIDToTypeID('Mk  '), desc1, DialogModes.NO);

        // Set black, gamma, and white of app.activeLayer
        // (activeLayer should be the Levels adjustment layer just created)
        desc1 = new ActionDescriptor();
        ref1 = new ActionReference();
        ref1.putEnumerated(charIDToTypeID('AdjL'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
        desc1.putReference(charIDToTypeID('null'), ref1);
        desc2 = new ActionDescriptor();
        desc2.putEnumerated(stringIDToTypeID("presetKind"), stringIDToTypeID("presetKindType"), stringIDToTypeID("presetKindCustom"));
        list1 = new ActionList();
        desc3 = new ActionDescriptor();
        ref2 = new ActionReference();
        ref2.putEnumerated(charIDToTypeID('Chnl'), charIDToTypeID('Chnl'), charIDToTypeID('Cmps'));
        desc3.putReference(charIDToTypeID('Chnl'), ref2);
        list2 = new ActionList();
        list2.putInteger(black); // <================================= black value
        list2.putInteger(white); // <================================= white value
        desc3.putList(charIDToTypeID('Inpt'), list2);
        desc3.putDouble(charIDToTypeID('Gmm '), gamma); // <========== gamma value
        list1.putObject(charIDToTypeID('LvlA'), desc3);
        desc2.putList(charIDToTypeID('Adjs'), list1);
        desc1.putObject(charIDToTypeID('T   '), charIDToTypeID('Lvls'), desc2);
        executeAction(charIDToTypeID('setd'), desc1, DialogModes.NO);

        // Done!

        // ======================= MARKER: END PER FILE TASK ===========================

        // Close doc when done.
        // If process is only to gather information, don't save.
        // If something was changed, save changes.
        // HAVE A BACKUP OF THE IMAGES! Don't take chances.
        // Change to the desired SaveOptions listed below:
        //     SaveOptions.SAVECHANGES
        //     SaveOptions.DONOTSAVECHANGES
        //     SaveOptions.PROMPTTOSAVECHANGES
        doc.close(SaveOptions.SAVECHANGES);
    }

})();
× Stripe

Choose an amount to contribute.
Thank you for supporting my work.

Download
Process Folder

License details included in download

For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.

Also available for hire to program custom solutions. Contact William for more information.

IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.