// Fetch // (c) 2017 Leo Davidson // This is a script for Directory Opus. // See http://www.gpsoft.com.au/DScripts/redirect.asp?page=scripts for development information. // Called by Directory Opus to initialize the script function OnInit(initData) { initData.name = "Fetch"; initData.version = "1.0"; initData.copyright = "(c) 2017 Leo Davidson"; initData.url = "https://resource.dopus.com/t/fetch-command-find-and-copy-related-files/26918"; initData.desc = "A 'Fetch' command which locates and copies matching files."; initData.default_enable = true; initData.min_version = "12.6"; var cmd = initData.AddCommand(); cmd.name = "Fetch"; cmd.method = "OnFetch"; cmd.desc = "Find and copy matching files."; cmd.label = "Fetch"; cmd.template = "PATTERN/K,MOVE/S,FIRSTONLY/S,NODESELECT/S,NORECURSE/S,FROM/K/M"; cmd.hide = false; cmd.icon = "dupepane"; } // Implement the Fetch command function OnFetch(scriptCmdData) { var COMMAND_FAILED = true; var COMMAND_OK = false; var args = scriptCmdData.func.args; var searchPattern = args.pattern; var moveFiles = args.move; var firstMatchOnly = args.firstonly; var deselectMatches = !args.nodeselect; var recursive = !args.norecurse; var vecPathsToSearch = args.from; if (!vecPathsToSearch || vecPathsToSearch.empty) { // Tell caller we failed/aborted, due to invalid arguments. return COMMAND_FAILED; } var cmd = scriptCmdData.func.command; // Prevent automatic deselection. // We will explicitly deselect the files that succeed and leave the rest selected. cmd.deselect = false; var cmdToDeselect = null; if (deselectMatches) { cmdToDeselect = DOpus.Create.Command(); cmdToDeselect.SetSourceTab(cmd.sourceTab); } var searchWild = null; if (searchPattern) { searchWild = DOpus.FSUtil.NewWild(searchPattern); } // Make a list of things we want to find matches for. var mapNamesToFind = DOpus.Create.Map(); makeNameMap(scriptCmdData.func.sourcetab.selected_files, mapNamesToFind); // Search the specified folders for any matches. var mapDestToVecSources = DOpus.Create.Map(); for (var eSearch = new Enumerator(vecPathsToSearch); !eSearch.atEnd(); eSearch.moveNext()) { var searchPath = eSearch.item(); searchFolder(searchPath, searchWild, mapNamesToFind, mapDestToVecSources, cmdToDeselect, firstMatchOnly, recursive) } // If we found nothing, consider it a failure. if (mapDestToVecSources.empty) { return COMMAND_FAILED; } // Copy everything we want to copy. for (var eDest = new Enumerator(mapDestToVecSources); !eDest.atEnd(); eDest.moveNext()) { var destPath = eDest.item(); var vecFilesToCopy = mapDestToVecSources(destPath); if (!copyFiles(cmd, destPath, vecFilesToCopy, moveFiles)) { // Tell caller we failed/aborted, because a copy command failed or was aborted. return COMMAND_FAILED; } } // Anything to deselect? Do it. if (cmdToDeselect && cmdToDeselect.files && cmdToDeselect.files.count > 0) { cmdToDeselect.RunCommand("Select DESELECT FROMSCRIPT"); } return COMMAND_OK; } function makeNameMap(sel_files, mapNamesToFind) { for (var eSel = new Enumerator(sel_files); !eSel.atEnd(); eSel.moveNext()) { var fileItem = eSel.item(); var nameNoExtLower = fileItem.name_stem_m.toLowerCase(); var parentPath = fileItem.path; if (!mapNamesToFind.exists(nameNoExtLower)) { mapNamesToFind(nameNoExtLower) = DOpus.Create.Vector(); } // Record that we are looking for things like nameNoExtLower. mapNamesToFind(nameNoExtLower).push_back(fileItem); } } function searchFolder(searchPath, searchWild, mapNamesToFind, mapDestToVecSources, cmdToDeselect, firstMatchOnly, recursive) { if (mapNamesToFind.empty) { return; // Avoid listing the folder if there is nothing left to find. } var searchEnum = DOpus.FSUtil.ReadDir(searchPath, recursive); while (!searchEnum.complete) { var searchItem = searchEnum.Next; if (!searchItem.is_dir) { if (searchWild && !searchWild.Match(searchItem.name)) { continue; } var nameNoExtLower = searchItem.name_stem_m.toLowerCase(); if (!mapNamesToFind.exists(nameNoExtLower)) { continue; } for (var eSelFile = new Enumerator(mapNamesToFind(nameNoExtLower)); !eSelFile.atEnd(); eSelFile.moveNext()) { var selFile = eSelFile.item(); var destPath = DOpus.FSUtil.NewPath(selFile.realpath); destPath.Parent(); if (!mapDestToVecSources.exists(destPath)) { mapDestToVecSources(destPath) = DOpus.Create.Vector(); } // Record that we want to copy searchItem to destPath. mapDestToVecSources(destPath).push_back(searchItem); if (cmdToDeselect) { // Record that we found at least one file for the selected item. // Note: This will add the same file to cmdToDeselect multiple times if there are // multiple matches for it (implies firstMatchOnly is false). That's OK as // it is a list of files to deselect. Deselecting the same file twice is harmless. cmdToDeselect.AddFile(selFile); } } if (firstMatchOnly) { // Stop looking for nameNoExtLower if we only want the first match. mapNamesToFind.erase(nameNoExtLower); if (mapNamesToFind.empty) { return; } } } } } function copyFiles(cmd, destPath, vecFilesToCopy, moveFiles) { cmd.ClearFiles(); cmd.AddFiles(vecFilesToCopy); cmd.RunCommand("Copy " + (moveFiles ? "MOVE " : "") + "QUEUE=none AUTOSELECT=no FLATVIEWCOPY=single TO=\"" + destPath + "\""); return (cmd.Results.result != 0); }