diff options
Diffstat (limited to 'yazi/plugins/lin-decompress.yazi/main.lua')
| -rw-r--r-- | yazi/plugins/lin-decompress.yazi/main.lua | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/yazi/plugins/lin-decompress.yazi/main.lua b/yazi/plugins/lin-decompress.yazi/main.lua new file mode 100644 index 0000000..d240870 --- /dev/null +++ b/yazi/plugins/lin-decompress.yazi/main.lua @@ -0,0 +1,419 @@ +-- Debugger +local IS_DEBUG = false +local function dmsg(msg) + if IS_DEBUG then + ya.dbg(msg) + end +end +-- Sync Helpers +local function tabs_to_table(tabs) + local t = {} + for i = 1, #tabs do + table.insert(t, tabs[i]) + end + return t +end +-- Syncs +local get_configs = ya.sync(function(state) + return state.config +end) +local get_files = ya.sync(function(state, args) + local tabs, paths = {}, {} + if args.tabselect then + if args.tabselect == "all" then + tabs = tabs_to_table(cx.tabs) + elseif args.tabselect == "active" or args.tabselect == "current" then + table.insert(tabs, cx.active) + end + for _, tab in pairs(tabs) do + for _, url in pairs(tab.selected) do + table.insert(paths, tostring(url.path)) + end + end + end + if not args.no_hover then + table.insert(paths, tostring(cx.active.current.hovered.url.path)) + end + return paths +end) +-- Notify Users +local function alert(msg, opts) + opts = opts or {} + ya.notify({ + title = "lin-decompress", + content = msg, + timeout = opts.timeout or 10, + level = opts.level or "info", + }) +end +-- Create directory +local function create_dir(dir_path) + local is_successful, error = fs.create("dir_all", Url(dir_path)) + if not is_successful then + alert("Unable to create directory -> " .. dir_path, { level = "error" }) + ya.err(tostring(error)) + end + return is_successful +end +-- Ask User +local function ask(msg, default, opts) + opts = opts or {} + local width = opts.width or 40 + local y = opts.y or 10 + local should_fail = opts.should_fail and true or false + local should_hide = opts.obscure and true or false + local value, event = ya.input({ + pos = { "top-center", x = -width + 10, y = y, w = width }, + title = msg, + value = default, + obscure = should_hide, + realtime = false, + debounce = 0.3, + }) + dmsg("Asked: '" .. msg .. "'") + if should_fail or not value then + error("Error occurred when asking user: '" .. msg .. "'") + end + return value +end +-- Ask encryption credential +local function ask_cred() + return ask("Password?: (Stop asking[!!!])", "", { obscure = true }) +end +-- Ask User output directory +local function ask_output(path) + local url = Url(path) + local parent_dir = url.parent + local extract_dir = ask("Directory to extract ALL archives?", tostring(parent_dir)) + return extract_dir +end +-- Variables +local cmd_options = {} +local global_tar_cmd = {} +local tar_cmd = { + ["tar"] = { + tool_name = "tar", + cmd = { "--overwrite" }, + in_cmd = "-xf", + out_cmd = "-C", + sub_cmd = "-I", + }, +} +local archive_cmds = {} +-- Strip content from mime-type +local function strip_type(str) + local f_index, _ = string.find(str, ":") + local file_type = string.sub(str, f_index + 1 or 1) + file_type = string.gsub(file_type, "\n", "") + file_type = string.gsub(file_type, "%s", "") + return file_type +end +-- Identify filetype +local function get_type(path) + dmsg("Getting type for Path: " .. path) + local output, err = Command("file"):arg("--mime-type"):arg(path):output() + if err then + alert("Unable to get mimetype for -> " .. path, { level = "error" }) + ya.err(tostring(err)) + return nil + end + return strip_type(output.stdout) +end +-- Get tar archive commands +local function get_tar_cmds_by_mime(mimetype) + for archive_name, cmd in pairs(tar_cmd) do + if string.find(mimetype, archive_name) then + return { is_tar_type = true, cmds = cmd } + end + end + return {} +end +local function get_tar_cmds_by_ext(ext) + for _, cmd in pairs(tar_cmd) do + if cmd.exts[ext] then + return { is_tar_type = true, cmds = cmd } + end + end + return {} +end +-- Get other archive type commands +local function get_other_cmds_by_mime(mimetype) + for archive_name, cmd in pairs(archive_cmds) do + if string.find(mimetype, archive_name) then + return { cmds = cmd } + end + end + return { cmds = archive_cmds["default"] } +end +local function get_other_cmds_by_ext(ext) + for _, cmd in pairs(archive_cmds) do + if cmd.exts[ext] then + return { cmds = cmd } + end + end + return { cmds = archive_cmds["default"] } +end +-- Get commands for archive by extension +local function get_cmds_by_ext(ext) + local cmds = get_tar_cmds_by_ext(ext) + if cmds.is_tar_type then + return cmds + end + return get_other_cmds_by_ext(ext) +end +-- Get commands for archive by mimetype +local function get_cmds_by_mime(mimetype) + local cmds = get_tar_cmds_by_mime(mimetype) + if cmds.is_tar_type then + return cmds + end + return get_other_cmds_by_mime(mimetype) +end +-- Get commands for archive +local function get_cmds(mimetype, path) + if string.find(mimetype, "octet-stream") then + local ext = Url(path).ext + return get_cmds_by_ext(ext) + else + return get_cmds_by_mime(mimetype) + end +end +-- Is tool installed? +local function tool_exists(tool_name) + local s = Command("which"):arg(tool_name):status() + return s and s.code == 0 +end +-- Selects an archive extract tool +local function get_tool(cmds) + if tool_exists(cmds.tool_name) then + return cmds + end + cmds.is_tar_type = false + return archive_cmds["default"] +end +-- Concatenate two items to strings +local function str_concat(item1, item2) + local result = "" + for _, v in ipairs({ item1, item2 }) do + if type(v) == "table" then + result = string.format("%s %s", result, table.concat(v, " ")) + else + if result == "" then + result = v + else + result = result .. " " .. v + end + end + end + return string.gsub(result, "(%s%s)%s*", " ") +end +-- Add collection of arguments to a command +local function get_command_args(command, args) + if args then + for i, arg in ipairs(args) do + command = command:arg(arg) + end + end + return command +end +-- Remove extracted folder with the same name as parent. e.g. (foo/foo/) +local function remove_dup_dir(archive_dir) + local archive_url = Url(archive_dir) + local files, err = fs.read_dir(archive_url, { limit = 1 }) + if err then + alert(err, { level = "error" }) + return + end + local file = files and files[1] or nil + dmsg("Dup Suspect: " .. file.name) + dmsg("Dup: " .. file.name .. " and " .. archive_url.name) + if not file or not file.cha.is_dir or file.name ~= archive_url.name then + dmsg("Directory does not contain duplicate at -> " .. archive_dir) + return + end + local _, err2 = Command("sh"):arg("-c"):arg("mv " .. file.url.path .. "/* " .. archive_dir):output() + if err2 then + dmsg(err2) + dmsg("Failed to move duplicate directory") + return + end + fs.remove("dir", file.url) +end +-- Retrieve archive directory save location +local function get_archive_dir(inputs, path) + local url = Url(path) + local strip_tar_path = string.gsub(url.stem, "%.tar$", "", 1) + local output_dir = Url(inputs.output) + local archive_url = output_dir:join(strip_tar_path) + return tostring(archive_url) +end +-- Retrieve extracted file url +local function get_extracted_file_url(inputs, path) + local path_url = Url(path) + local output_url = Url(inputs.output) + local full_file_name = path_url.name or "file_name" + local file_name_wo_archive_ext = string.gsub(full_file_name, ".%w+$", "", 1) + return output_url:join(file_name_wo_archive_ext) +end +-- Basic extract command for tar +local function tar_command(path, tar_tool, archive_dir) + local command = Command(tar_tool.tool_name) + dmsg(tar_tool) + -- Tar extract commands + command = command:arg(tar_tool.in_cmd):arg(path) + -- Tar flag commands + command = get_command_args(command, tar_tool.cmd) + -- Tar argument commands + return command:arg(tar_tool.out_cmd):arg(archive_dir) +end +-- Extracts a .tar archive +local function tar_extract(path, tool_cmd, archive_dir) + local command = tar_command(path, tool_cmd, archive_dir) + local _, error = command:output() + if error then + alert("An error occurred extracting -> " .. path, { level = "error" }) + ya.err(tostring(error)) + fs.remove("dir_clean", Url(archive_dir)) + end +end +-- Extracts archives with tar.* +local function double_extract(path, tool_cmd, archive_dir) + -- Tar flag commands + local command = tar_command(path, tar_cmd["tar"], archive_dir) + dmsg(tool_cmd) + -- Sub compressor commands + local compressor_cmds = tool_cmd.tool_name + if tool_cmd.cmd then + compressor_cmds = str_concat(compressor_cmds, tool_cmd.cmd) .. " " + end + -- Sub compressor global commands + if not tool_cmd.no_global_tar then + compressor_cmds = str_concat(compressor_cmds, global_tar_cmd.cmd) + end + local _, error = command:arg(tar_cmd["tar"].sub_cmd):arg(compressor_cmds):output() + if error then + alert("An error occurred extracting -> " .. path, { level = "error" }) + ya.err(tostring(error)) + fs.remove("dir_clean", Url(archive_dir)) + end +end +-- Extracts single tar related files (.lz,.zstd,etc) +local function tar_like_extract(path, tool_cmd, file_url) + local command = Command(tool_cmd.tool_name) + dmsg(tool_cmd) + -- Compressor specific commands + command = get_command_args(command, tool_cmd.cmd) + -- Global compressor commands + if not tool_cmd.no_global_tar then + command = get_command_args(command, global_tar_cmd.cmd) + end + local output = command:arg(path):output() + dmsg("File URL -> " .. tostring(file_url)) + local is_successful, error = fs.write(file_url, output.stdout) + if not is_successful then + alert("Unable to extract file -> " .. tostring(file_url), { level = "error" }) + ya.err(tostring(error)) + fs.remove("dir_clean", file_url.parent) + end +end +-- Extracts other types of archives (zip,rar,7z,etc.) +local function other_extract(path, tool_cmd, archive_dir, inputs) + dmsg(tool_cmd) + local command = Command(tool_cmd.tool_name) + command = get_command_args(command, tool_cmd.cmd) + if tool_cmd.pw_cmd and inputs.cred ~= "!!!" and not cmd_options.no_password then + inputs.cred = ask_cred() + if inputs.cred and inputs.cred ~= "!!!" then + local format_pw = string.format("%s%s", tool_cmd.pw_cmd, inputs.cred) + command = command:arg(format_pw) + end + end + local is_successful = create_dir(archive_dir) + if not is_successful then + return + end + local format_out = string.format("%s%s", tool_cmd.out_cmd, archive_dir) + local _, error = command:arg(format_out):arg(path):stdout(Command.PIPED):output() + if error then + fs.remove("dir_clean", Url(archive_dir)) + alert("An error occurred extracting -> " .. path, { level = "error" }) + ya.err(tostring(error)) + end +end +-- Operate on a single file +local function decompress_file(path, mimetype, inputs) + local cmds = get_cmds(mimetype, path) + local tool_cmd = get_tool(cmds.cmds) + dmsg("Chosen tool -> " .. tool_cmd.tool_name) + local archive_dir = get_archive_dir(inputs, path) + dmsg("Archive Dir -> " .. archive_dir) + local skip_dup_removal = false + if cmds.is_tar_type then + if string.find(path, "%.tar%.", 1) then + create_dir(archive_dir) + dmsg("Extract type -> double") + double_extract(path, tool_cmd, archive_dir) + elseif string.find(path, "%.tar$", 1) then + create_dir(archive_dir) + dmsg("Extract type -> tar") + tar_extract(path, tool_cmd, archive_dir) + else + create_dir(inputs.output) + skip_dup_removal = true + dmsg("Extract type -> tar-like") + local file_url = get_extracted_file_url(inputs, path) + tar_like_extract(path, tool_cmd, file_url) + end + else + create_dir(archive_dir) + dmsg("Extract type -> other") + other_extract(path, tool_cmd, archive_dir, inputs) + end + if not skip_dup_removal then + remove_dup_dir(archive_dir) + end +end +-- Extract all archive files +local function decompress_files(paths) + local inputs = {} + alert("Attempting to extract " .. #paths .. " files", { timeout = 5 }) + for _, path in ipairs(paths) do + local mimetype = get_type(path) + dmsg(path .. ": " .. mimetype) + if mimetype and not string.find(mimetype, "directory") then + if not inputs.output then + inputs.output = ask_output(path) + dmsg("Ask Output directory -> " .. inputs.output) + end + decompress_file(path, mimetype, inputs) + end + end + alert("Extraction process completed!") +end +-- Move table 1 into table 2 +local function table_move(t1, t2) + for k, v in pairs(t1) do + t2[k] = v + end +end +-- Setup Configurations +local function setup_vars() + local configs = get_configs() + global_tar_cmd = configs.global_tar_compressor + table_move(configs.tar_compressors, tar_cmd) + archive_cmds = configs.other_compressors +end +-- Entry point +local M = {} +function M:setup(state) + self.config = state +end +function M:entry(job) + setup_vars() + cmd_options = job.args + local paths = get_files(cmd_options) + if #paths > 0 then + decompress_files(paths) + end +end +return M |
