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
Process Folder screen
Download
Process Folder

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 2021 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 () {

    var title = "Process Folder";

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

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

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

    // SETUP

    app.displayDialogs = DialogModes.NO;

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";
    g = w.add("group");
    btnFolder = g.add("button", undefined, "Folder...");
    txtFolder = g.add("statictext", undefined, "", {
        truncate: "middle"
    });
    txtFolder.preferredSize = [350, -1]; // 350 pixels wide, default height.
    cbIncludeSubfolders = w.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");

    // 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(count + " files processed in " + ((timeEnd - timeBegin) / 1000) + " seconds", title, false);
        } catch (e) {
            if (/User cancelled/.test(e.message)) {
                alert("User cancelled", title, false);
            } else {
                alert("An error has occurred.\nLine " + e.line + ": " + e.message, title, true);
            }
        }
    }

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

    function getFiles(folder, subfolders) {
        // folder = folder object, not folder name.
        // subfolder = bool, include subfolders.
        var f;
        var files;
        var i;
        var result = [];
        files = folder.getFiles();
        for (i = 0; i < files.length; i++) {
            f = files[i];
            if (f instanceof Folder && subfolders) {
                result = result.concat(getFiles(f, subfolders));
            } else if (f instanceof File && !f.hidden) {
                result.push(f);
            }
        }
        return result;
    }

    function process() {
        var files = [];
        var i;
        files = getFiles(folder, cbIncludeSubfolders.value);
        count = 0;
        if (files.length) {
            try {
                progress();
                for (i = 0; i < files.length; i++) {
                    processFile(files[i]);
                    count++;
                }
            } finally {
                progress.close();
            }
        }
    }

    function processFile(file) {
        var doc;
        // Open the file.
        try {
            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;

        // Make Levels adjustment layer
        // It becomes 'app.activeLayer'
        var desc1 = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putClass(charIDToTypeID('AdjL'));
        desc1.putReference(charIDToTypeID('null'), ref1);
        var desc2 = new ActionDescriptor();
        var 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)
        var desc1 = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putEnumerated(charIDToTypeID('AdjL'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
        desc1.putReference(charIDToTypeID('null'), ref1);
        var desc2 = new ActionDescriptor();
        desc2.putEnumerated(stringIDToTypeID("presetKind"), stringIDToTypeID("presetKindType"), stringIDToTypeID("presetKindCustom"));
        var list1 = new ActionList();
        var desc3 = new ActionDescriptor();
        var ref2 = new ActionReference();
        ref2.putEnumerated(charIDToTypeID('Chnl'), charIDToTypeID('Chnl'), charIDToTypeID('Cmps'));
        desc3.putReference(charIDToTypeID('Chnl'), ref2);
        var 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);
    }

    function progress() {
        var w = new Window("palette");
        w.preferredSize = [400, -1]; // 400 pixels wide, default height.
        w.add("statictext", undefined, "Working, please wait...");
        w.add("statictext", undefined, "Press ESC to cancel");
        progress.close = function () {
            w.close();
        };
        w.show();
        app.refresh();
    }

})();
Download
Process Folder

License details included in download

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

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