exportdynamic.js

Modified: February 14, 2026 7:58 PM Category: Coding Notes List: Projects Created: November 18, 2025 8:37 AM Master Type: Notes Hide: No Starred: No Status: Unassigned

#target photoshop

// ---------------------------
// Get arguments from Bash/AppleScript
// arguments[0] = POS_DIR
// arguments[1] = NEG_DIR
// arguments[2] = INPUT_FOLDER
// ---------------------------
var posFolder   = arguments[0];
var negFolder   = arguments[1] || "";
var inputFolder = arguments[2];

if (!posFolder) { alert("❌ No output folder for Positives passed"); quit(); }
if (!inputFolder) { alert("❌ No input folder passed"); quit(); }

var folder = new Folder(inputFolder);
var allFiles = folder.getFiles("*.psd");
var files = [];

// Only push real, visible PSD files
for (var i = 0; i < allFiles.length; i++) {
    var f = allFiles[i];
    if (f instanceof File && f.exists && f.name[0] !== "." && !f.name.match(/^._/)) {
        files.push(f);
    }
}

app.displayDialogs = DialogModes.NO;

// ---------------------------
// Helpers
function pause(ms) { $.sleep(ms); }

function selectBaseLayer(doc) {
    var layerToSelect = null;
    for (var i = 0; i < doc.layers.length; i++) {
        if (doc.layers[i].name === "L1" || doc.layers[i].name === "Background" || doc.layers[i].name === "Layer 0") {
            layerToSelect = doc.layers[i];
            break;
        }
    }
    if (layerToSelect) { doc.activeLayer = layerToSelect; pause(150); }
    else {
        for (var i = 0; i < doc.layers.length; i++) {
            if (doc.layers[i].kind == LayerKind.NORMAL) { doc.activeLayer = doc.layers[i]; pause(100); break; }
        }
    }
}

function hideAllExceptBottomLayer(doc, keepHueSat) {
    var hiddenLayers = [];

    function recurse(layerContainer, baseLayer) {
        for (var i = 0; i < layerContainer.layers.length; i++) {
            var lyr = layerContainer.layers[i];
            if (lyr === baseLayer) continue;

            if (keepHueSat && lyr.name.match(/^Hue\/Saturation \d+/)) continue;

            if (lyr.typename === "ArtLayer" || lyr.typename === "LayerSet") {
                if (lyr.visible) {
                    lyr.visible = false;
                    hiddenLayers.push(lyr);
                }
            }

            if (lyr.typename === "LayerSet") recurse(lyr, baseLayer);
        }
    }

    var baseLayer = null;
    for (var i = doc.layers.length - 1; i >= 0; i--) {
        var lyr = doc.layers[i];
        if (lyr.visible) {
            baseLayer = lyr;
            break;
        }
    }

    if (!baseLayer) {
        alert("⚠️ No visible base layer found. Nothing will be kept visible.");
        return hiddenLayers;
    }

    recurse(doc, baseLayer);
    return hiddenLayers;
}

function hideHueSaturationLayers(doc) {
    var hiddenLayers = [];
    function recurse(layerContainer) {
        for (var i = 0; i < layerContainer.layers.length; i++) {
            var lyr = layerContainer.layers[i];
            if (lyr.name.match(/^Hue\/Saturation \d+/) && lyr.visible) {
                lyr.visible = false;
                hiddenLayers.push(lyr);
            }
            if (lyr.typename === "LayerSet") recurse(lyr);
        }
    }
    recurse(doc);
    return hiddenLayers;
}

function restoreLayers(layers) {
    for (var i = 0; i < layers.length; i++) {
        try { layers[i].visible = true; } catch(e) {}
    }
}

function saveMergedTIFF(doc, folderPath, suffix) {
    try {
        var tempDoc = doc.duplicate();
        tempDoc.flatten();

        try { 
            if (tempDoc.colorProfileName != "Adobe RGB (1998)") {
                tempDoc.convertProfile("Adobe RGB (1998)", Intent.RELATIVECOLORIMETRIC, true, true);
                pause(100);
            }
        } catch(e) {}

        var outFolder = new Folder(folderPath); 
        if (!outFolder.exists) outFolder.create();
        var saveName = doc.name.replace(/\.[^\.]+$/, "") + suffix + ".tif";
        var saveFile = new File(folderPath + "/" + saveName);

        var tiffOpts = new TiffSaveOptions();
        tiffOpts.layers = false;
        tiffOpts.embedColorProfile = true;
        tiffOpts.byteOrder = ByteOrder.IBM;
        tiffOpts.imageCompression = TIFFEncoding.TIFFLZW;
        tiffOpts.alphaChannels = false;
        tiffOpts.transparency = false;
        tiffOpts.saveImagePyramid = false;

        tempDoc.saveAs(saveFile, tiffOpts, true, Extension.LOWERCASE);
        tempDoc.close(SaveOptions.DONOTSAVECHANGES);
        pause(150);
    } catch(e) { alert("❌ Failed to save TIFF: " + e); }
}

// ---------------------------
// Main batch loop
for (var i = 0; i < files.length; i++) {
    var f = files[i]; 
    if (!(f instanceof File)) continue;

    try {
        $.sleep(200);
        app.open(f);
    } catch(e) {
        alert("⚠️ Skipping file, couldn't open: " + f.fullName + "\nError: " + e);
        continue;
    }

    var doc = app.activeDocument;

    try {
        // Ensure RGB mode
        if (doc.mode != DocumentMode.RGB) doc.changeMode(ChangeMode.RGB);

        // Switch bit depth based on filename
        if (f.name.match(/-small\.psd$/i)) {
            if (doc.bitsPerChannel != BitsPerChannelType.EIGHT) {
                doc.bitsPerChannel = BitsPerChannelType.EIGHT;
            }
        } else {
            if (doc.bitsPerChannel != BitsPerChannelType.SIXTEEN) {
                doc.bitsPerChannel = BitsPerChannelType.SIXTEEN;
            }
        }
        pause(100);

        selectBaseLayer(doc);

        if (doc.name.match(/p_Processed\.psd$/i)) {
            // Prints branch: hide all but base, but keep Hue/Saturation visible
            var hiddenPrints = hideAllExceptBottomLayer(doc, true);
            saveMergedTIFF(doc, posFolder, "");
            restoreLayers(hiddenPrints);
        } else {
            // Digital Positive: hide only Hue/Saturation layers
            var hiddenPos = hideHueSaturationLayers(doc);
            saveMergedTIFF(doc, posFolder, "-A");
            restoreLayers(hiddenPos);

            // Digital Negative: hide all except base
            if (negFolder) {
                var hiddenNeg = hideAllExceptBottomLayer(doc, false);
                saveMergedTIFF(doc, negFolder, "");
                restoreLayers(hiddenNeg);
            }
        }
    } catch(e) { alert("❌ Error processing " + doc.name + ": " + e); }

    doc.close(SaveOptions.DONOTSAVECHANGES);
}