SyncTags (Merge tags of file pairs)

SyncTags merges the tags of the selected items (files and folders) in the source with those in the destination that have the same name. The tags will be unique, sorted alphabetically, and written to both files of a pair.

The script will work in collections but not in Flatview.

SyncTags supports one argument:

Argument Description
DRYRUN List the generated SetAttr commands without executing them.

How to set up and use

:one: Save CommandSyncTags.js.txt to   

%appdata%\GPSoftware\Directory Opus\Script AddIns

:two: Add the new command to a button, hotkey, context menu, etc. like any built-in command, or run it as an Ad-hoc command.

Things you might enjoy reading

Docs: Keywords for SetAttr META
FAQ: How to use buttons and scripts from this forum

The script's inner workings

JScript
function OnInit(initData) {
    initData.name = 'SyncTags';
    initData.version = '2024-10-22';
    initData.url = 'https://resource.dopus.com/t/sync-tags-between-files/41616';
    initData.desc = 'Sync tags between pairs of files in source and destination';
    initData.default_enable = true;
    initData.min_version = '12.0';
}

function OnAddCommands(addCmdData) {
    var cmd = addCmdData.AddCommand();
    cmd.name = 'SyncTags';
    cmd.method = 'OnSyncTags';
    cmd.desc = 'Sync tags between pairs of files in source and destination';
    cmd.label = '';
    cmd.template = 'dryrun/s';
    cmd.hide = false;
    cmd.icon = 'script';
}

function OnSyncTags(scriptCmdData) {
    var cmd = scriptCmdData.func.command;
    var tab = scriptCmdData.func.sourcetab;
    var dtab = scriptCmdData.func.desttab;
    var args = scriptCmdData.func.args;
    var fsu = DOpus.FSUtil();

    cmd.deselect = false;

    if (!dtab) return;

    var vec = DOpus.Create().Vector();

    DOpus.Output('Enumerating...' + (args.dryrun ? ' (dry run)' : ''));
    DOpus.Output('');

    for (var e = new Enumerator(tab.selected); !e.atEnd(); e.moveNext()) {
        var srcItem = e.item();
        if (srcItem.metadata == 'none') continue;

        var destItem = fsu.GetItem(fsu.Resolve(dtab.path + '\\' + srcItem.name));
        if (!fsu.Exists(destItem)) continue;
        if (destItem.metadata == 'none') continue;

        DOpus.Output(srcItem);
        DOpus.Output(destItem);

        vec.assign(srcItem.metadata.tags);
        vec.append(destItem.metadata.tags);
        vec.sort();
        vec.unique(); // remove duplicates

        var tagString = '';
        for (var ee = new Enumerator(vec); !ee.atEnd(); ee.moveNext()) {
            if (tagString) tagString += ';';
            tagString += ee.item();
        }

        cmd.ClearFiles();
        cmd.AddFile(srcItem);
        cmd.AddFile(destItem);

        var cmdLine = 'SetAttr META "tags:' + tagString + '"';
        DOpus.Output(cmdLine);
        DOpus.Output('');
        if (!args.dryrun) cmd.RunCommand(cmdLine);
    }

    DOpus.Output('');
    DOpus.Output('... done.');
}
5 Likes

This looks great! If I can get it to work, it will be very useful to me. I am trying to figure out scripts and JavaScript. I had to research how to 'uncomment' the JavaScript.
Could this be edited to sync other metadata such as Title, Description, Rating, and GPS coordinates?
I am a professional photo organizer, and a huge task in all my digital projects is duplicate removal. A big challenge when de-duping is that one file in a duplicate group may be desirable to keep because it has larger pixel dimension, but another file has added metadata that needs to be preserved. Syncing the metadata would make de-duping much easier.

Yes, you can! Just remove the two leading slashes.

image

Yes. Would be rather copying than syncing, I guess? Or do you want to merge text entries? Averaging ratings would not be overly useful, I presume?

Thank you. I used the word 'sync' because this is about 'syncing' tags.

In most cases, I get files with no Tags, Title (Object), Description, Rating, or other, AND duplicate files that do have metadata in those fields.

Because I gather my clients' files from so many different Sources (EHDs, Google Photos, Apple Photos Libraries, Dropbox or other cloud storage, file system on Mac and/or PC) many of those Sources contain duplicate files and even entire duplicate folders, but not all will have had metadata added. Unfortunately, the 'best' files are not always the ones with the added metadata.

So, I would like to copy metadata from files that have metadata in those fields to files that don't have metadata in those fields.

I realize the files must have the same name, and that's no problem, as I can make them the same if they aren't.

In some cases files may have different Keywords (Tags), in which case I would love to be able to sync (or merge) them so that each file acquires the Tag(s) of the other file.

Ratings are not usually a factor, I'm not too concerned about that field.

So, since you say this will work with other metadata such as Title, Description, Rating, and GPS coordinates, please help me do that.
I think I need to determine the SetAttr META Keywords for those fields, but I don't know how/where I would add them to the script because I don't know JavaScript.
Can you please advise?

Alright, so no answers or suggestions?
I intend to figure out how to edit this to suit my needs, but can someone please tell me if I can add lines to the script for each metadata field I want to sync/copy, or if I'd need to create a script for each field?

Either way is possible. It's entirely up to you and how you want the script to work. But if you don't know JavaScript, editing the script will be difficult. Learning the basics first would be the right first step. There are loads of tutorials for learning JavaScript on the web.

The SetAttr META keywords are in the manual.

I said I had to research how to 'uncomment', but I neglected to say that I found the answer quickly. But thank you for your willingness to help.

I made a separate command for this:

Wow! That's fabulous. I look forward to trying it. I intended to try to create a script myself, but hadn't yet.
As a professional photo organizer, a primary goal of mine is to preserve information like Keywords, ratings and Descriptions my clients have added to their photos. However, information is often not added to versions of the files with the biggest file size and pixel dimensions. This will help me by copying the metadata to the bigger/better files. Thank you.

Thank you. While I appreciate the new CopyMeta script, it does not suit my needs because syncing metadata between files with the same names is my goal. I will attempt to get some help editing the 'Sync Tags' script to include Description, Rating, and Title.

Thank you so much dear lxp. This Script is very useful to me.
Please create one more script for find and replace metadata for the selected files. which should have

  1. a combo list for select metadata field name.
  2. a find what popup box for input text what should be find in selected metadata field
  3. a Replace Popup box for input text which should be replace
    Regards

Works great, once you read the fine print.

:grin:

Update 2024-10-22

  • Converted to script command
  • Added argument DRYRUN
  • Light code touch up
1 Like

lxp,
I am finding this very useful for a client's collection I am currently working on. Most of the tags added by my client were added to smaller versions of photos in a copy of her collection. I want the tags copied to the much bigger photos.
I am trying to find a way to speed up this process, so instead of working folder by folder, I want to use collections: one collection has files with Keywords/tags, and the other collection without keywords/tags. The folder names also match.
If I use flat view for the collections, will the tags be copied to any file with the same name that is in the collection without Tags? Or will the tags be copied only to files with the same name that's in a folder with the same name?
If not, is there a way to restrict the copying only when the folders have the same names?
Thank you in advance.

The short answer is: No. No. No.


Here's an enhanced version that can handle folder trees :slight_smile:

It comes with these parameters (all optional):

RECURSE Ignore selection and loop through the source (or the FROM path) and all subfolders.

PATTERN=... Only process files that match the pattern. Full support for Opus wildcards: *.jpg, grp:images, etc. Only active when RECURSE or FROM is used.

FROM=... Ignore source and use this path instead

TO=... Ignore destination and use this path instead

DRYRUN works as before: The script will only shoot blanks.

You are my beta tester. Please use DRYRUN often enough :wink:

CommandSyncTags.js.txt (2025-05-17)

lxp,
Thank you very much, but I do not have enough knowledge to implement this.
Yesterday (testing with the older version of the command) I placed the files in Source and destination Collections because the files are from several different folders. That worked with no errors only because the files matched 1:1 between Source and Destination.
Today I installed the new version with no changes or edits (because I have no idea how) and did the same thing. But when I used the new command, nothing happened. No Tags copied between Source and destination Collections.
You say it can now handle folder trees, but I don't know if or how that relates to flat view or if I still need to use collections.

Stick with folders and trees; collections and trees don't mix well.

Navigate to the base of both trees, then run the script. Here's a short demo:

The script finds pairs by replacing the source base with the destination base (here: K:\ImagesA with L:\ImagesB).

lxp,
Thank you very much for your effort, but I still don't know how to implement this for multiple folders in both Source and Destination.

I did just get it to work, but ONLY with ONE folder open in both Source and Destination.

You say to "stick with folders and trees", but because of the huge number of files and folders, I really need to use a Flat view so that I don't need to open or expand folders one by one to sync the Tags. Can I use a flat view? In your screenshot, it looks like you just expanded folders.

This might amaze you, but I have never used that command line and I don't even know what to do after entering the command. Hit 'enter'? Or type 'run'? Nothing in this forum (that I've found) has the answer to such a basic question. I make buttons or menu items for all the scripts/commands I use.

I assume 'DRYRUN' puts it in test mode so one can see what would happens. Is that correct?
I always use test folders, so It's OK if it doesn't work as expected when I test.

I want to have a button to run this for the situation I described - when there may be multiple files with the same filenames - so that the Tags sync only when the files have the same folder name.

Yes. To process folders in Flatview, pass them to the script with FROM={filepath}.

If you need more advanced path manipulation than...

... you'd need to implement this directly in the script:

var destItem = fsu.GetItem(String(srcItem.realpath).replace(srcPath, dstPath));