diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-10-06 13:45:27 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-10-06 13:45:27 +0900 |
| commit | b40d1b8c322ce927fdbcb0df1b56ebaf1210cd17 (patch) | |
| tree | f7928ee635237dfe853c32b2cd22bc04b09c07ed /mac/.config/yazi/plugins/compress.yazi/main.lua | |
| parent | eeb6aff35decf8d4978c3403aeb24ab109877e96 (diff) | |
modified yazi/keymap-default.toml, modified yazi/keymap-default.toml, created compress.yazi/, created compress.yazi/
Diffstat (limited to 'mac/.config/yazi/plugins/compress.yazi/main.lua')
| -rw-r--r-- | mac/.config/yazi/plugins/compress.yazi/main.lua | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/mac/.config/yazi/plugins/compress.yazi/main.lua b/mac/.config/yazi/plugins/compress.yazi/main.lua new file mode 100644 index 0000000..b695b3f --- /dev/null +++ b/mac/.config/yazi/plugins/compress.yazi/main.lua @@ -0,0 +1,496 @@ +-- Check for windows +local is_windows = ya.target_family() == "windows" +-- Define flags and strings +local is_password, is_encrypted, is_level, cmd_password, cmd_level, default_extension = false, false, false, "", "", "zip" + +-- Function to check valid filename +local function is_valid_filename(name) + -- Trim whitespace from both ends + name = name:match("^%s*(.-)%s*$") + if name == "" then + return false + end + if is_windows then + -- Windows forbidden chars and reserved names + if name:find('[<>:"/\\|%?%*]') then + return false + end + else + -- Unix forbidden chars + if name:find("/") or name:find("%z") then + return false + end + end + return true +end + +-- Function to send notifications +local function notify_error(message, urgency) + ya.notify( + { + title = "Archive", + content = message, + level = urgency, + timeout = 5 + } + ) +end + +-- Function to check if command is available +local function is_command_available(cmd) + local stat_cmd + if is_windows then + stat_cmd = string.format("where %s > nul 2>&1", cmd) + else + stat_cmd = string.format("command -v %s >/dev/null 2>&1", cmd) + end + local cmd_exists = os.execute(stat_cmd) + if cmd_exists then + return true + else + return false + end +end + +-- Function to change command arrays --> string -- Use first command available or first command +local function find_command_name(cmd_list) + for _, cmd in ipairs(cmd_list) do + if is_command_available(cmd) then + return cmd + end + end + return cmd_list[1] -- Return first command as fallback +end + +-- Function to append filename to it's parent directory url +local function combine_url(path, file) + path, file = Url(path), Url(file) + return tostring(path:join(file)) +end + +-- Function to make a table of selected or hovered files: path = filenames +local selected_or_hovered = + ya.sync( + function() + local tab, paths, names, path_fnames = cx.active, {}, {}, {} + for _, u in pairs(tab.selected) do + paths[#paths + 1] = tostring(u.parent) + names[#names + 1] = tostring(u.name) + end + if #paths == 0 and tab.current.hovered then + paths[1] = tostring(tab.current.hovered.url.parent) + names[1] = tostring(tab.current.hovered.name) + end + for idx, name in ipairs(names) do + if not path_fnames[paths[idx]] then + path_fnames[paths[idx]] = {} + end + table.insert(path_fnames[paths[idx]], name) + end + return path_fnames, names, tostring(tab.current.cwd) + end +) + +-- Table of archive commands +local archive_commands = { + ["%.zip$"] = { + {command = "zip", args = {"-r"}, level_arg = "-", level_min = 0, level_max = 9, passwordable = true}, + { + command = {"7z", "7zz", "7za"}, + args = {"a", "-tzip"}, + level_arg = "-mx=", + level_min = 0, + level_max = 9, + passwordable = true + }, + { + command = {"tar", "bsdtar"}, + args = {"-caf"}, + level_arg = {"--option", "compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.7z$"] = { + { + command = {"7z", "7zz", "7za"}, + args = {"a"}, + level_arg = "-mx=", + level_min = 0, + level_max = 9, + header_arg = "-mhe=on", + passwordable = true + } + }, + ["%.rar$"] = { + { + command = "rar", + args = {"a"}, + level_arg = "-m", + level_min = 0, + level_max = 5, + header_arg = "-hp", + passwordable = true + } + }, + ["%.tar.gz$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "gzip"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-tgzip"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-czf"}, + level_arg = {"--option", "gzip:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.xz$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "xz"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-txz"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-cJf"}, + level_arg = {"--option", "xz:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.bz2$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "bzip2"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-tbzip2"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-cjf"}, + level_arg = {"--option", "bzip2:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.zst$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-", + level_min = 1, + level_max = 22, + compress = "zstd", + compress_args = {"--ultra"} + } + }, + ["%.tar.lz4$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-", + level_min = 1, + level_max = 12, + compress = "lz4" + } + }, + ["%.tar.lha$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-o", + level_min = 5, + level_max = 7, + compress = "lha", + compress_args = {"-a"} + } + }, + ["%.tar$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}} + } +} + +return { + entry = function(_, job) + -- Parse flags and default extension + if job.args ~= nil then + for _, arg in ipairs(job.args) do + if arg:match("^%-(%w+)$") then + -- Handle combined flags (e.g., -phl) + for flag in arg:sub(2):gmatch(".") do + if flag == "p" then + is_password = true + elseif flag == "h" then + is_encrypted = true + elseif flag == "l" then + is_level = true + end + end + elseif arg:match("^%w[%w\\.]*$") then + -- Handle default extension (e.g., 7z, zip) + if archive_commands["%." .. arg .. "$"] then + default_extension = arg + else + notify_error(string.format("Unsupported extension: %s", arg), "warn") + end + else + notify_error(string.format("Unknown argument: %s", arg), "warn") + end + end + end + + -- Exit visual mode + ya.emit("escape", {visual = true}) + -- Define file table and output_dir (pwd) + local path_fnames, fnames, output_dir = selected_or_hovered() + -- Get archive filename + local output_name, event = + ya.input( + { + title = "Create archive:", + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + + -- Determine the default name for the archive + local default_name = #fnames == 1 and fnames[1] or Url(output_dir).name + output_name = output_name == "" and string.format("%s.%s", default_name, default_extension) or output_name + + -- Add default extension if none is specified + if not output_name:match("%.%w+$") then + output_name = string.format("%s.%s", output_name, default_extension) + end + + -- Validate the final archive filename + if not is_valid_filename(output_name) then + notify_error("Invalid archive filename", "error") + return + end + + -- Match user input to archive command + local archive_cmd, + archive_args, + archive_compress, + archive_level_arg, + archive_level_min, + archive_level_max, + archive_header_arg, + archive_passwordable, + archive_compress_args + local matched_pattern = false + for pattern, cmd_list in pairs(archive_commands) do + if output_name:match(pattern) then + matched_pattern = true -- Mark that file extension is correct + for _, cmd in ipairs(cmd_list) do + -- Check if archive_cmd is available + local find_command = type(cmd.command) == "table" and find_command_name(cmd.command) or cmd.command + if is_command_available(find_command) then + -- Check if compress_cmd (if listed) is available + if cmd.compress == nil or is_command_available(cmd.compress) then + archive_cmd = find_command + archive_args = cmd.args + archive_compress = cmd.compress or "" + archive_level_arg = is_level and cmd.level_arg or "" + archive_level_min = cmd.level_min + archive_level_max = cmd.level_max + archive_header_arg = is_encrypted and cmd.header_arg or "" + archive_passwordable = cmd.passwordable or false + archive_compress_args = cmd.compress_args or {} + break + end + end + end + if archive_cmd then + break + end + end + end + + -- Check if no archive command is available for the extension + if not matched_pattern then + notify_error("Unsupported file extension", "error") + return + end + + -- Check if no suitable archive program was found + if not archive_cmd then + notify_error("Could not find a suitable archive program for the selected file extension", "error") + return + end + + -- Check if archive command has multiple names + if type(archive_cmd) == "table" then + archive_cmd = find_command_name(archive_cmd) + end + + -- Exit if archive command is not available + if not is_command_available(archive_cmd) then + notify_error(string.format("%s not available", archive_cmd), "error") + return + end + + -- Exit if compress command is not available + if archive_compress ~= "" and not is_command_available(archive_compress) then + notify_error(string.format("%s compression not available", archive_compress), "error") + return + end + + -- Add password arg if selected + if archive_passwordable and is_password then + local output_password, event = + ya.input( + { + title = "Enter password:", + obscure = true, + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + if output_password ~= "" then + cmd_password = "-P" .. output_password + if archive_cmd == "rar" and is_encrypted then + cmd_password = archive_header_arg .. output_password -- Add archive arg for rar + end + table.insert(archive_args, cmd_password) + end + end + + -- Add header arg if selected for 7z + if is_encrypted and archive_header_arg ~= "" and archive_cmd ~= "rar" then + table.insert(archive_args, archive_header_arg) + end + + -- Add level arg if selected + if archive_level_arg ~= "" and is_level then + local output_level, event = + ya.input( + { + title = string.format("Enter compression level (%s - %s)", archive_level_min, archive_level_max), + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + -- Validate user input for compression level + if + output_level ~= "" and tonumber(output_level) ~= nil and tonumber(output_level) >= archive_level_min and + tonumber(output_level) <= archive_level_max + then + cmd_level = + type(archive_level_arg) == "table" and archive_level_arg[#archive_level_arg] .. output_level or + archive_level_arg .. output_level + local target_args = archive_compress == "" and archive_args or archive_compress_args + if type(archive_level_arg) == "table" then + -- Insert each element of archive_level_arg (except last) into target_args at the correct position + for i = 1, #archive_level_arg - 1 do + table.insert(target_args, i, archive_level_arg[i]) + end + table.insert(target_args, #archive_level_arg, cmd_level) -- Add level at the end + else + -- Insert the compression level argument at the start if not a table + table.insert(target_args, 1, cmd_level) + end + else + notify_error("Invalid level specified. Using defaults.", "warn") + end + end + + -- Store the original output name for later use + local original_name = output_name + + -- If compression is needed, adjust the output name to exclude extensions like ".tar" + if archive_compress ~= "" then + output_name = output_name:match("(.*%.tar)") or output_name + end + + -- Create a temporary directory for intermediate files + local temp_dir_name = ".tmp_compress" + local temp_dir = combine_url(output_dir, temp_dir_name) + local temp_dir, _ = tostring(fs.unique_name(Url(temp_dir))) + + -- Attempt to create the temporary directory + local temp_dir_status, temp_dir_err = fs.create("dir_all", Url(temp_dir)) + if not temp_dir_status then + -- Notify the user if the temporary directory creation fails + notify_error(string.format("Failed to create temp directory, error code: %s", temp_dir_err), "error") + return + end + + -- Define the temporary output file path within the temporary directory + local temp_output_url = combine_url(temp_dir, output_name) + + -- Add files to the output archive + for filepath, filenames in pairs(path_fnames) do + -- Execute the archive command for each path and its respective files + local archive_status, archive_err = + Command(archive_cmd):arg(archive_args):arg(temp_output_url):arg(filenames):cwd(filepath):spawn():wait() + if not archive_status or not archive_status.success then + -- Notify the user if the archiving process fails and clean up the temporary directory + notify_error(string.format("Failed to create archive %s with '%s', error: %s", output_name, archive_cmd, archive_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + end + + -- If compression is required, execute the compression command + if archive_compress ~= "" then + local compress_status, compress_err = + Command(archive_compress):arg(archive_compress_args):arg(temp_output_url):spawn():wait() + if not compress_status or not compress_status.success then + -- Notify the user if the compression process fails and clean up the temporary directory + notify_error(string.format("Failed to compress archive %s with '%s', error: %s", output_name, archive_compress, compress_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + end + + -- Move the final file from the temporary directory to the output directory + local final_output_url, temp_url_processed = combine_url(output_dir, original_name), combine_url(temp_dir, original_name) + final_output_url, _ = tostring(fs.unique_name(Url(final_output_url))) + local move_status, move_err = os.rename(temp_url_processed, final_output_url) + if not move_status then + -- Notify the user if the move operation fails and clean up the temporary directory + notify_error(string.format("Failed to move %s to %s, error: %s", temp_url_processed, final_output_url, move_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + + -- Cleanup the temporary directory after successful operation + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + end +} |
