BackupMeta creates recovery files that let you restore Opus-specific metadata. Currently supported metadata are labels and user comments. This metadata is stored in the NTFS filesystem and might get deleted by other software or when files are moved to drives that don't support this type of metadata. Examples are non-NTFS drives, cloud storage, and archives except WinRAR.
The recovery file contains the Opus commands Properties and SetAttr to restore the metadata. It should be self-explanatory and can easily be mass-edited to reflect e.g. changes in the drive/folder structure.
User comments may contain line breaks. Opus can read these multi-line comments, but cannot write them. For these files, the recovery file contains ExifTool commands.
BackupMeta supports one switch: ONLYEXPLICIT. If used, only explicitly assigned labels will be returned, wildcard and label filters will not be considered.
(Last update: 2025-02-06)
How to set up and use
Save CommandBackupMeta.js.txt to ↓
%appdata%\GPSoftware\Directory Opus\Script AddIns
Add the new command to a button, hotkey, context menu, etc. like any built-in command.
Optional: Get ExifTool and install it on your system. It's not needed to create the recovery file.
Select the files and folders for which you want to create a recovery file, and run BackupMeta on them. Folders will be read recursively. The dated recovery file will show up in
/profile\OpusBackup
. Make the necessary changes with a text editor. Depending on your system, you might need to adjust the path to exiftool.exe
. Starting a line with //
will prevent it from being executed.
Select a single recovery file and run BackupMeta to restore metadata. The script will read and execute the commands.
You can also manually copy the commands from the recovery file to an Opus button and run them from there. See below for a short demo.
Things you might enjoy reading
FAQ: How to use buttons and scripts from this forum
Docs: What are aliases?
The script's inner workings
JScript
function OnInit(initData) {
initData.name = 'BackupMeta';
initData.version = '2025-02-06';
initData.url = 'https://resource.dopus.com/t/backupmeta-backup-and-restore-opus-metadata/45497';
initData.desc = 'Write recovery commands for some metadata to a text file.';
initData.default_enable = true;
initData.min_version = '12.0';
}
function OnAddCommands(addCmdData) {
var cmd = addCmdData.AddCommand();
cmd.name = 'BackupMeta';
cmd.method = 'OnBackupMeta';
cmd.desc = 'Write recovery commands for some metadata to a text file.';
cmd.label = 'BackupMeta';
cmd.template = 'onlyexplicit/s';
cmd.hide = false;
cmd.icon = 'script';
}
function OnBackupMeta(scriptCmdData) {
var cmd = scriptCmdData.func.command;
var tab = scriptCmdData.func.sourcetab;
var dlg = scriptCmdData.func.Dlg();
var fsu = DOpus.FSUtil();
var args = scriptCmdData.func.args;
var stt = DOpus.Create().StringTools();
cmd.deselect = false;
if (tab.selected.count == 0) return;
var bakFolder = '/profile\\OpusBackup';
// var datetimeShort = 'D#yyyyMMdd-T#HHmmss'; // Opus 12
var datetimeShort = 'A#yyyyMMdd-HHmmss';
// var exeExifTool = fsu.Resolve('/programfiles\\ExifTool\\exiftool.exe');
var exeExifTool = 'exiftool.exe';
var CRLF = '\r\n';
var re = /^Recovery.+\.txt$/;
if (tab.selected_files.count == 1 && String(tab.selected_files[0].name).match(re)) {
var item = tab.selected_files[0];
var tmpFile = fsu.OpenFile(item);
var arr = stt.Decode(tmpFile.Read(), 'utf8').split(CRLF);
tmpFile.Close();
cmd.Clear();
for (var i = 0; i < arr.length; i++) {
var cmdLine = arr[i];
if (!cmdLine) continue;
if (cmdLine.substring(0, 2) == '//') continue;
cmd.AddLine(cmdLine);
}
if (cmd.linecount) {
var message = 'Execute ' + cmd.linecount + ' commands found in\n\n' + item.name + '?';
if (dlg.Request(message)) cmd.Run();
} else {
var message = 'No commands found in \n\n' + item.name + '!';
dlg.Request(message, 'OK');
}
return;
}
var prg = DOpus.Create().Command().progress;
prg.abort = true;
prg.delay = false;
prg.Init(tab.lister);
prg.SetTitle('BackupMeta');
prg.SetStatus('Enumerating selection...');
prg.SetFiles(tab.selected.count);
prg.Show();
var vec = DOpus.Create().Vector(tab.selected_files);
for (var e = new Enumerator(tab.selected_dirs); !e.atEnd(); e.moveNext()) {
var item = e.item();
vec.push_back(item);
prg.StepFiles(1);
prg.SetName(item);
var folderEnum = fsu.ReadDir(item, 'r');
while (!folderEnum.complete) {
if (prg.GetAbortState() == 'a') return;
var folderItem = folderEnum.Next();
vec.push_back(folderItem);
if (!folderItem.is_dir) continue;
prg.SetName(folderItem);
}
folderEnum.Close();
}
if (vec.empty) return;
prg.Restart();
prg.SetStatus('Analyzing ' + vec.count + ' files...');
prg.SetFiles(vec.count);
var bakFileItem = fsu.GetItem(fsu.Resolve(bakFolder + '\\Recovery-' + DOpus.Create().Date().Format(datetimeShort) + '.txt'));
cmd.RunCommand('CreateFolder NAME="' + bakFileItem.path + '"');
var bakFile = bakFileItem.Open('wa'); // create a new file, always. If the file already exists it will be overwritten.
var fileCounter = 0;
for (var e = new Enumerator(vec); !e.atEnd(); e.moveNext()) {
if (prg.GetAbortState() == 'a') break;
var item = e.item();
prg.StepFiles(1);
prg.SetName(item);
if (item.metadata == 'none') continue;
var hasMeta = false;
var itemLabels = args.onlyexplicit ? item.Labels('*', 'explicit') : item.Labels(); // only explicitly assigned labels will be returned, wildcard and label filters will not be considered
if (itemLabels.count) {
var strLabels = '';
for (var ee = new Enumerator(itemLabels); !ee.atEnd(); ee.moveNext()) {
if (strLabels) strLabels += ',';
strLabels += ee.item();
}
var cmdLine = 'Properties' +
' FILE="' + item + '"' +
' SETLABEL="' + strLabels + '"';
bakFile.Write(cmdLine + CRLF);
hasMeta = true;
}
var userComment = item.metadata.other.usercomment;
if (userComment) {
if (userComment.indexOf('\r') < 0 && userComment.indexOf('\n') < 0) {
userComment = userComment.replace(/"/g, '""');
var cmdLine = 'SetAttr' +
' FILE="' + item + '"' +
' META' +
' "usercomment:' + userComment + '"';
} else {
userComment = userComment.replace(/\r/g, '\\r');
userComment = userComment.replace(/\n/g, '\\n');
userComment = userComment.replace(/"/g, '\\"');
var cmdLine = '"' + exeExifTool + '"' +
' -escapeC' +
' -UserComment="' + userComment + '"' +
' "' + item + '"';
}
bakFile.Write(cmdLine + CRLF);
hasMeta = true;
}
if (hasMeta) {
prg.SetStatus('Generating commands for ' + ++fileCounter + ' of ' + vec.count + ' files...');
}
}
bakFile.Close();
cmd.RunCommand('Go' +
' PATH="' + bakFileItem + '"' +
' NEWTAB=findexisting' +
' OPENINDUAL');
cmd.SetFiles(tab.selected);
cmd.RunCommand('Select FROMSCRIPT SETFOCUS');
cmd.RunCommand('Select SHOWFOCUS');
}