Skip to main content
Stephen Marsh
Community Expert
Community Expert
August 19, 2024
Open for Voting

Add Support for IPTC/PLUS Coalition - Generative AI/ML training Metadata

  • August 19, 2024
  • 13 replies
  • 922 views

I believe that it would be a good thing to add native File Info support for the following to Adobe products via File Info and Metadata Templates:

 

https://iptc.org/news/exclude-images-from-generative-ai-iptc-photo-metadata-standard-2023-1/

 

Caveats: Not for Adobe Stock and this requires that the embedded metadata is not stripped out  :]

13 replies

Participating Frequently
August 14, 2025

Totally agree with this! We need protection from AI scraping! 

Stephen Marsh
Community Expert
Community Expert
November 1, 2024

@Ultimatebeetle 

 

I think the jury is out on that... It's an opt-in and honour system, and I don't believe that any of the AI players are following such flags, but it can't hurt.  :]

Ultimatebeetle
Inspiring
November 1, 2024

Thank you! These scripts look very useful.

Stephen Marsh
Community Expert
Community Expert
October 31, 2024

I have updated the Photoshop script to a v2.0, now including batch processing:

 

 

* The interface defaults to "Single Document" mode on an open/active document.

 

* The interface defaults to "Batch Processing" mode if the script is run without any open documents. Select a root/top-level folder and optionally all sub-folders.

 

/*
Add IPTC PLUS Generative AI-ML Metadata scriptUI GUI v2.jsx
v1.0 - 26th September 2024, Stephen Marsh
v1.1 - 27th September 2024, Added a "None" option to remove data mining metadata
https://community.adobe.com/t5/photoshop-ecosystem-ideas/add-support-for-iptc-plus-coalition-generative-ai-ml-training-metadata/idi-p/14808232

https://iptc.org/news/exclude-images-from-generative-ai-iptc-photo-metadata-standard-2023-1/
https://ns.useplus.org/LDF/ldf-XMPSpecification#DataMining

Regional laws applying to an asset may prohibit, constrain, or allow data mining for certain purposes (such as search indexing or research), and may overrule the value selected for this property. Similarly, the absence of a prohibition does not indicate that the asset owner grants permission for data mining or any other use of an asset. The prohibition “Prohibited except for search engine indexing” only permits data mining by search engines available to the public to identify the URL for an asset and its associated data (for the purpose of assisting the public in navigating to the URL for the asset), and prohibits all other uses, such as AI/ML training.
*/

#target photoshop

// Create the ScriptUI dialog
var dialog = new Window("dialog", "Add IPTC PLUS Data Mining Permission Metadata (v2.0)");
dialog.orientation = "column";
dialog.alignChildren = ["fill", "top"];
dialog.preferredSize.width = 450;
dialog.spacing = 10;
dialog.margins = 16;

// Create panel for single document processing
var singlePanel = dialog.add("panel", undefined, "Single Document Processing Options");
singlePanel.orientation = "column";
singlePanel.alignChildren = ["fill", "top"];
singlePanel.margins = 16;

// Add "Data Mining Permission" label and dropdown to singlePanel
singlePanel.add("statictext", undefined, "Data Mining Permission:");
var singleDropdown = singlePanel.add("dropdownlist", undefined, [
    "Unspecified - no prohibition defined",
    "Allowed",
    "Prohibited for AI/ML training",
    "Prohibited for Generative AI/ML training",
    "Prohibited except for search engine indexing",
    "Prohibited",
    "None (Remove existing metadata)"
]);
singleDropdown.selection = 4; // Default selection

// Array of corresponding values for metadata entries
var dropdownValues = [
    "DMI-UNSPECIFIED",
    "DMI-ALLOWED",
    "DMI-PROHIBITED-AIMLTRAINING",
    "DMI-PROHIBITED-GENAIMLTRAINING",
    "DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING",
    "DMI-PROHIBITED",
    ""
];

// Create panel for batch processing
var batchPanel = dialog.add("panel", undefined, "Batch Processing Options");
batchPanel.orientation = "column";
batchPanel.alignChildren = ["fill", "top"];
batchPanel.margins = 16;

// Add "Data Mining Permission" label and dropdown to batchPanel
batchPanel.add("statictext", undefined, "Data Mining Permission:");
var batchDropdown = batchPanel.add("dropdownlist", undefined, [
    "Unspecified - no prohibition defined",
    "Allowed",
    "Prohibited for AI/ML training",
    "Prohibited for Generative AI/ML training",
    "Prohibited except for search engine indexing",
    "Prohibited",
    "None (Remove existing metadata)"
]);
batchDropdown.selection = 4; // Default selection

// Folder selection for batch processing
batchPanel.add("statictext", undefined, "Select Folder:");
var browseGroup = batchPanel.add("group");
browseGroup.orientation = "row";
browseGroup.alignChildren = ["left", "center"];
browseGroup.spacing = 5;

var selectFolderButton = browseGroup.add("button", undefined, "Select Folder");
selectFolderButton.preferredSize.width = 80;

var folderPath = browseGroup.add("edittext");
folderPath.text = "No folder selected";
folderPath.enabled = false; // Read-only
folderPath.alignment = ["fill", "center"]; // Fill width

// Checkbox to include subfolders
var subFolderCheckbox = batchPanel.add("checkbox", undefined, "Include Subfolders");

// Set panel states based on open documents
if (app.documents.length === 0) {
    batchPanel.enabled = true;
    singlePanel.enabled = false;
} else {
    batchPanel.enabled = false;
    singlePanel.enabled = true;
}

// Select folder button functionality
selectFolderButton.onClick = function () {
    var selectedFolder = Folder.selectDialog("Select the root folder for batch processing");
    if (selectedFolder) folderPath.text = selectedFolder.fsName;
};

// Create Cancel and OK buttons
var buttonGroup = dialog.add("group");
buttonGroup.orientation = "row";
buttonGroup.alignment = ["right", "top"];
var cancelButton = buttonGroup.add("button", undefined, "Cancel");
var okButton = buttonGroup.add("button", undefined, "OK");

// Show the dialog
if (dialog.show() == 1) {
    var contVocabEntry = (batchPanel.enabled ? dropdownValues[batchDropdown.selection.index] : dropdownValues[singleDropdown.selection.index]);

    // Check if the AdobeXMPScript library is available
    if (ExternalObject.AdobeXMPScript === undefined) {
        try {
            ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
        } catch (e) {
            alert("Unable to load AdobeXMPScript. Please ensure the library is available.");
            dialog.close();
        }
    }

    // Function to update XMP metadata for a given file without opening it
    function updateXMP(filePath) {
        var xmpFile = new XMPFile(filePath.fsName, XMPConst.FILE_JPEG, XMPConst.OPEN_FOR_UPDATE);
        var xmp = xmpFile.getXMP();

        if (contVocabEntry === "") {
            xmp.deleteProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining");
        } else {
            xmp.setProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining", "https://ns.useplus.org/ldf/vocab/" + contVocabEntry);
        }

        if (xmpFile.canPutXMP(xmp)) {
            xmpFile.putXMP(xmp);
        }
        xmpFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
    }

    // Process the active document if batch processing is disabled
    if (!batchPanel.enabled) {
        var xmp = new XMPMeta(app.activeDocument.xmpMetadata.rawData);

        if (contVocabEntry === "") {
            xmp.deleteProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining");
        } else {
            xmp.setProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining", "https://ns.useplus.org/ldf/vocab/" + contVocabEntry);
        }

        app.activeDocument.xmpMetadata.rawData = xmp.serialize();
        alert("Data Mining Permission set to: " + "\r" + singleDropdown.selection.text);
    }
    // Process files in the selected folder and subfolders if batch processing is enabled
    else {
        if (folderPath.text === "" || folderPath.text === "No folder selected") {
            alert("Please select a folder for batch processing.");
        } else {
            var rootFolder = new Folder(folderPath.text);

            // Recursive function to process files in folder and subfolders if selected
            function processFolder(folder) {
                var files = folder.getFiles();
                for (var i = 0; i < files.length; i++) {
                    var file = files[i];
                    if (file instanceof Folder && subFolderCheckbox.value) {
                        processFolder(file); // Recurse into subfolders
                    } else if (file instanceof File && file.name.match(/\.(png|webp|jpg|jpeg|tif|tiff|psd|psb)$/i)) {
                        updateXMP(file); // Update metadata for valid image files
                    }
                }
            }

            processFolder(rootFolder);
            alert("Batch processing complete. Data Mining Permission set to: " + "\r" + batchDropdown.selection.text);
        }
    }
} else {
    dialog.close();
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html 

Legend
September 30, 2024

And final update posted, switching method of editing xmp and allowing the GUI to remain open or auto-close.

Legend
September 29, 2024

Yes that's how. I usually serialize the XMP because there are more tools to work with it. I was just being lazy 😉

Stephen Marsh
Community Expert
Community Expert
September 28, 2024
quote

I could completely remove the namespace instead of just making it blank but that takes a bit different approach and using XMPUTILS so I left it like this.


By @Lumigraphics 

 

I ironically used AI/ML to revise the Bridge script! :]

 

/*
Add IPTC PLUS Generative AI-ML Metadata from Bridge Tools scriptUI GUI.jsx
v1.0 - 19th August 2024, Stephen Marsh
Modified 26th September 2024 by David M. Converse - Added a scriptUI GUI and a "None" option
Modified 28th September 2024, by Stephen Marsh - Updated the code from David to completely remove the metadata without leaving any "debris" when "None" is selected
https://community.adobe.com/t5/photoshop-ecosystem-ideas/add-support-for-iptc-plus-coalition-generative-ai-ml-training-metadata/idi-p/14808232

https://iptc.org/news/exclude-images-from-generative-ai-iptc-photo-metadata-standard-2023-1/
https://ns.useplus.org/LDF/ldf-XMPSpecification#DataMining

Example Controlled Vocabulary Entries:

https://ns.useplus.org/ldf/vocab/DMI-UNSPECIFIED
https://ns.useplus.org/ldf/vocab/DMI-ALLOWED
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-AIMLTRAINING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-GENAIMLTRAINING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED
*/

#target bridge

if (BridgeTalk.appName == 'bridge') {
    var MLmenu = MenuElement.create('command', 'Add IPTC PLUS GenAI-ML Metadata Window', 'at the end of Tools');
}
try {
    MLmenu.onSelect = function () {
        if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript'); //load XMP Library
        var plusPrefix = 'https://ns.useplus.org/ldf/vocab/'; //shared prefix
        var contVocabEntry = ['DMI-UNSPECIFIED', 'DMI-ALLOWED', 'DMI-PROHIBITED-AIMLTRAINING', 'DMI-PROHIBITED-GENAIMLTRAINING', 'DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING', 'DMI-PROHIBITED'];
        var ddlItem = contVocabEntry[0]; //initialize
        var sels = app.document.selections;
        var MLWindow = new Window('palette', '', undefined); //create window
        var MLPanel = MLWindow.add('panel', undefined, 'PLUS Generative AL-ML Restrictions');
        MLPanel.alignChildren = ['fill', 'top'];
        MLPanel.margins = [10, 10, 10, 10];
        MLPanel.ddl = MLPanel.add('dropdownlist', undefined, '');
        MLPanel.ddl.preferredSize = [350, 25];
        
        // Populate dropdown list
        for (var i = 0; i < contVocabEntry.length; i++) {
            MLPanel.ddl.add('item', contVocabEntry[i]);
        }
        MLPanel.ddl.add('item', 'NONE'); // Add 'None' option
        MLPanel.ddl.selection = 0;
        
        // Cancel and Process buttons
        var MLGroup = MLWindow.add('group', undefined, '');
        MLGroup.orientation = 'row';
        MLGroup.alignChildren = ['fill', 'top'];
        var cancelButton = MLGroup.add('button', undefined, 'Cancel');
        cancelButton.preferredSize = [80, 25];
        var processButton = MLGroup.add('button', undefined, 'Process');
        processButton.preferredSize = [80, 25];

        // Cancel button closes the window
        cancelButton.onClick = function () {
            MLWindow.close();
        };

        // Process button to update metadata
        processButton.onClick = function () {
            if (sels.length < 1) { // No selections
                MLWindow.close();
                return;
            }

            for (var j = 0; j < sels.length; j++) {
                if (sels[j].hasMetadata && !sels[j].container) { // Not a folder
                    var md = sels[j].synchronousMetadata;
                    var xmp = new XMPMeta(md.serialize()); // Create XMPMeta from file metadata

                    // Remove existing DataMining metadata
                    XMPUtils.removeProperties(xmp, 'http://ns.useplus.org/ldf/xmp/1.0/', 'DataMining', XMPConst.REMOVE_ALL_PROPERTIES);

                    // If dropdown selection is not 'None', add the new metadata
                    if (MLPanel.ddl.selection.index != 6) { // 'None' is at index 6
                        var selectedValue = plusPrefix + contVocabEntry[MLPanel.ddl.selection.index];
                        xmp.setProperty('http://ns.useplus.org/ldf/xmp/1.0/', 'DataMining', selectedValue);
                    }

                    // Write the updated metadata back to the file
                    var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER); // Serialize without extra wrapper
                    sels[j].metadata = new Metadata(updatedPacket); // Assign the new metadata back to the file
                }
            }

            MLWindow.close(); // Close the window after processing
        };

        MLWindow.layout.layout(true);
        MLWindow.show();
        MLWindow.active = true;
    };
}
catch (e) {
    Window.alert(e + ' ' + e.line);
}

 

Minor updates to the Photoshop 1.2 version script:

 

/*
Add IPTC PLUS Generative AI-ML Metadata to Open Doc scriptUI GUI v1-2.jsx
v1.0 - 26th September 2024, Stephen Marsh
v1.1 - 27th September 2024, Added a "None" option to remove data mining metadata
v1.2 - 31st October 2024, Cosmetic Update to dropdown list text and default selection
https://community.adobe.com/t5/photoshop-ecosystem-ideas/add-support-for-iptc-plus-coalition-generative-ai-ml-training-metadata/idi-p/14808232

https://iptc.org/news/exclude-images-from-generative-ai-iptc-photo-metadata-standard-2023-1/
https://ns.useplus.org/LDF/ldf-XMPSpecification#DataMining

Regional laws applying to an asset may prohibit, constrain, or allow data mining for certain purposes (such as search indexing or research),and may overrule the value selected for this property. Similarly, the absence of a prohibition does not indicate that the asset owner grants permission for data mining or any other use of an asset. The prohibition “Prohibited except for search engine indexing” only permits data mining by search engines available to the public to identify the URL for an asset and its associated data (for the purpose of assisting the public in navigating to the URL for the asset), and prohibits all other uses, such as AI/ML training.
*/

#target photoshop

if (app.documents.length) {
    // Create the ScriptUI dialog
    var dialog = new Window("dialog", "Add IPTC PLUS Data Mining Permission Metadata (v1.2)");
    dialog.orientation = "column";
    dialog.alignChildren = ["left", "top"];
    dialog.preferredSize.width = 450;
    dialog.spacing = 10;
    dialog.margins = 16;

    // Create the label and dropdown menu
    dialog.add("statictext", undefined, "Data Mining Permission:");
    var dropdown = dialog.add("dropdownlist", undefined, [
        "Unspecified - no prohibition defined",
        "Allowed",
        "Prohibited for AI/ML training",
        "Prohibited for Generative AI/ML training",
        "Prohibited except for search engine indexing",
        "Prohibited",
        "None (Remove existing metadata)"  // Remove data mining metadata
    ]);
    dropdown.selection = 4; // Prohibited except for search engine indexing

    // Array of corresponding values
    var dropdownValues = [
        "DMI-UNSPECIFIED",
        "DMI-ALLOWED",
        "DMI-PROHIBITED-AIMLTRAINING",
        "DMI-PROHIBITED-GENAIMLTRAINING",
        "DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING",
        "DMI-PROHIBITED",
        ""  // "None"
    ];

    // Create OK and Cancel buttons
    var buttonGroup = dialog.add("group");
    buttonGroup.orientation = "row";
    buttonGroup.alignment = ["right", "top"];
    var cancelButton = buttonGroup.add("button", undefined, "Cancel");
    var okButton = buttonGroup.add("button", undefined, "OK");

    // Show the dialog
    if (dialog.show() == 1) {
        var contVocabEntry = dropdownValues[dropdown.selection.index];

        if (ExternalObject.AdobeXMPScript === undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
        var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);

        if (contVocabEntry === "") {
            xmp.deleteProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining");
        } else {
            xmp.setProperty("http://ns.useplus.org/ldf/xmp/1.0/", "DataMining", "https://ns.useplus.org/ldf/vocab/" + contVocabEntry);
        }

        app.activeDocument.xmpMetadata.rawData = xmp.serialize();

        alert("Data Mining Permission set to: " + "\r" + dropdown.selection.text);
    }
} else {
    alert("A document must be open to run this script!");
}

 

 

Legend
September 27, 2024

@Stephen Marsh 

Yeah creating the GUI in every script is crazy, both Windows and Mac have built-in frameworks for building apps that don't require hand coding. I could completely remove the namespace instead of just making it blank but that takes a bit different approach and using XMPUTILS so I left it like this. I'm glad you posted this as I wasn't aware but I'm tagging all my images.

Stephen Marsh
Community Expert
Community Expert
September 26, 2024

@Lumigraphics 

 

Thanks David, that’s fantastic! I like the "NONE" option to remove said metadata. I don't script Bridge very often and a GUI is extra hard work for me as well, so thank you for contributing.

Legend
September 26, 2024

Stephen, I modified your Bridge script to add a GUI and some error checking. *Final update*

 

 

/*
Add PLUS Generative AI-ML Metadata from Bridge Tools.jsx
v1.0 - 19th August 2024, Stephen Marsh
Modified 9-30-24 David M. Converse
https://community.adobe.com/t5/photoshop-ecosystem-ideas/add-support-for-iptc-plus-coalition-generative-ai-ml-training-metadata/idi-p/14808232

https://ns.useplus.org/LDF/ldf-XMPSpecification#DataMining

Example Controlled Vocabulary Entries:

https://ns.useplus.org/ldf/vocab/DMI-UNSPECIFIED
https://ns.useplus.org/ldf/vocab/DMI-ALLOWED
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-AIMLTRAINING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-GENAIMLTRAINING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING
https://ns.useplus.org/ldf/vocab/DMI-PROHIBITED
*/
#target bridge
if(BridgeTalk.appName == 'bridge'){
    var MLmenu = MenuElement.create('command', 'Add PLUS GenAI-ML Metadata', 'at the end of Tools'); 
    }
try{
    MLmenu.onSelect = function(){
        if(ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript'); //load XMP Library
        var plusPrefix = 'https://ns.useplus.org/ldf/vocab/'; //shared prefix
        var ns = 'http://ns.useplus.org/ldf/xmp/1.0/';
        var contVocabEntry = ['DMI-UNSPECIFIED', 'DMI-ALLOWED', 'DMI-PROHIBITED-AIMLTRAINING', 'DMI-PROHIBITED-GENAIMLTRAINING', 'DMI-PROHIBITED-EXCEPTSEARCHENGINEINDEXING', 'DMI-PROHIBITED']
        var ddlItem = contVocabEntry[0]; //initialize
        var sels = null;
        var md = null;
        var xmp = null;
        var updatedPacket = null;
        var j = 0;
        MLWindow = new Window('palette', '', undefined); //create window
        MLPanel = MLWindow.add('panel', undefined, 'PLUS Generative AL-ML Restrictions');
        MLPanel.alignChildren = ['fill', 'top'];
        MLPanel.margins = [10, 10, 10, 10];
        MLPanel.ddl = MLPanel.add('dropdownlist', undefined, '');
        MLPanel.ddl.preferredSize = [350, 25];
        for(var i = 0; i < 6; i++){ //add listitems
            ddlItem = contVocabEntry[i]; //walk through array of settings
            MLPanel.ddl.add('item', ddlItem);
            }
        MLPanel.ddl.add('item', 'None');
        MLPanel.ddl.selection = 0;
        MLGroup = MLWindow.add('group', undefined, '');
        MLGroup.orientation = 'row';
        MLGroup.alignChildren = ['fill', 'top'];
        MLGroup.cb = MLGroup.add('checkbox', undefined, 'Auto-close');
        MLGroup.cb.value = true;
        MLGroup.cb.preferredSize = [80, 25];
        MLGroup.spacer = MLGroup.add('statictext', undefined, '');
        MLGroup.spacer.size = [40, 25];
        MLGroup.button1 = MLGroup.add('button', undefined, 'Cancel');
        MLGroup.button1.preferredSize = [80, 25];
        MLGroup.button2 = MLGroup.add('button', undefined, 'Process');
        MLGroup.button2.preferredSize = [80, 25];

        MLGroup.button1.onClick = function(){ //cancel
            MLWindow.close();
            return;
            }
            
        MLGroup.button2.onClick = function(){ //process
            sels = app.document.selections;
            if(sels.length < 1){ //no selections
                Window.alert('Please select at least one file to process.'); 
                }
            else{
                for(j = 0; j < sels.length; j++){
                    try{
                        if(sels[j].hasMetadata && !sels[j].container){ //not a folder
                            md = sels[j].synchronousMetadata; //get metadata
                            xmp = new XMPMeta(md.serialize());
                            xmp.deleteProperty(ns, 'DataMining');
                            if(MLPanel.ddl.selection.index != 6){ //write value if not 'None'
                                xmp.setProperty(ns, 'DataMining', plusPrefix + contVocabEntry[MLPanel.ddl.selection.index]);
                                }
                            updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);
                            sels[j].metadata = new Metadata(updatedPacket); //write to file
                            }
                        }
                    catch(e){
                        Window.alert(e + e.line);
                        }
                    }
                if(MLGroup.cb.value == true){
                    MLWindow.close();
                    }
                }
            }

        MLWindow.layout.layout(true);
        MLWindow.show();
        MLWindow.active = true;
        }
    }
catch(e){
    Window.alert(e + ' ' + e.line);
    }