Creating "extract here" button when inside archive

When I have a zip or other archive my typical workflow is to extract it into a self-named folder.

Often I'll download the archive, open it from my browser, opens in a lister tab. Then I have to go up one level, right click the archive, "Directory opus archives", "Extract to ".

I would really love to shorten this. Ideally a button that I can press while inside the zip which self extracts and then navigates to the resulting folder.

I searched previous posts and found buttons I can use when the zip is highlighted but that will still require navigating back up when I'm inside the zip.

I'm working off of one of Leo's scripts. My current challenge is figuring out whether I'm inside a ZIP for the script.

function OnClick(clickData)
{
	var cmd = clickData.func.command;
	cmd.AddLine("Copy EXTRACT=sub HERE");

	if (clickData.func.sourcetab.selected_files.count > 0)
	{
		var realPath = clickData.func.sourcetab.selected_files(0).realpath;
		var extractPath = DOpus.FSUtil.NewPath(realPath);
		if (extractPath.Parent())
		{
			extractPath.Add(realPath.stem_m);

			cmd.AddLine("Go \"" + extractPath + "\"");
		}
	}

	cmd.Run();
}

FSUtil has a GetType helper function that will report if a path is a file or dir (or invalid).

If you specify "a" for the optional flags argument, it will return file for archives.

Since a normal file can't be a parent of the current directory, you could detect if you are inside an archive by looping through the parent paths until you reach a root or one which is a file.

As a bonus, that will also get you the archive's path, not just tell you that you are somewhere inside an archive. (You might be in a sub-folder of an archive, for example. The parent will be a dir but the parent of that will be a file.)

Unless you only want to extract the currently selected files/folders, you'd also want to do a cmd.ClearFiles(); and then cmd.AddFile(<archive path>) so that the command extracts the whole archive. (You'd need to do that after working out the archive path, of course.)

I'd also recommend removing the HERE argument and using TO="<parent of archive path>" to specify where you want the extraction to go. Otherwise, it's going to try to extract the files into the archive itself (since that's the current directory).

1 Like

You can get the archive's path by trimming off the nasty bits with:

var archivePath = realPath.replace(/(.*\.)(zip|7z|rar|iso)\\?.*/i, '$1$2');

BTW: Using clickData.func.sourcetab.path instead of clickData.func.sourcetab.selected_files(0).realpath will work more reliably.

1 Like

Thanks for your help! I'm quite close to getting it but having trouble getting the COPY EXTRACT job to run properly.

function OnClick(clickData)
{
	var cmd = clickData.func.command;
	var listerPath = clickData.func.sourcetab.path;
	if (DOpus.FSUtil.GetType(listerPath, "a") == "file")
	{
		DOpus.Output("In archive");
		cmd.ClearFiles();
		cmd.AddFile(listerPath);
		var parentPath = DOpus.FSUtil.NewPath(listerPath);
		parentPath.Parent();
		DOpus.Output("Extracting to " + parentPath);
		for (var f = new Enumerator(cmd.files); !f.atEnd(); f.moveNext()) {
			DOpus.Output("File: " + f.item());
		}
		cmd.AddLine("COPY EXTRACT=sub TO=\"" + parentPath + "\"");
		cmd.Run();
	}
}

When I run this I get:
An error occured copying 'Source_Code_Pro.zip'. The system cannot find the path specified.

Debug output:

In archive
Extracting to C:\Users\Ben\Downloads
File: C:\Users\Ben\Downloads\Source_Code_Pro.zip

I'm not sure how to debug this further. I suspect maybe a type problem? VScript is a strange beast.

Try

function OnClick(clickData) {
    var cmd = clickData.func.command;
    var tab = clickData.func.sourcetab;
    var fsu = DOpus.FSUtil();
    cmd.deselect = false;

    var archivePath = String(tab.path).replace(/(.*\.)(zip|7z|rar|iso)\\?.*/i, '$1$2');
    var targetPath = fsu.NewPath(archivePath).Parent();
    cmd.RunCommand('COPY EXTRACT=sub FILE="' + archivePath + '" TO="' + targetPath + '"');
}
Button as XML
<?xml version="1.0"?>
<button backcol="none" display="both" label_pos="right" textcol="none">
	<label>37666</label>
	<icon1>#newcommand</icon1>
	<function type="script">
		<instruction>@script JScript</instruction>
		<instruction>function OnClick(clickData) {</instruction>
		<instruction>    var cmd = clickData.func.command;</instruction>
		<instruction>    var tab = clickData.func.sourcetab;</instruction>
		<instruction>    var fsu = DOpus.FSUtil();</instruction>
		<instruction>    cmd.deselect = false;</instruction>
		<instruction />
		<instruction>    var archivePath = String(tab.path).replace(/(.*\.)(zip|7z|rar|iso)\\?.*/i, &apos;$1$2&apos;);</instruction>
		<instruction>    var targetPath = fsu.NewPath(archivePath).Parent();</instruction>
		<instruction>    cmd.RunCommand(&apos;COPY EXTRACT=sub FILE=&quot;&apos; + archivePath + &apos;&quot; TO=&quot;&apos; + targetPath + &apos;&quot;&apos;);</instruction>
		<instruction>}</instruction>
	</function>
</button>

How to

How to use buttons and scripts from this forum

1 Like

Thanks! FILE= was the key piece! I prefer not to do string manipulation to archive path so it will support all archives Opus supports.
Here's the final VScript that works whether you have an archive selected or are inside of one:

function OnClick(clickData)
{
	var cmd = clickData.func.command;
	var st = clickData.func.sourcetab;
	var listerPath = st.path;
	var parentPath = DOpus.FSUtil.NewPath(listerPath);
	var createdFolder = null;
	if (DOpus.FSUtil.GetType(listerPath, "a") == "file") {
                // within archive
		parentPath.Parent();
		createdFolder = listerPath.stem_m;
		cmd.AddLine('COPY EXTRACT=sub FILE="' + listerPath + '" TO="' + parentPath + '"');
	}
	else {
                // Archive selected.
		createdFolder = st.selected_files(0).realpath.stem_m
		cmd.AddLine('COPY EXTRACT=sub HERE');
	}
	parentPath.add(createdFolder);
	cmd.AddLine('GO "' + parentPath + '"');
	cmd.Run();
}

Thanks again for the help!

1 Like

When you're within the archive, you might be more than one level deep in it, which you'd need a loop to deal with (testing each parent level until you find an archive, or run out of levels).

Another advantage is the script won't be confused if there's a folder with an archive extension in its name. Rare, but it does happen sometimes.