Reduce folders levels in a compressed File

Hello, I am dealing a lot of time trying to solve a problem I have.
Using chatgpt I finally get to have a script working, but when I began to use it, I have directories too long then it doesn't work.
Finally I desesperate and instead of asking ChatGPT I am going to ask to DOPUS support :smiley:
I am sure this issue must have been solved by someone somewhere.. but don't know where :sob:
I am manipulating many COMICS files. Some have a subdirectory and many files in it.
What I want is to extract the files in the subdirectory to the main file and delete the subdir.

SITUATION INITIAL

  • File.cbz (or cbr or zip or rar)
  • -- Subidrectory
  • ------ File1
  • ------ File2
  • ------ File3

SITUATION DESIRED

  • File.cbz
  • --File1
  • --File2
  • --File3

Thanks for any suggestion

Try this button:
Move Selected Items to the Parent.dcf (879 Bytes)

The code is:

@disablenosel
@keydown:none
@set ChildPath={sourcepath$|noterm}
Copy MOVE TO {sourcepath|..} WHENEXISTS=rename
@keydown:shift
@set ChildPath={sourcepath$|noterm}
Copy MOVE TO {sourcepath|..} WHENEXISTS=rename
Delete FILE="{$ChildPath}" NORECYCLE SKIPNOTEMPTY QUIET

With archives, it's probably best to extract them, then recompress the desired folder to a new archive. Renaming/moving lots of files around within archives is possible but often slower, as it's done one file at a time (at least currently).

You can use the Folder Count column to quickly identify archives that have sub-folders and skip the ones that don't.

Thank you @jinsight and @Leo for the fast help.

I understand you are suggesting instead of using directly the command
Copy MOVE
I should use the command
Copy EXTRACT
and later
Copy ARCHIVE
to recompress
But in the uncompress world, should I use Copy MOVE for changing from the subdirectory to the main level or do I have to do differently.

By the way @jinsight I was very surprised how short was your code. What I get from ChatGPT was much longuer

function OnClick(clickData)
{
var source = clickData.func.sourcetab;
var cmd    = clickData.func.command;
cmd.deselect = false; // Leave source files selected.

var objFSO = new ActiveXObject("Scripting.FileSystemObject");
var tempDir = "C:\\TempProcessing";

// Crear el directorio temporal
if (!objFSO.FolderExists(tempDir)) {
    objFSO.CreateFolder(tempDir);
}

// Procesar cada archivo seleccionado
for (var e = new Enumerator(source.selected); !e.atEnd(); e.moveNext())
{
    var f = e.item();
    var tempArchivePath = tempDir + "\\" + objFSO.GetFileName(f.RealPath);
    
    // Copiar el archivo al directorio temporal
    if (objFSO.FileExists(f.RealPath)) {
        objFSO.CopyFile(f.RealPath, tempArchivePath);

        // Procesar el archivo temporalmente
        ConvertArchive(tempArchivePath, f.RealPath, tempDir);
    } else {
        WScript.Echo("El archivo no existe: " + f.RealPath);
    }
}

// Eliminar el directorio temporal después del procesamiento
if (objFSO.FolderExists(tempDir)) {
    objFSO.DeleteFolder(tempDir, true);
}
}

function ConvertArchive(tempArchivePath, originalPath, tempDir)
{
var objShell = new ActiveXObject("Shell.Application");
var objFSO = new ActiveXObject("Scripting.FileSystemObject");

var extractFolder = tempDir + "\\" + objFSO.GetBaseName(tempArchivePath);
var archiveName = objFSO.GetBaseName(tempArchivePath);
var cbzPath = tempDir + "\\" + archiveName + ".cbz";

if (objFSO.GetExtensionName(tempArchivePath).toLowerCase() === "zip" || objFSO.GetExtensionName(tempArchivePath).toLowerCase() === "cbz") {
    ExtractZip(tempArchivePath, extractFolder, objShell, objFSO);
} else if (objFSO.GetExtensionName(tempArchivePath).toLowerCase() === "rar" || objFSO.GetExtensionName(tempArchivePath).toLowerCase() === "cbr") {
    ExtractRar(tempArchivePath, extractFolder, objShell, objFSO);
} else {
    WScript.Echo("Tipo de archivo no soportado. Usa un archivo .zip, .rar, .cbz, o .cbr.");
    return;
}

MoveFilesToTopLevel(extractFolder, objFSO);
RemoveEmptyDirs(extractFolder, objFSO);
CreateZip(extractFolder, cbzPath, objShell, objFSO);
WaitUntilFileIsReady(cbzPath, objFSO);

var originalSize = objFSO.GetFile(tempArchivePath).Size;
var resultSize = objFSO.GetFile(cbzPath).Size;

if (IsSizeAcceptable(originalSize, resultSize)) {
    // Mover el archivo resultante de vuelta a la ubicación original
    objFSO.MoveFile(cbzPath, originalPath);
} else {
    WScript.Echo("Error: El archivo resultante parece incompleto. No se eliminará el archivo original.");
}

// Eliminar archivos temporales
if (objFSO.FileExists(tempArchivePath)) {
    objFSO.DeleteFile(tempArchivePath);
}
if (objFSO.FolderExists(extractFolder)) {
    objFSO.DeleteFolder(extractFolder, true);
}
}

// Funciones auxiliares

function ExtractZip(zipPath, destFolder, objShell, objFSO) {
if (!objFSO.FolderExists(destFolder)) {
    objFSO.CreateFolder(destFolder);
}

var objZip = objShell.NameSpace(zipPath);
if (objZip) {
    objShell.NameSpace(destFolder).CopyHere(objZip.Items());
} else {
    WScript.Echo("No se pudo acceder al archivo ZIP: " + zipPath);
}
}

function ExtractRar(rarPath, destFolder, objShell, objFSO) {
var command = "unrar x -y \"" + rarPath + "\" \"" + destFolder + "\"";
objShell.ShellExecute("cmd.exe", "/c " + command, "", "open", 0);
 }

function MoveFilesToTopLevel(folderPath, objFSO) {
if (objFSO.FolderExists(folderPath)) {
    var subfolders = new Enumerator(objFSO.GetFolder(folderPath).SubFolders);
    
    for (; !subfolders.atEnd(); subfolders.moveNext()) {
        var subfolder = subfolders.item();
        var files = new Enumerator(subfolder.Files);
        
        for (; !files.atEnd(); files.moveNext()) {
            var file = files.item();
            file.Move(folderPath + "\\" + file.Name);
        }
    }
} else {
    WScript.Echo("El directorio no existe: " + folderPath);
}
}

function RemoveEmptyDirs(folderPath, objFSO) {
if (objFSO.FolderExists(folderPath)) {
    var subfolders = new Enumerator(objFSO.GetFolder(folderPath).SubFolders);
    
    for (; !subfolders.atEnd(); subfolders.moveNext()) {
        var subfolder = subfolders.item();
        RemoveEmptyDirs(subfolder.Path, objFSO);
        if (objFSO.GetFolder(subfolder.Path).Files.Count === 0 && objFSO.GetFolder(subfolder.Path).SubFolders.Count === 0) {
            objFSO.DeleteFolder(subfolder.Path);
        }
    }
}
}

function CreateZip(sourceFolder, zipPath, objShell, objFSO) {
var objZipFile, objSourceFolder;

if (!objFSO.FileExists(zipPath)) {
    objZipFile = objFSO.CreateTextFile(zipPath, true);
    objZipFile.Write("PK" + String.fromCharCode(5) + String.fromCharCode(6) + new Array(19).join(String.fromCharCode(0)));
    objZipFile.Close();
}

objZipFile = objShell.NameSpace(zipPath);
objSourceFolder = objShell.NameSpace(sourceFolder);

if (objZipFile && objSourceFolder) {
    objZipFile.CopyHere(objSourceFolder.Items());
} else {
    WScript.Echo("No se pudo crear el archivo ZIP: " + zipPath);
}

objZipFile = null;
objSourceFolder = null;
}

function WaitUntilFileIsReady(filePath, objFSO) {
var size1, size2;
size1 = objFSO.GetFile(filePath).Size;

do {
    size2 = objFSO.GetFile(filePath).Size;
    if (size1 === size2) {
        var startTime = new Date().getTime();
        while (new Date()..getTime() < startTime + 1000) {
        }
        break;
    } else {
        size1 = size2;
    }
} while (true);
}

function IsSizeAcceptable(originalSize, resultSize) {
var sizeDifference = Math.abs(originalSize - resultSize);
var tolerance = originalSize * 0.1; // Tolerancia del 10%

return sizeDifference <= tolerance;
}

I suppose this is due to the fact that DOPUS functions are very well optimized. :+1:

Finally I asked ChatGPT to help me to generate specifically a DOPUS script and to my surprise it generate something which may work but there are some issue with the variables.

@set archive={filepath$}
@set extract_dir={sourcepath$|noterm}\extracted
@set source_path={sourcepath$|noterm}

@confirm Paso 1: Extracción de archivos desde el archivo comprimido.\nArchivo: "{$archive}"\nDirectorio de extracción: "{$extract_dir}"\nRuta de origen: "{$source_path}".\n¿Deseas continuar?
Copy EXTRACT=sub TO="{$extract_dir}" HERE

@confirm Paso 2: Mover archivos al nivel superior.\nMover desde "{$extract_dir}" a "{$source_path}".\n¿Deseas continuar?
Copy "{$extract_dir}*" TO="{$source_path}" MOVE WHENEXISTS=rename

@confirm Paso 3: Eliminar el archivo comprimido original.\nArchivo a eliminar: "{$archive}".\n¿Deseas continuar?
Delete FILE="{$archive}" NORECYCLE QUIET

@confirm Paso 4: Eliminar el directorio temporal.\nDirectorio a eliminar: "{$extract_dir}".\n¿Deseas continuar?
Delete "{$extract_dir}" NORECYCLE QUIET

@confirm Paso 5: Re-comprimir archivos.\nRe-comprimir en "{$archive}".\n¿Deseas continuar?
Copy ZIP TO="{sourcepath$}" ARCHIVE=single HERE

When I run it I get this message
Error DOPUS script

This is why I suspect I am not using correctly the variables.
Sure I must be doing some "stupid error"
Thanks for the help.

@confirm will print any message verbatim. It won't parse any variables. You can work around it with the Evaluator:

=myVar="Yep"
=return "@confirm:Hi " + myVar 

You'd also need to write the rest of the button in this Evaluator style.

Alternatively, you could use

dopusrt.exe /argsmsgbox Hello World

to display the messages. This command will interpret variables.

Thank you @lxp.
That clarify the problem with @confirm function.
But anyway I don't understand how are the variables assigned.
Look this little test
image

as you can see the variable {filename} doesn't seam to be assigned.
{sourcepath} is ok.
don't understand the difference

The Evaluator uses a different set of variables. They look similar to the classic ones but usually don't mix well. Best to stick with one system.

@output can handle eval code, so this works:

=theVar=filepath
@output:{=theVar=}

Sorry. Don't understand this comment.
What is The Evaluator?
Where do I find the info to see "classic ones" and "the other"?

1 Like

Taking a step back: You mentioned having many archives to process, but your current button involves several confirmation dialogs. Won't that become a tedious task? Leo's suggestion is quite straightforward: gather all the relevant archives, unpack them, eliminate unwanted subfolders, create new archives, and then delete the old ones. This process could be done using the built-in functionality. This button could help in removing the subfolders:

Explode (deflate) selected folder

Thanks.
The confirmation dialogs are there just because I am trying to debug my problems.
I understand conceptually Leo's suggestion.
My problem is implementing it. :wink:
I am going to read the EVALUATOR manual to understand how it manage them.
I'll be back then. :+1: