• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Find and Replace function for Bridge

Explorer ,
May 17, 2022 May 17, 2022

Copy link to clipboard

Copied

Hi there. I used to have this incredible Find and Replace function for Bridge. It meant I could strip out a given part of a caption in the description field and replace it. I used it to strip out credit information. It used to look like this. I recently had to wipe and reinstall my Mac and I have lost it. Does anyone know where I can get this and how to load it back into Bridge? Thank you!Screenshot 2022-05-17 at 12.12.15.pngScreenshot 2022-05-17 at 12.10.57.png

TOPICS
How to , Metadata

Views

2.6K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , May 21, 2022 May 21, 2022
Looking at Paul Riggott's original Find Replace In Description script, I noticed that it runs this command when it loads: app.document.chooseMenuItem("PurgeCacheForSelected");
 
I'm guessing that there was a problem with metadata edits not always displaying and that purging the cache solved that. Ineed, I experienced the same blank Description as @mikejlkemp after running Find Replace In Description. I then manually purged the cache and the edited Description displayed.
 
Riggott's script used a sim
...

Votes

Translate

Translate
LEGEND ,
Oct 06, 2022 Oct 06, 2022

Copy link to clipboard

Copied

The whole idea of list items is that you have one for each data item. So your second example is correct, the first would not be to spec.

As for writing a specific script, we are willing to help but this isn't a forum for "do this work for me" requests. You may need to hire someone to help with this.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 07, 2022 Oct 07, 2022

Copy link to clipboard

Copied

@JoIIy I can help you create a script, if you still need a solution for this issue.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 10, 2022 Oct 10, 2022

Copy link to clipboard

Copied

Cheers @gregreser, Any lending hand would be greatly appreciated; my skill/ knowledge is a work in progress. I believe the DAM imports the 'Person Shown' field directly with no seperation, it doesn't edit any of the data, it just displays each field, it would look exactly how it looks in bridge. The names will change, but the format given that needs to be changed is generally from "Gandalf, Frodo" - changed to - Gandalf; Frodo.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 10, 2022 Oct 10, 2022

Copy link to clipboard

Copied

I think the easiest way to dd this is to use catenateArrayItems() and separateArrayItems() which you can read about in the very useful JavaScript Tools Guide CC 

 

I had not used those two methods before, so I took this as a learning opportunity and wrote this script. I added the ability to use other bag and seq properties so it might be useful to other people. I think others might use it on keywords.

 

I included a lot of comments so you can see how it works. There are short instructions at the top. You might want to modify it to suit you better (menu name, completion message, pass/fail log, additional functions/features, etc.). It will split the text string on every comma, so be sure that your names all formatted the same way.

 

Try it out on a few duplicate test images and let me know if works for you. You should check the XMP data to be sure it is correct, like this:

 <Iptc4xmpExt:PersonInImage>
	<rdf:Bag>
	   <rdf:li>Gandalf</rdf:li>
	   <rdf:li>Frodo</rdf:li>
	</rdf:Bag>
 </Iptc4xmpExt:PersonInImage>

 

 

#target bridge-12

// Script to split a single array (bag or seq) item containing comma separators into separate array items.
// To use it: select thumbnail(s) then go to Tools and click "Split joined array items" to run he script. When it is finished a window will open showing how many files were changed

/* features to consider
// add a UI window with instructions and a dropdown to select which property to change
// run a pre-change test to see how many images contain separators
*/

/*
Terms and Conditions of Use:
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// create menu item in Tools
if (BridgeTalk.appName == 'bridge') {
    var sJAiLabel = "Split joined array items"; // use unique variable names at this level to avoid conflicts with other StartupScripts
    var sJAiVersion = "2022-10-10";
	var sJAiMenuItem = MenuElement.create("command", sJAiLabel, "at the end of tools");
}

// when the menu item is clicked, run the script
sJAiMenuItem.onSelect = function () {
    sJAi();
    }

function sJAi(){
// Load the XMP Script library
if ( ExternalObject.AdobeXMPScript == undefined ) {
    ExternalObject.AdobeXMPScript = new ExternalObject( "lib:AdobeXMPScript" );
    }

// Register XMP namespace that is not included in XMPConst
var NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"; // IPTC Extension
XMPMeta.registerNamespace (NS_IPTC_EXT, "Iptc4xmpExt");

var namespace = NS_IPTC_EXT;
// for IPTC Core: XMPConst.NS_DC
// for IPTC Extension: NS_IPTC_EXT
var property = "PersonInImage";
// for IPTC Core: "creator" "subject"
// for IPTC Extension: "PersonInImage" "OrganisationInImageName"

var filePass = 0; // variable to count files successfully updated so it can be reported at the end

var items = app.document.selections; // array of all selected thumbnails

// for each selected thumbnail,make the change
for (var L1 = 0; L1 < items.length; L1++){ // loop through each selected thumbnail
    var file=new Thumbnail(items[L1]); // get selected file
    try{
        var xmpFile = new XMPFile(file.path, XMPConst.UNKNOWN,XMPConst.OPEN_FOR_UPDATE); // create an XMPFile
        var xmp = xmpFile.getXMP(); // get the XMP data
    
      if(xmp.doesPropertyExist(namespace, property) == true){ // if the property exists, run the edit
            var catenated = XMPUtils.catenateArrayItems(xmp, namespace, property, '; ', '"', XMPConst.SEPARATE_ALLOW_COMMAS); // Concatenates a set of array item values into a single string. An item containing a comma will not be surrounded by double quotes, so the comma will be treated as a separator by separateArrayItems, i.e., “LastName, FirstName” will be separated into two array items.
            XMPUtils.separateArrayItems(xmp, namespace, property, 0, catenated); // Updates individual array item strings string returned by catenateArrayItems(). Recognizes a large set of separator characters, including semicolons, commas, tab, return, linefeed, and multiple spaces.
           
         if (xmpFile.canPutXMP(xmp)) { // write new array to XMP
            xmpFile.putXMP(xmp);
            filePass++
            }
        
            xmpFile.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
            
            } // CLOSE  if(xmp.doesPropertyExist(namespace, property) == true) 
        } // CLOSE  try
        catch(e){
            } 
        } // CLOSE for (var i = 0; i < items.length; i++)
        
        alert(items.length+" file(s) selected\n"+filePass+" file(s) updated");
        
    } // CLOSE function sJAi()

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 13, 2022 Oct 13, 2022

Copy link to clipboard

Copied

@JoIIy did this work for you?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 17, 2022 Oct 17, 2022

Copy link to clipboard

Copied

Hey @gregreser, That worked a treat, thanks a lot! It's much appreciated, the detail is provides much needed clarity too; the only other thing I'm attempting to now tweak is instead of "Gandalf, Frodo" - it's changed to - Gandalf & Frodo.

- J

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 17, 2022 Oct 17, 2022

Copy link to clipboard

Copied

Hi @JoIIy the proccess for that will be different. You have to search each item in the array for ", " then replace it with " & "

 

Here's how you do that:

        // replace characters in an array item
        var searchChar = new RegExp(", "); // caracter(s) to be replaced
        var replaceValue = " & "; // replacement character(s)
        
        var arrayCount = xmp.countArrayItems(namespace, property); // count the array items

        for (var L2 = 0; L2 < arrayCount; L2++){ // loop through each array item

            var currentValue = xmp.getArrayItem(namespace, property, L2+1); // read the current array item value from xmp
              
            if(searchChar.test(currentValue) == true ){ // only change xmp if the current value contains the spcified character(s)
                var newValue = currentValue.toString().replace(searchChar, replaceValue); // replace old with new character(s)
                xmp.setArrayItem(namespace, property, L2+1, newValue); // set the new value in xmp
                }
            
            } // CLOSE for (var L2 = 0; L2 < arrayCount.length; L2++)

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 13, 2023 Apr 13, 2023

Copy link to clipboard

Copied

Hi 🙂

I've tested this script on Bridge 2022, I selected multiple Images that has multiple values and perform Find/Change to a specific letter that exist only in a few of them. Apart from bridge crushing 2 times when I perform the search/ find, it also changed the existed description to a different description. It didn't preserve the original description. Perhaps this script isn't meant to find part of name in the description field?

 

Here are 2 examples (out of multiple images selected):

Before:

1. Description: Amygdalus-communis-03

2. Description: Amygdalus-communis-03sw

 

Replace: sw, With: sw שקד מצוי

(Hebrew Letters)

 

After:

1. Description: Amygdalus-communis-01cosw שקד מצוי

The description changd completely and the "With" text was added even though it didn't contain the letters "sw" that I wrote in the "Replace" field. It just took a different descriptio from one of the selected photos and used it. 

 

2. Description: Amygdalus-communis-03sw שקד מצוי

The description changed correctly

 

I hope I manged to explain this correctly.

 

Thank you

Shlomit.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 13, 2023 Apr 13, 2023

Copy link to clipboard

Copied

@Shlomit Heymann I can help you with this. I'll look at the script today and see if I can find the problem.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 13, 2023 Apr 13, 2023

Copy link to clipboard

Copied

@Shlomit Heymann I think I have fixed the problem. Your find/replace examples work now. I added a feature to show an alert when the process is complete and how many files were changed. Give this version a try on a small group of test files.

#target bridge

/*
Terms and Conditions of Use:
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
Adapted from "Find Replace In Description.jsx" by Paul Riggott

version: 2023-04-13
    - only delete existing Caption/Description if Replace value is found
    - added No file(s) selected. message;  added results Alert
    - added var replaceInputEscaped to escape special characters so they are treated as strings in regex
*/

if( BridgeTalk.appName == "bridge" ) { 
    ReplaceDescription = new MenuElement("command", "Find and Replace Description/Caption", "at the end of tools");
}
 // Load the XMP Script library
if( !xmpLib){
	if( Folder.fs == "Windows" ){
		var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.dll";
	}
	else{
		var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.framework";
	}
	var libfile = new File( pathToLib );
	var xmpLib = new ExternalObject("lib:" + pathToLib );
}

ReplaceDescription.onSelect = function () { 
    var win = new Window( 'dialog', 'Find & Replace' ); 
    //g = win.graphics;
    //var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0.99, 0.99, 0.99, 1]);
    //g.backgroundColor = myBrush;
    win.orientation='column';
    win.p1= win.add("panel", undefined, undefined, {borderStyle:"black"}); 
    win.p1.preferredSize=[380,100];
    win.g1 = win.p1.add('group');
    win.g1.orientation = "row";
    win.title = win.g1.add('statictext',undefined,'Description/Caption Editor');
    win.title.alignment="fill";
    var g = win.title.graphics;
    g.font = ScriptUI.newFont("Georgia","BOLDITALIC",22);
    win.p6= win.p1.add("panel", undefined, undefined, {borderStyle:"black"}); //Replace
    win.p6.preferredSize=[380,100];
    win.g600 =win.p6.add('group');
    win.g600.orientation = "row";
    win.g600.alignment='fill';
    win.g600.st1 = win.g600.add('statictext',undefined,'Replace');
    win.g600.st1.preferredSize=[75,20];
    win.g600.et1 = win.g600.add('edittext');
    win.g600.et1.preferredSize=[200,20];
    win.g610 =win.p6.add('group');
    win.g610.orientation = "row";
    win.g610.alignment='fill';
    win.g610.st1 = win.g610.add('statictext',undefined,'With');
    win.g610.st1.helpTip="Leave this field blank if you want to remove the characters";
    win.g610.st1.preferredSize=[75,20];
    win.g610.et1 = win.g610.add('edittext');
    win.g610.et1.preferredSize=[200,20];
    win.g620 =win.p6.add('group');
    win.g620.orientation = "row";
    win.g620.alignment='fill';
    win.g620.cb1 = win.g620.add('checkbox',undefined,'Global');
    win.g620.cb1.helpTip="Replace all occurrences of";
    win.g620.cb2 = win.g620.add('checkbox',undefined,'Case Insensitive');
    win.g620.cb2.value=true;
    win.g620.cb3 = win.g620.add('checkbox',undefined,'Remove any ()[]');
    win.g620.cb3.value=false;
    win.g1000 =win.p1.add('group');
    win.g1000.orientation = "row";
    win.g1000.alignment='center';
    win.g1000.bu1 = win.g1000.add('button',undefined,'Process');
    win.g1000.bu1.preferredSize=[170,30];
    win.g1000.bu2 = win.g1000.add('button',undefined,'Cancel');
    win.g1000.bu2.preferredSize=[170,30];
    win.g1000.bu1.onClick=function(){
        if(win.g600.et1.text == ''){
            alert("No replace value has been entered!");
            return;
            }
         win.close(0);
        var matchFound = 0;
        var sels = app.document.selections;
        for(var a in sels){
            var thumb = sels[a];
            // Get the metadata object 
            md = thumb.synchronousMetadata;
            // Get the XMP packet as a string and create the XMPMeta object
            var xmp = new XMPMeta(md.serialize());
            
            var replaceInput = win.g600.et1.text.toString();
            // escape special characters so they are treated as strings in regex
            var replaceInputEscaped = replaceInput.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');

            if(win.g620.cb1.value && !win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"g");
            if(!win.g620.cb1.value && win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"i");
            if(win.g620.cb1.value && win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"gi");
            if(!win.g620.cb1.value && !win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped);
            // Read existing dc:description property
            var Caption = xmp.getArrayItem(XMPConst.NS_DC,"description", 1);
            // Change existing dc:description property based on user input
            var result=patt.test(Caption.toString());
            // if the Replace value is found in the existing Caption/Description, replace dc:descrition value with newCaption
            if(result == true){
                matchFound++;
                var newCaption = Caption.toString().replace(patt,win.g610.et1.text.toString());
                if(win.g620.cb3.value)  newCaption = newCaption.replace(/["'\(\)]/g, "");
                // Delete the dc:description property
                xmp.deleteProperty(XMPConst.NS_DC, "description");	
                // Populate the new dc:description property and set lang to x-default
                xmp.appendArrayItem(XMPConst.NS_DC, "description", newCaption, 0, XMPConst.ALIAS_TO_ALT_TEXT);
                xmp.setQualifier(XMPConst.NS_DC, "description[1]", "http://www.w3.org/XML/1998/namespace", "lang", "x-default");
                // Write the packet back to the selected file
                var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);
                thumb.metadata = new Metadata(updatedPacket);
                }
            } // close for(var a in sels)
            if(matchFound > 0){
                Window.alert("Find & Replace Complete\n"+matchFound+" file(s) changed")
                }
            else{
                Window.alert("'Replace' not found\nNo files changed")
                }
        } // close win.g1000.bu1.onClick=function    
        if(app.document.selections.length == 0){
            win.p6.enabled = false;
            win.g1000.bu1.enabled = false;
            win.title.text = "No file(s) selected. 'Cancel' then try again."
            };
    win.show();
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 14, 2023 Apr 14, 2023

Copy link to clipboard

Copied

@gregreser Thank you so much:).

It works perfectly now. I wish I'd knew to write scripts.

 

The reason I was looking to a script like this is because of apple Photos that uses the description as caption and I wanted to have the plant name when I look at photos on my iPhone. 

 

Now I was thinking that perhaps a better script for this reason will be one that will preserve the existing description with option to add characters (in a dialog box) with option of "before" or "after" the existed description.

 

Do you know if anything like that exist? I was searching a lot yesterday. I'm very interseted in finding all sort of sripts for Bridge. Do you know of a site that contain all scripts?

 

Again,

Thank you so much

Shlomit

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 14, 2023 Apr 14, 2023

Copy link to clipboard

Copied

@Shlomit Heymann You're welcome, I'm glad it's working for you.

Funny you would mention a script with the option of "before" or "after" the existed description, I've been working on just such a thing. It would also allow you to choose which field to edit. It still needs some work, but I'm more motivated now.

 

gregreser_0-1681485350632.png

 

gregreser_1-1681485380167.png

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 14, 2023 Apr 14, 2023

Copy link to clipboard

Copied

Wow, that looks fantastic. I'll be really glad to test it for you.:)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 16, 2023 Apr 16, 2023

Copy link to clipboard

Copied

@gregreser Hello again.. I've been using the Find/Replace script a few times, and again, sometimes it's working fine and somethimes it crashed bridge. I have no idea when it does that. When I restart Brdige and use it 1-3 times it's ok. Then it's crash bridge when the Find/Replace window is still open. I put comma in the Replace field and nothing in the "With" field so it will romove the comma. But it crushed also in other situations. 

Thank you,

Shlomit

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 16, 2023 Apr 16, 2023

Copy link to clipboard

Copied

Hi @Shlomit Heymann I haven't experienced a crash yet (used 10 times in a row). Your system and other paramaeters might be different. I'm using Bridge 2023 and Windows 10.

 

Does Bridge crash before you click the "Proccess" button? Knowing if just opening the script window casues the crash will be helpful.

 

I have tried one possibility: I removed some font and border parameters. Bridge 2023 has problems with UI graphics paramaeters so it's possible that is the problem.

 

Give this version a try:

#target bridge

/*
Terms and Conditions of Use:
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
Adapted from "Find Replace In Description.jsx" by Paul Riggott

version: 2023-04-13
    - only delete existing Caption/Description if Replace value is found
    - added No file(s) selected. message;  added results Alert
    - added var replaceInputEscaped to escape special characters so they are treated as strings in regex
version: 2023-04-16
	- removed , {borderStyle:"black"} from win.p1
	- removed var g = win.title.graphics;
	- removed g.font = ScriptUI.newFont("Georgia","BOLDITALIC",22);
	- removed , {borderStyle:"black"} from win.p6
*/

if( BridgeTalk.appName == "bridge" ) { 
    ReplaceDescription = new MenuElement("command", "Find and Replace Description/Caption", "at the end of tools");
}
 // Load the XMP Script library
if( !xmpLib){
	if( Folder.fs == "Windows" ){
		var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.dll";
	}
	else{
		var pathToLib = Folder.startup.fsName + "/AdobeXMPScript.framework";
	}
	var libfile = new File( pathToLib );
	var xmpLib = new ExternalObject("lib:" + pathToLib );
}

ReplaceDescription.onSelect = function () { 
    var win = new Window( 'dialog', 'Find & Replace' ); 
    //g = win.graphics;
    //var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0.99, 0.99, 0.99, 1]);
    //g.backgroundColor = myBrush;
    win.orientation='column';
    win.p1= win.add("panel", undefined, undefined); 
    win.p1.preferredSize=[380,100];
    win.g1 = win.p1.add('group');
    win.g1.orientation = "row";
    win.title = win.g1.add('statictext',undefined,'Description/Caption Editor');
    win.title.alignment="fill";
  //  var g = win.title.graphics;
  //  g.font = ScriptUI.newFont("Georgia","BOLDITALIC",22);
    win.p6= win.p1.add("panel", undefined, undefined); //Replace
    win.p6.preferredSize=[380,100];
    win.g600 =win.p6.add('group');
    win.g600.orientation = "row";
    win.g600.alignment='fill';
    win.g600.st1 = win.g600.add('statictext',undefined,'Replace');
    win.g600.st1.preferredSize=[75,20];
    win.g600.et1 = win.g600.add('edittext');
    win.g600.et1.preferredSize=[200,20];
    win.g610 =win.p6.add('group');
    win.g610.orientation = "row";
    win.g610.alignment='fill';
    win.g610.st1 = win.g610.add('statictext',undefined,'With');
    win.g610.st1.helpTip="Leave this field blank if you want to remove the characters";
    win.g610.st1.preferredSize=[75,20];
    win.g610.et1 = win.g610.add('edittext');
    win.g610.et1.preferredSize=[200,20];
    win.g620 =win.p6.add('group');
    win.g620.orientation = "row";
    win.g620.alignment='fill';
    win.g620.cb1 = win.g620.add('checkbox',undefined,'Global');
    win.g620.cb1.helpTip="Replace all occurrences of";
    win.g620.cb2 = win.g620.add('checkbox',undefined,'Case Insensitive');
    win.g620.cb2.value=true;
    win.g620.cb3 = win.g620.add('checkbox',undefined,'Remove any ()[]');
    win.g620.cb3.value=false;
    win.g1000 =win.p1.add('group');
    win.g1000.orientation = "row";
    win.g1000.alignment='center';
    win.g1000.bu1 = win.g1000.add('button',undefined,'Process');
    win.g1000.bu1.preferredSize=[170,30];
    win.g1000.bu2 = win.g1000.add('button',undefined,'Cancel');
    win.g1000.bu2.preferredSize=[170,30];
    win.g1000.bu1.onClick=function(){
        if(win.g600.et1.text == ''){
            alert("No replace value has been entered!");
            return;
            }
         win.close(0);
        var matchFound = 0;
        var sels = app.document.selections;
        for(var a in sels){
            var thumb = sels[a];
            // Get the metadata object 
            md = thumb.synchronousMetadata;
            // Get the XMP packet as a string and create the XMPMeta object
            var xmp = new XMPMeta(md.serialize());
            
            var replaceInput = win.g600.et1.text.toString();
            // escape special characters so they are treated as strings in regex
            var replaceInputEscaped = replaceInput.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');

            if(win.g620.cb1.value && !win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"g");
            if(!win.g620.cb1.value && win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"i");
            if(win.g620.cb1.value && win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped,"gi");
            if(!win.g620.cb1.value && !win.g620.cb2.value) var patt = new RegExp (replaceInputEscaped);
            // Read existing dc:description property
            var Caption = xmp.getArrayItem(XMPConst.NS_DC,"description", 1);
            // Change existing dc:description property based on user input
            var result=patt.test(Caption.toString());
            // if the Replace value is found in the existing Caption/Description, replace dc:descrition value with newCaption
            if(result == true){
                matchFound++;
                var newCaption = Caption.toString().replace(patt,win.g610.et1.text.toString());
                if(win.g620.cb3.value)  newCaption = newCaption.replace(/["'\(\)]/g, "");
                // Delete the dc:description property
                xmp.deleteProperty(XMPConst.NS_DC, "description");	
                // Populate the new dc:description property and set lang to x-default
                xmp.appendArrayItem(XMPConst.NS_DC, "description", newCaption, 0, XMPConst.ALIAS_TO_ALT_TEXT);
                xmp.setQualifier(XMPConst.NS_DC, "description[1]", "http://www.w3.org/XML/1998/namespace", "lang", "x-default");
                // Write the packet back to the selected file
                var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT);
                thumb.metadata = new Metadata(updatedPacket);
                }
            } // close for(var a in sels)
            if(matchFound > 0){
                Window.alert("Find & Replace Complete\n"+matchFound+" file(s) changed")
                }
            else{
                Window.alert("'Replace' not found\nNo files changed")
                }
        } // close win.g1000.bu1.onClick=function    
        if(app.document.selections.length == 0){
            win.p6.enabled = false;
            win.g1000.bu1.enabled = false;
            win.title.text = "No file(s) selected. 'Cancel' then try again."
            };
    win.show();
// not needed?  app.document.chooseMenuItem("PurgeCacheForSelected");
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 19, 2023 Apr 19, 2023

Copy link to clipboard

Copied

Hello @gregreser ,

Thank you for your help. I'm on Bridge 2022. I actually downgraded from 2023 after it took away ability for a new window. The crash happen after I used a script before using this one. When I restart Bridge it works fine. I have no idea why.

Here are my steps:

1. I select a few photos and and add text to description field. 

2. I than run a script that adds the file name into the description field I got here after corresponding with the creator at the end of the thread. 

3. I than run your script to make some changes.

 

When I run it the first time after I launch Bridge, it's ok. But when I run it the second time after I did the above steps, thats when Bridge crash. Maybe it's connected to the script before?

 

Thank you 🙂

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 20, 2023 Apr 20, 2023

Copy link to clipboard

Copied

Hi @Shlomit Heymann I'm not able to reproduce the crash on Bridge 2022. I've looked at the other script you are using and I can't see anything that might cause a conflict with mine, but obvioulsy something is causing a problem.

 

Are you working on an external drive or a network drive?

Are you processing a very large number of files?

If either of these is true, the process from the first script might not be completely finished before you run the second script.

 

I'll keep investigating.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Apr 20, 2023 Apr 20, 2023

Copy link to clipboard

Copied

Good morning @gregreser 

Bridge will not let me start a new script before the previous one finished... Yes, I'm on external drive. Sometimes I process large number, sometimes just a few. In the meanwhile, I have removed the comma form the other script which is the one I'm looking to find and change (delete) so I don't need to use this script. But I do like this script. I will later try to purge bridge cash, maybe it's because of that? Who knows...

 

Thank you so much for all your help 🙂 I really appriciate it. Please let me know when the other script is ready, I will gladly test it for you. 

 

have a good weekend,

Shlomit

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Apr 27, 2023 Apr 27, 2023

Copy link to clipboard

Copied

LATEST

@Shlomit Heymann my new add-find-replace script is ready for testing. It is working well for me, but plase consider it still in development and only work with test copy files. The script is called "Add-Replace Metadata" and will be in the Tools menu.

 

Click the "?" buttom for instructions. I hope they are clear, let me know.

 

I added a feature to add the file name. If you enter [FILENAME] in the Add text box, the actual file name will be added. You have to add any punctuation and space you want before or after the filename. In your case, you would enter ", [FILENAME]" (quotes aren't necessary).

The script window will stay open after it runs and you can do additional changes.

 

I have to say that adding and replacing metadata via a script makes me nervous because things could go wrong and there is no undo option. You might consider exporting all of your metadata to a text file as a backup. I can help with that, if you are interested.

 

#target bridge

/*
Terms and Conditions of Use:
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

//Create a menu item within Adobe Bridge
if (BridgeTalk.appName == 'bridge') {
    var addReplaceLbl = "Add-Replace Metadata";
    var addReplaceVers = "2023-04-26";
    addReplaceMenuItem = MenuElement.create("command", addReplaceLbl, "at the end of tools");
}
 
// when the menu item is clicked, run the script
addReplaceMenuItem.onSelect = function () {
    addReplace();
    }

function addReplace(){
    // Load the XMP Script library
    if ( ExternalObject.AdobeXMPScript == undefined ) {
        ExternalObject.AdobeXMPScript = new ExternalObject( "lib:AdobeXMPScript" );
        }
    // Register XMP namespace
    if (NS_IPTC_EXT == undefined){
        var NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"; 
        XMPMeta.registerNamespace (NS_IPTC_EXT, "Iptc4xmpExt");
        }
        
    // Main window
    // =======
    var addReplace = new Window("palette"); 
        addReplace.text = addReplaceLbl+" ("+addReplaceVers+")"; 
        addReplace.orientation = "column"; 
        addReplace.alignChildren = ["center","top"]; 
        addReplace.spacing = 10; 
        addReplace.margins = 16; 

    var fieldGrp = addReplace.add("group", undefined); 
        fieldGrp.orientation = "row"; 
        fieldGrp.alignChildren = ["left","center"]; 
        fieldGrp.spacing = 10; 
        fieldGrp.margins = 0;

    //var fieldLabel = fieldGrp.add("statictext", undefined, "Edit"); 

    var fieldsArr = [
    {label:'Title',xmpType:'langAlt',namespace:XMPConst.NS_DC,prefix:'dc',path:'title'},      
    {label:'Creator',xmpType:'seq',namespace:XMPConst.NS_DC,prefix:'dc',path:'creator'}, 
    {label:'Headline',xmpType:'text',namespace:XMPConst.NS_PHOTOSHOP,prefix:'photoshop',path:'Headline'},
    {label:'Description',xmpType:'langAlt',namespace:XMPConst.NS_DC,prefix:'dc',path:'description'},
    {label:'Keywords',xmpType:'bag',namespace:XMPConst.NS_DC,prefix:'dc',path:'subject'},
    {label:'Copyright Notice',xmpType:'langAlt',namespace:XMPConst.NS_DC,prefix:'dc',path:'rights'},
    {label:'Rights Usage Terms',xmpType:'langAlt',namespace:XMPConst.NS_XMP_RIGHTS,prefix:'xmpRights',path:'UsageTerms'}
    ];
    // {label:'Person Shown',xmpType:'bag',namespace:NS_IPTC_EXT,prefix:'Iptc4xmpExt',path:'PersonInImage'}
        
    // default property values (first item in fieldsArr). these will change if the dropdown selection is changed by the user
    var selIndex = 0;
    var selLabel = "Title";
    var selXmpType = "langAlt";
    var selNamespace = XMPConst.NS_DC;
    var selPrefix = "dc";
    var selPath = "title";

    var fieldDd = fieldGrp.add("dropdownlist", undefined, undefined);
        fieldDd.minimumSize = [200,25];
        fieldDd.maximumSize = [200,25];
    for(var L1 = 0; L1 < fieldsArr.length; L1++){
        fieldDd.add('item', fieldsArr[L1].label)
        }    
    fieldDd.selection = 0;

    var group1 = addReplace.add("group", undefined); 
        group1.orientation = "row"; 
        group1.alignChildren = ["left","center"]; 
        group1.spacing = 10; 
        group1.margins = 0; 

    var radiobutton1 = group1.add("radiobutton", undefined, undefined); 
        radiobutton1.text = "Add"; 
        radiobutton1.value = true; 

    var radiobutton2 = group1.add("radiobutton", undefined, undefined); 
        radiobutton2.text = "Replace"; 
    
    info1Btn = group1.add('button', undefined, "?");
        info1Btn.minimumSize = [32,25];
        info1Btn.maximumSize = [32,25];
        info1Btn.helpTip = "Instructions";
        info1Btn.onClick = showinfo1Win;

    var panel1 = addReplace.add("panel", undefined, undefined); 
        panel1.orientation = "column"; 
        panel1.alignChildren = ["center","top"]; 
        panel1.spacing = 2; 
        panel1.margins = 10; 

    var group2 = panel1.add("group", undefined); 
        group2.orientation = "row"; 
        group2.alignChildren = ["left","center"]; 
        group2.spacing = 10; 
        group2.margins = 0; 

    var statictext2 = group2.add("statictext", undefined, undefined); 
        statictext2.text = "Add"; 
        statictext2.minimumSize = [60,20];
        statictext2.maximumSize = [60,20];
        statictext2.justify = "right"; 
     
    // var addPositionGrp
    var addPositionGrp = group2.add("group", undefined);
        addPositionGrp.minimumSize = [120,22];
        addPositionGrp.maximumSize = [120,22];
    var addPositionDd = addPositionGrp.add("dropdownlist", undefined, undefined, {items: [" to begining"," to end"]});
        addPositionDd.minimumSize = [100,22];
        addPositionDd.maximumSize = [100,22];
        addPositionDd.selection = 0;

    var edittext1 = group2.add("edittext"); 
        edittext1.minimumSize = [250,22];
        edittext1.maximumSize = [250,22];
        
    var infoTextTxt = 
    'Add\n'+
    '   - Select "to beginning" or "to end"\n'+
    '   - Enter text in the Add box\n'+
    '   Title, Headline, Description:\n'+
    '      - Enter exact text to be added, including spaces and punctuation\n'+
    '      - Enter [FILENAME] to add the file name (without file extension)\n'+
    '   Creator, Keywords:\n'+
    '      - Commas are treated as text\n'+
    '      - Separate multiple values with semicolons (;)\n\n'+
    'Replace\n'+
    '   - Replace box: Enter text, including spaces and punctuation, to be replaced\n'+
    '   - With box: Enter replacement text. Leave blank to delete the Replace text\n\n'+
    'Note:\n'+
    'Creator and Keyword entries are stored in XMP metadata as lists of individual items\n'+
    '      Sue\n'+
    '      Mark Johnson\n'+
    '      Cutler, Michelle\n'+
    'Bridge displays multiple entries separated by a semicolon and space (; )\n'+
    'Bridge displays quotes around entries containing a comma (,)\n'+
    '      Sue; Mark Johnson; "Cutler, Michelle"'
    
    var info1WinLabel = "About adding/replacing";

    function showinfo1Win(){
       var info1Win = new Window('palette', info1WinLabel);  
       info1WinText = info1Win.add('statictext', undefined, infoTextTxt, {multiline:true});
       if (Folder.fs == "Windows"){
        info1WinText.minimumSize = [440,360];
        info1WinText.maximumSize = [440,360];
        }
    else{
        info1WinText.minimumSize = [520,460];
        info1WinText.maximumSize = [520,460];
        }               
            info1Win.cancelBtn = info1Win.add('button', undefined, 'Close');
            info1Win.cancelBtn.onClick =  function(){ 
            info1Win.hide();
            info1Win.close();
            addReplace.active = true;
        }
       info1Win.show();
       info1Win.active = true;
    }
     
    var replaceOpts = panel1.add("group", undefined);
        replaceOpts.orientation = "row"; 
        replaceOpts.alignChildren = ["left","center"]; 
        replaceOpts.spacing = 10; 
        replaceOpts.margins = 0; 
        replaceOpts.indent = 70;
        replaceOpts.minimumSize = [0,0];
        replaceOpts.maximumSize = [0,0];
        exactCB = replaceOpts.add("checkbox", undefined, "Match Case"); 
        
    var statictext3 = panel1.add("statictext", undefined, "commas are treated as text, not separators"); 
        statictext3.minimumSize = [400,25];
        statictext3.maximumSize = [400,25];
        statictext3.justify = 'center';
        
    var group3 = panel1.add("group", undefined); 
        group3.orientation = "row"; 
        group3.alignChildren = ["left","center"]; 
        group3.spacing = 10; 
        group3.margins = 0; 

    var statictext4 = group3.add("statictext", undefined, undefined); 
        statictext4.text = "With"; 
        statictext4.minimumSize = [60,20];
        statictext4.maximumSize = [60,20]; 
        statictext4.justify = "right"; 
    var group3spc = group3.add("statictext", undefined); 
        group3spc.minimumSize = [0,0];
        group3spc.maximumSize = [0,0];
    var edittext2 = group3.add("edittext", undefined); 
        edittext2.minimumSize = [250,22];
        edittext2.maximumSize = [250,22];
        
    var statictext5 = panel1.add("statictext", undefined, undefined); 
        statictext5.text = "leave blank to delete"; 
        statictext5.indent = 70;

    var statictext6 = addReplace.add("statictext", undefined, undefined, {multiline: true}); 
        statictext6.minimumSize = [300,30]; 
        statictext6.maximumSize = [300,30]; 
        statictext6.justify = "center"; 
    
    var group4 = addReplace.add("group", undefined); 
        group4.orientation = "row"; 
        group4.alignChildren = ["left","top"]; 
        group4.spacing = 40; 
        group4.margins = 0; 

    var ok = group4.add("button", undefined, undefined); 
        ok.text = "Add"; 
        ok.minimumSize = [150,30];
        ok.maximumSize = [150,30];

    var cancel = group4.add("button", undefined, undefined); 
        cancel.text = "Close"; 

    // set  default view to Add name
    statictext2.text = "Add"; 
    group3.visible = false;
    statictext5.visible=false;
    exactCB.value = true;
    // apply saved preferences
    try{
        if(app.preferences.mDeluxe_addReplace_fieldDd){ // edit which field
            fieldDd.selection = app.preferences.mDeluxe_addReplace_fieldDd;
            selIndex = app.preferences.mDeluxe_addReplace_fieldDd;
            selXmpType = fieldsArr[selIndex].xmpType;
            selLabel = fieldsArr[selIndex].label;
            selNamespace = fieldsArr[selIndex].namespace;
            selPath = fieldsArr[selIndex].path;
            }
        
        if(app.preferences.mDeluxe_addReplace_radiobutton1 == true){ // Add
            radiobutton1.value = true;
            group2.visible = true;
            statictext2.text = "Add"; 
            group3.visible = false;
            if(selXmpType == "bag"|| selXmpType == "seq"){
                statictext3.visible = true;
                statictext3.text = 'commas are treated as text, not separators';
                }
            if(selXmpType == "text" || selXmpType == "langAlt"){
                statictext3.visible = true;
                statictext3.text = 'enter exact text to be added, including spaces and punctuation';
                }
        replaceOpts.minimumSize = [0,0];
        replaceOpts.maximumSize = [0,0];
        addPositionGrp.minimumSize = [100,22];
        addPositionGrp.maximumSize = [100,22];
        statictext5.visible=false;
        ok.text = "Add"
            };
        if(app.preferences.mDeluxe_addReplace_radiobutton2 == true){ // Replace
            radiobutton2.value = true;
            group2.visible = true;
            statictext2.text = "Replace";
            group3.visible = true;
            statictext3.visible = false;
            replaceOpts.minimumSize = [400,25];
            replaceOpts.maximumSize = [400,25];
            addPositionGrp.minimumSize = [0,0];
            addPositionGrp.maximumSize = [0,0];
            statictext5.visible=true;
            ok.text = "Replace"
            };
        if(app.preferences.mDeluxe_addReplace_addPositionDd){ // add to beginning or end
            addPositionDd.selection = app.preferences.mDeluxe_addReplace_addPositionDd;
            }

        if(app.preferences.mDeluxe_addReplace_exactCB == true){ // match case
            exactCB.value = true; //app.preferences.mDeluxe_addReplace_exactCB;
            }
        else{
            exactCB.value = false;
            }
        }
        catch(e){}
    
    addReplace.show();

    // Dialog functions 
    if(app.document.selections.length == 0){
        ok.enabled = false;
        statictext6.text = "No file(s) selected. 'Close' then try again"
        }; 
    
    fieldDd.onChange=function(){ // when a field is selected, load XMP variables for read/write
        selIndex = fieldDd.selection.index;
        selLabel = fieldsArr[selIndex].label;
        selXmpType = fieldsArr[selIndex].xmpType;
        selNamespace = fieldsArr[selIndex].namespace;
        selPath = fieldsArr[selIndex].path;
        if(selXmpType == "bag" && radiobutton1.value == true || selXmpType == "seq" && radiobutton1.value == true){
            statictext3.visible = true;
            statictext3.text = 'commas are treated as text, not separators';
            }
        if(selXmpType == "text" && radiobutton1.value == true || selXmpType == "langAlt" && radiobutton1.value == true){
            statictext3.visible = true;
            statictext3.text = 'enter exact text to be added, including spaces and punctuation';
            }
        }
    
    radiobutton1.onClick = function(){
        group2.visible = true;
        statictext2.text = "Add"; 
        edittext1.text = "";
        edittext2.text = "";
        group3.visible = false;
        if(selXmpType == "bag" || selXmpType == "seq"){
            statictext3.visible = true;
            statictext3.text = 'commas are treated as text, not separators';
            }
        else{
            statictext3.visible = true;
            statictext3.text = 'enter exact text to be added, including spaces and punctuation';
            }
        replaceOpts.minimumSize = [0,0];
        replaceOpts.maximumSize = [0,0];
        addPositionGrp.minimumSize = [100,22];
        addPositionGrp.maximumSize = [100,22];
        statictext5.visible=false;
        ok.text = "Add"
        addReplace.layout.layout (true);
        }

    radiobutton2.onClick = function(){
        group2.visible = true;
        statictext2.text = "Replace";
        edittext1.text = "";
        edittext2.text = "";
        group3.visible = true;
        statictext3.visible = false;
        replaceOpts.minimumSize = [400,25];
        replaceOpts.maximumSize = [400,25];
        addPositionGrp.minimumSize = [0,0];
        addPositionGrp.maximumSize = [0,0];
        statictext5.visible=true;
        ok.text = "Replace"
        addReplace.layout.layout (true);
        }

    edittext1.addEventListener ("focus", function (e){statictext6.text = ""}); // clear result text when edittext1 is clicked
     
     var okToRun = true; // used for testForSeparator() - only used for text and langAlt functions
    
    function testForSeparator(){ // test for the presence of a separator before or after Add text input 
        if(addPositionDd.selection.index == 0){ // add to beginning
            var regExSeparators = new RegExp('[!@#\\$%\\^\\&*\\)\\(\/ +=.,:;_-]$'); // The line ends with a space or special character
            }
        if(addPositionDd.selection.index == 1){ // add to end
            var regExSeparators = new RegExp('^[!@#\\$%\\^\\&*\\)\\(\/ +=.,:;_-]'); // The line begins  with a space or special character
        }
        if(regExSeparators.test(edittext1.text.toString()) == false){          
            var showConfirm = Window.confirm("There is no space or punctuation to separate added text from existing text.\n\nContinue?", true, "No Separator")
            if( showConfirm == false){
                okToRun = false;
                }
            else{ 
                okToRun = true;
                }
            }
        else{
            okToRun = true;
            }
        } // CLOSE function testForSeparator()

    ok.onClick = function processChange(){
        var addText = edittext1.text.toString().split('\"').join(""); // remove double quotes
        if(selXmpType == 'bag' || selXmpType == 'seq'){ 
           var Replace = edittext1.text.toString().replace(/; |;|"/g, ""); // remove double quotes and semi-colons
            }
        else{
            var Replace = edittext1.text.toString();// don't remove quotes for text or lang alt  .replace(/"/g); // remove double quotes
            }
        var ReplaceEscaped = Replace.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); // escape special characters so they are treated as strings in regex
        var With = edittext2.text.toString().split('\"').join(""); // remove double quotes
        var addKey = radiobutton1.value;
        var items = app.document.selections;
        var errorLog = [];
        if(items.length > 1){
            var fileTxt = "files";
            }
        else{
            var fileTxt = "file";
            }
        
        if(Replace.length == 0){
            Window.alert("No value entered");
            }
        if(Replace.length > 0 && items.length > 0){
            var addedTo = 0;
            var matchFound = 0;
            /// write changes to XMP
            for (var i = 0; i < items.length; i++){
                if(!items[i].container && items[i].hasMetadata){ //exclude folders and only proccess files with metadata  
                    var file=new Thumbnail(items[i]);
                    try{
                        /* old method
                        var xmpFile = new XMPFile(file.path, XMPConst.UNKNOWN,XMPConst.OPEN_FOR_UPDATE);  // TODO handle RAW files?
                        var xmp = xmpFile.getXMP();
                        */
                        var ftMeta = items[i].synchronousMetadata; // Get the metadata object - wait for  valid values
                        var xmp = new XMPMeta(ftMeta.serialize()); // Get the XMP packet as a string and create the XMPMeta object
                        }
                    catch(e){
                    // Window.alert("Problem getting xmp data:-\r"  + e.message);
                      errorLog.push("Problem getting xmp data: " + e.message);
                      return;
                    }  
                if(selXmpType == 'text'){                      
                    var keyTxt = " "+selLabel; // used for completed message
                   
                    if(addKey){  // if Add is selected
                        testForSeparator();
                        if (okToRun == true){    
                            addedTo++;
                            var splitKeys = 1;
                            var New = "";
                            // not needed?    var  regex = new RegExp('FILENAME');
                            // not needed?   if(regex.test(edittext1.text.toString()) == true){ // if Add text conatins [FILENAME], add the filename wiithout the extension
                            var filenameNoExt = decodeURI(items[i].spec.name).replace(/\..+$/,""); // filename without extension - used for [FILENAME] feature
                            addText = edittext1.text.toString().replace("[FILENAME]", filenameNoExt); // replace [FILENAME] with real filename in Add text
                            // }
                            var prop = xmp.getProperty(selNamespace, selPath); // get current text value
                            if(prop){
                                if(addPositionDd.selection.index == 0){ // add to beginning
                                    New = addText+prop;
                                    }
                                else{ // add to end
                                    New = prop+addText;
                                    }
                                };
                            else{New = Replace};
                            xmp.deleteProperty(selNamespace, selPath);
                            xmp.setProperty(selNamespace, selPath, New);
                            statictext6.text = keyTxt+" text added to "+addedTo+" "+fileTxt; 
                            } // CLOSE if (okToRun)
                        } // CLOSE if(addKey)
                   
                    if(!addKey){ // if Replace is selected
                        var Lookup = xmp.getProperty(selNamespace, selPath); 
                        var result = "";
                        if(exactCB.value == false){var patt = new RegExp (ReplaceEscaped,"gi")}; // do not match case
                        if(exactCB.value == true){var patt = new RegExp (ReplaceEscaped,"g")}; // Match Case
                        if(Lookup){ // only run regex test if xmp property exists
                            result = patt.test(Lookup.toString());
                            }
                        if(result == true){
                        matchFound++;
                        var New = Lookup.toString().replace(patt,With);
                        xmp.deleteProperty(selNamespace, selPath);
                        xmp.setProperty(selNamespace, selPath, New);
                        } // CLOSE if(result == true)
                        if(matchFound > 1){var editedTxt = "files"};
                        else{var editedTxt = "file";}
                        if(matchFound == 0){
                            statictext6.text = "'"+Replace+ "' not found. No replacement made"; 
                            }
                        else{
                            statictext6.text = keyTxt+" text edited in "+matchFound+" "+editedTxt;
                            }
                        } // CLOSE  if(tmpCount >0 && !addKey)            
                    } // CLOSE if(selXmpType == 'text')
                                
                if(selXmpType == 'langAlt'){
                        var keyTxt = " "+selLabel; // used for completed message
                        
                        if(addKey){ // if Add is selected             
                            testForSeparator();
                            if (okToRun == true){ 
                                addedTo++;
                                var splitKeys = 1;
                                var New = "";
                                // not needed?    var  regex = new RegExp('FILENAME');
                                // not needed?   if(regex.test(edittext1.text.toString()) == true){ // if Add text conatins [FILENAME], add the filename wiithout the extension
                                var filenameNoExt = decodeURI(items[i].spec.name).replace(/\..+$/,""); // filename without extension - used for [FILENAME] feature
                                addText = edittext1.text.toString().replace("[FILENAME]", filenameNoExt); // replace [FILENAME] with real filename in Add text
                                // }              
                                var prop = xmp.getArrayItem(selNamespace, selPath, 1); // get current text value
                                if(prop){
                                    if(addPositionDd.selection.index == 0){ // add to beginning
                                        New = addText+prop;
                                        }
                                    else{ // add to end
                                        New = prop+addText;
                                        }
                                    };
                                else{New = Replace};
                                xmp.deleteProperty(selNamespace, selPath);
                                xmp.setLocalizedText(selNamespace, selPath, null, "x-default", New);
                                statictext6.text = keyTxt+" text added to "+addedTo+" "+fileTxt; 
                                } // CLOSE if (okToRun)
                            } // CLOSE if(addKey)
                        
                        if(!addKey){ // if Replace is selected
                            var Lookup = xmp.getArrayItem(selNamespace, selPath, 1); // get current text value 
                            var result = "";
                            if(exactCB.value == false){var patt = new RegExp (ReplaceEscaped,"gi")}; // do not match case
                            if(exactCB.value == true){var patt = new RegExp (ReplaceEscaped,"g")}; // Match Case
                            if(Lookup){ // only run regex test if xmp property exists
                                result = patt.test(Lookup.toString());
                                }
                            if(result == true){
                            matchFound++;
                            var New = Lookup.toString().replace(patt,With);
                            xmp.deleteProperty(selNamespace, selPath);
                            xmp.setLocalizedText(selNamespace, selPath, null, "x-default", New);
                            } // CLOSE if(result == true)
                            if(matchFound > 1){var editedTxt = "files"};
                            else{var editedTxt = "file";}
                            if(matchFound == 0){
                                statictext6.text = "'"+Replace+ "' not found. No replacement made"; 
                                }
                            else{
                                statictext6.text = keyTxt+" text edited in "+matchFound+" "+editedTxt;
                                }
                            } // CLOSE  if(tmpCount >0 && !addKey)
                    } // CLOSE if(selXmpType == 'langAlt')
                
                if(selXmpType == 'bag' || selXmpType == 'seq'){       
                    var  regex = new RegExp('FILENAME');
                    if(regex.test(edittext1.text.toString()) == true){
                        Window.alert("[FILENAME] add does not work for this field"); // alert if Add includes "[FILENAME]", do not process
                        return;
                        }
                
                    if(selXmpType == 'bag'){var xmpConstArray = XMPConst.ARRAY_IS_UNORDERED;}
                    if(selXmpType == 'seq'){var xmpConstArray = XMPConst.ARRAY_IS_ORDERED;}
                    try{
                        var tmpCount =  xmp.countArrayItems(selNamespace, selPath);
                        }
                    catch(e){
                        errorLog.push("Cannot get bag or seq array count \r" + e.message);
                        }
                     // if Add is selected
                    if(addKey){     
                        addedTo++;
                        var splitKeys = addText.split('; ').join(';').split (';'); // split text separated by semicolon into an array (handle either "; " or ";")
                        var keyTxt = " "+selLabel; // used for completed message
                        if(splitKeys.length > 1){keyTxt = " "+selLabel+"s"}; // used for completed message
                        var prop = []; //get existing array items
                        var newProp= []; // array for existing and added items
                        for (var L2 = 1; L2 <= (tmpCount); L2++){
                            prop.push(xmp.getArrayItem(selNamespace, selPath, L2));
                            }
                        if(addPositionDd.selection.index == 0){ // add to beginning
                                newProp = splitKeys.concat(prop);
                                }
                            else{ // add to end
                                newProp = prop.concat(splitKeys);
                                }
                        xmp.deleteProperty(selNamespace, selPath); // Delete the array if it exists                              
                        for (var L1 = 1; L1 < (newProp.length + 1); L1++){ // write each item in newProp array
                            xmp.appendArrayItem(selNamespace, selPath, newProp[L1 - 1], 0, xmpConstArray);
                            }       
                        statictext6.text = splitKeys.length+keyTxt+" added to "+addedTo+" "+fileTxt; 
                        } // CLOSE if(addKey)
                    // if Replace is selected
                    if(tmpCount >0 && !addKey){
                        var matchFound = 0;
                        for (var a =0;a<tmpCount;a++){
                        var Lookup = xmp.getArrayItem(selNamespace, selPath, a+1);
                        var result = "";
                        if(exactCB.value == false){var patt = new RegExp (ReplaceEscaped,"gi")}; // do not match case
                        if(exactCB.value == true){var patt = new RegExp (ReplaceEscaped,"g")}; // Match Case
                        if(Lookup){ // only run regex test if xmp property exists
                            result = patt.test(Lookup.toString());
                            }
                        if(result == true){
                            matchFound++;
                            var New = Lookup.toString().replace(patt,With);
                            if(!New){ // if the replace text matches all the text in the array item and with text  is empty, delete the array item
                                xmp.deleteArrayItem(selNamespace, selPath, a+1);
                                }
                            else{
                                xmp.setArrayItem(selNamespace, selPath, a+1,New); // if the Replace text matches the existing value, replace the current array item with the new text
                                }
                            } // CLOSE if(result == true)
                        }
                        if(matchFound == 0){
                            statictext6.text = "'"+Replace+ "' not found. No replacement made"; 
                            }
                        else{
                            var matchTxt = " replacement";
                            if(matchFound > 1){matchTxt = " replacements"};
                            statictext6.text = matchFound+matchTxt+" made to "+items.length+" "+fileTxt; 
                            }
                        } // CLOSE  if(tmpCount >0 && !addKey)          
                    } // CLOSE if(selXmpType == 'bag' || selXmpType == 'seq')
                var updatedPacket = xmp.serialize(XMPConst.SERIALIZE_OMIT_PACKET_WRAPPER | XMPConst.SERIALIZE_USE_COMPACT_FORMAT); // // Write the packet back to the selected file
                items[i].metadata = new Metadata(updatedPacket); // Write the packet back to the selected file
                }
            } // CLOSE if(item[i].hasMetadata)
        } // CLOSE for (var i = 0; i < items.length; i++)
    savePrefs();
    } // CLOSE if(Replace.length > 0 && items.length > 0)
    cancel.onClick = function(){
        savePrefs();
        addReplace.close();
        }

    function savePrefs(){ // save  settings to preferences
        app.preferences.mDeluxe_addReplace_radiobutton1 = radiobutton1.value; // Add
        app.preferences.mDeluxe_addReplace_radiobutton2 = radiobutton2.value; // Replace
        app.preferences.mDeluxe_addReplace_addPositionDd = addPositionDd.selection.index; // beginning or end
        app.preferences.mDeluxe_addReplace_fieldDd = fieldDd.selection.index; // which field
        app.preferences.mDeluxe_addReplace_exactCB = exactCB.value; // match case
        } 

    }// CLOSE function addReplace()

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 06, 2022 Oct 06, 2022

Copy link to clipboard

Copied

I wanted to thank you all scripters for the great work you do for the community!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines