Just for future reference, posting Ai code is prohibited here since it almost always writes bad, made-up code that the user is now asking coders to help fix the Ai slop. And I agree with that. We (non-coders) shouldn't waste coders time by posting non-working Ai code for someone else to fix. We (non-coders) think it'll be a good starting point, when it's not.
For someone like myself, who knows zero coding and doesn't have the correct brain to learn it (I've tried for decades), I think it's ok if working Ai code is posted since I'm not asking for any help. Please correct me if I'm wrong.
With that being said, I've had great success with about 4 JScript/VBscript buttons now, using Grok. Grok works best if you tell it to 'think harder' so it reasons with itself. And also to tell it that you're coding a Directory Opus button. I used 'Grok 4 Fast Beta' for this and finished it within an hour, less than 20 questions for troubleshooting and tweaks. I started with your description of what you wanted (just copied and pasted).
You can paste the below JScript code into Grok if you want to make changes OR want each section explained in detail to learn about it, it does a great job explaining things.
I used 6 test folders. Only the test6 folder had clean files, hence why it's not listed.
Example Output:
Multi-Part Archive Folder Completeness Check
Generated: Wednesday, November 5, 2025 2:29:09 PM
Selected folders checked: 6
Incomplete folders:
O:\.Temp\test\test1 - contains 4 non-archive files
O:\.Temp\test\test2 - inconsistent part sizes in 1 part: 3
O:\.Temp\test\test3 - missing 2 parts: 6, 7; inconsistent part sizes in 1 part: 3
O:\.Temp\test\test4 - multiple archive sets detected
O:\.Temp\test\test5 - contains 1 subdirectory; contains 2 non-archive files; no multi-part archives found
The Button:
Create a button and set it to "Script Function", Type is "JScript" and paste in this code:
function OnClick(clickData)
{
var tab = clickData.func.sourcetab;
var sel = tab.selected;
var folders = [];
var enumSel = new Enumerator(sel);
for (;!enumSel.atEnd(); enumSel.moveNext()) {
var item = enumSel.item();
if (item.is_dir) {
folders.push(item.realpath);
}
}
if (folders.length == 0) {
DOpus.Output("No folders selected.");
return;
}
var incomplete = [];
for (var f=0; f<folders.length; f++) {
var folderpath = folders[f];
var fldr = DOpus.FSUtil.ReadDir(folderpath, false);
var archives = [];
var issues = [];
var subdirCount = 0;
var nonArchiveCount = 0;
var invalidArchiveCount = 0;
while (!fldr.complete) {
var it = fldr.Next();
if (!it) continue;
if (it.is_dir) {
subdirCount++;
continue;
}
var extl = it.ext.toLowerCase();
if (!(extl == ".rar" || extl == ".zip" || /^\.z\d+$/.test(extl))) {
nonArchiveCount++;
continue;
}
var name = it.name;
var base = "";
var part = 0;
var archType = "";
var isValid = false;
if (extl == ".rar") {
var match = name.match(/^(.*)\.part([0-9]+)\.rar$/i);
if (match) {
base = match[1];
part = parseInt(match[2], 10);
archType = "rar";
isValid = true;
}
} else if (extl == ".zip") {
var match = name.match(/^(.*)\.zip$/i);
if (match) {
base = match[1];
part = 1;
archType = "zip";
isValid = true;
}
} else { // .zNN
var match = name.match(/^(.*)\.z([0-9]+)$/i);
if (match) {
base = match[1];
part = parseInt(match[2], 10);
archType = "zip";
isValid = true;
}
}
if (!isValid) {
invalidArchiveCount++;
continue;
}
archives.push({
base: base,
part: part,
type: archType,
size: it.size.val,
ext: extl
});
}
if (subdirCount > 0) {
var subdirText = subdirCount > 1 ? "subdirectories" : "subdirectory";
issues.push("contains " + subdirCount + " " + subdirText);
}
if (nonArchiveCount > 0) {
var nonArchText = nonArchiveCount > 1 ? "non-archive files" : "non-archive file";
issues.push("contains " + nonArchiveCount + " " + nonArchText);
}
if (invalidArchiveCount > 0) {
var invArchText = invalidArchiveCount > 1 ? "non-multi-part archives" : "non-multi-part archive";
issues.push("contains " + invalidArchiveCount + " " + invArchText);
}
if (archives.length == 0) {
issues.push("no multi-part archives found");
} else if (archives.length == 1) {
issues.push("insufficient number of parts");
} else {
// Check same base
var base = archives[0].base;
var baseOk = true;
for (var i=1; i<archives.length; i++) {
if (archives[i].base !== base) {
baseOk = false;
break;
}
}
if (!baseOk) {
issues.push("multiple archive sets detected");
} else {
// Check same type
var typ = archives[0].type;
var typeOk = true;
for (var i=1; i<archives.length; i++) {
if (archives[i].type !== typ) {
typeOk = false;
break;
}
}
if (!typeOk) {
issues.push("mixed archive types");
}
// Adjust parts for split ZIP
if (typ == "zip") {
var zipEntry = null;
var zMax = 0;
for (var k=0; k<archives.length; k++) {
if (archives[k].ext == ".zip") {
zipEntry = archives[k];
} else {
zMax = Math.max(zMax, archives[k].part);
}
}
if (zipEntry && zMax > 0) {
zipEntry.part = zMax + 1;
}
}
// Check parts
var parts = [];
var dupOk = true;
for (var i=0; i<archives.length; i++) {
var p = archives[i].part;
var exists = false;
for (var j=0; j<parts.length; j++) {
if (parts[j] === p) {
exists = true;
break;
}
}
if (exists) {
dupOk = false;
} else {
parts.push(p);
}
}
if (!dupOk) {
issues.push("duplicate part numbers");
}
if (dupOk) {
parts.sort(function(a,b){return a-b;});
var maxPart = parts[parts.length-1];
var seqOk = (parts[0] === 1 && parts.length === maxPart);
if (!seqOk) {
var missing = [];
for (var p=1; p<=maxPart; p++) {
var found = false;
for (var j=0; j<parts.length; j++) {
if (parts[j] === p) {
found = true;
break;
}
}
if (!found) {
missing.push(p);
}
}
var missText = missing.length > 1 ? "parts" : "part";
issues.push("missing " + missing.length + " " + missText + ": " + missing.join(", "));
}
}
// Check sizes
archives.sort(function(a,b){return a.part - b.part;});
var common = archives[0].size;
var inconsistent = [];
for (var i=0; i<archives.length - 1; i++) {
if (archives[i].size !== common) {
inconsistent.push(archives[i].part);
}
}
if (inconsistent.length > 0) {
var incText = inconsistent.length > 1 ? "parts" : "part";
issues.push("inconsistent part sizes in " + inconsistent.length + " " + incText + ": " + inconsistent.join(", "));
}
if (archives.length > 1 && archives[archives.length-1].size > common) {
issues.push("last part larger than others");
}
}
}
if (issues.length > 0) {
var reason = issues.join("; ");
incomplete.push(folderpath + " - " + reason);
}
}
var now = new Date();
function pad(num, size) {
var s = num + "";
while (s.length < size) s = "0" + s;
return s;
}
var datestr = now.getFullYear() + "-" + pad(now.getMonth()+1, 2) + "-" + pad(now.getDate(), 2);
var reportName = "ArchiveCheck_" + datestr + ".txt";
var reportPath = tab.path + "\\" + reportName;
var fso = new ActiveXObject("Scripting.FileSystemObject");
if (fso.FileExists(reportPath)) {
fso.DeleteFile(reportPath);
}
var txtFile = fso.CreateTextFile(reportPath, true);
txtFile.WriteLine("Multi-Part Archive Folder Completeness Check");
txtFile.WriteLine("Generated: " + now.toLocaleString());
txtFile.WriteLine("Selected folders checked: " + folders.length);
txtFile.WriteLine("");
if (incomplete.length === 0) {
txtFile.WriteLine("All selected folders contain complete multi-part archives.");
} else {
txtFile.WriteLine("Incomplete folders:");
for (var i=0; i<incomplete.length; i++) {
txtFile.WriteLine(incomplete[i]);
}
txtFile.WriteLine("");
}
txtFile.Close();
// DOpus.Output("Report created: " + reportPath);
}