From 5941f1226321f6d97d71dede67be351dd4faddba Mon Sep 17 00:00:00 2001 From: CogentRedTester Date: Sun, 30 Oct 2022 13:16:20 +1030 Subject: [PATCH] custom-keybinds: implement version checking custom-keybinds is now part of the addon API and shares a version number --- addons/addons.md | 11 +++++---- custom-keybinds.md | 25 ++++++++++++++----- file-browser.lua | 60 ++++++++++++++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 30 deletions(-) diff --git a/addons/addons.md b/addons/addons.md index 2860b89..20ae44e 100644 --- a/addons/addons.md +++ b/addons/addons.md @@ -1,4 +1,4 @@ -# How to Write an Addon - API v1.4.0 +# How to Write an Addon - API v1.5.0 Addons provide ways for file-browser to parse non-native directory structures. This document describes how one can create their own custom addon. @@ -23,10 +23,13 @@ not be loaded. A minor version number denotes a change to the API that is backwards compatible. This includes additional API functions, or extra fields in tables that were previously unused. It may also include additional arguments to existing functions that add additional behaviour without changing the old behaviour. -If the parser's minor version number is greater than the API_VERSION, then a warning is printed to the console. +If the parser's minor version number is greater than the API_VERSION, then an error message will be printed and the parser will +not be loaded. -Patch numbers denote bug fixes, and are ignored when loading an addon. -For this reason addon authors are allowed to leave the patch number out of their version tag and just use `MAJOR.MINOR`. +Patch numbers denote bug fixes, and are mostly ignored when loading an addon. +However if the addon has a patch number greater than the patch number file-browser uses then a warning will +be printed just in case the addon uses a bug that has been fixed in the patches. +Addon authors are allowed to leave the patch number out of their version tag and just use `MAJOR.MINOR`. ## Overview diff --git a/custom-keybinds.md b/custom-keybinds.md index 7094781..9c834c5 100644 --- a/custom-keybinds.md +++ b/custom-keybinds.md @@ -1,8 +1,10 @@ # Custom Keybinds -File-browser also supports custom keybinds. These keybinds send normal input commands, but the script will substitute characters in the command strings for specific values depending on the currently open directory, and currently selected item. +File-browser supports custom keybinds. These keybinds send normal input commands, but the script will substitute characters in the command strings for specific values depending on the currently open directory, and currently selected item. This allows for a wide range of customised behaviour, such as loading additional audio tracks from the browser, or copying the path of the selected item to the clipboard. +The custom-keybind API shares a version number with the [addon API](addons/addons.md#api-version). + The feature is disabled by default, but is enabled with the `custom_keybinds` script-opt. Keybinds are declared in the `~~/script-opts/file-browser-keybinds.json` file, the config takes the form of an array of json objects, with the following keys: @@ -11,6 +13,7 @@ Keybinds are declared in the `~~/script-opts/file-browser-keybinds.json` file, t | key | yes | - | the key to bind the command to - same syntax as input.conf | | command | yes | - | json array of commands and arguments | | name | no | numeric id | name of the script-binding - see [modifying default keybinds](#modifying-default-keybinds) | +| version | no | `1.0.0` | the minimum API version required for the keybind - will warn the user if they are using an invalid keybind for their file-browser version | | condition | no | - | a Lua [expression](#expressions) - the keybind will only run if this evaluates to true | | flags | no | - | flags to send to the mpv add_keybind function - see [here](https://mpv.io/manual/master/#lua-scripting-[,flags]]\)) | | filter | no | - | run the command on just a file (`file`) or folder (`dir`) | @@ -55,7 +58,8 @@ You can set the filter to match multiple parsers by separating the names with sp { "key": "KP2", "command": [ ["print-text", "example3"] ], - "parser": "ftp file" + "parser": "ftp file", + "version": "1.5.0" } ``` @@ -184,12 +188,14 @@ the selected item is a matroska file: { "key": "KP1", "command": ["print-text", "in my C:/ drive!"], - "condition": "(%P):find('C:/') == 1" + "condition": "(%P):find('C:/') == 1", + "version": "1.5.0" }, { "key": "KP2", "command": ["print-text", "Matroska File!"], - "condition": "fb.get_extension(%N) == 'mkv'" + "condition": "fb.get_extension(%N) == 'mkv'", + "version": "1.5.0" } ] ``` @@ -216,7 +222,8 @@ Any `=>` string will be substituted for `script-message`. { "key": "KP1", "command": ["script-message", "=>", "delay-command", "%j * 2", "=>", "evaluate-expressions", "print-text", "!{%j * 2}"], - "multiselect": true + "multiselect": true, + "version": "1.5.0" } ``` @@ -230,6 +237,7 @@ This example command will only run if the player is currently paused: { "key": "KP1", "command": ["script-message", "conditional-command", "mp.get_property_bool('pause')", "print-text", "is paused"], + "version": "1.5.0" } ``` @@ -241,6 +249,7 @@ This example only runs if the currently selected item in the browser has a `.mkv { "key": "KP1", "command": ["script-message", "conditional-command", "fb.get_extension(%N) == 'mkv'", "print-text", "a matroska file"], + "version": "1.5.0" } ``` @@ -255,6 +264,7 @@ The following example will send the `print-text` command after 5 seconds: { "key": "KP1", "command": ["script-message", "delay-command", "5", "print-text", "example"], + "version": "1.5.0" } ``` @@ -272,6 +282,7 @@ For example the following keybind will print 3 to the console: { "key": "KP1", "command": ["script-message", "evaluate-expressions", "print-text", "!{1 + 2}"], + "version": "1.5.0" } ``` @@ -282,6 +293,7 @@ This example replaces all `/` characters in the path with `\` { "key": "KP1", "command": ["script-message", "evaluate-expressions", "print-text", "!{ string.gsub(%F, '/', '\\\\') }"], + "version": "1.5.0" } ``` @@ -316,7 +328,8 @@ rename items in file-browser: "fb.rescan()" ], "parser": "file", - "multiselect": true + "multiselect": true, + "version": "1.5.0" } ``` diff --git a/file-browser.lua b/file-browser.lua index 3d2948a..3216b6f 100644 --- a/file-browser.lua +++ b/file-browser.lua @@ -138,7 +138,7 @@ if not success then input = nil end -------------------------------------------------------------------------------------------------------- --sets the version for the file-browser API -API_VERSION = "1.4.0" +API_VERSION = "1.5.0" --switch the main script to a different environment so that the --executed lua code cannot access our global variales @@ -295,6 +295,29 @@ local ABORT_ERROR = { msg = "browser is no longer waiting for list - aborting parse" } +local API_MAJOR, API_MINOR, API_PATCH = API_VERSION:match("(%d+)%.(%d+)%.(%d+)") + +--checks if the given table has a valid version number +function API.check_version(version) + version = version or "1.0.0" + + local major, minor = version:match("(%d+)%.(%d+)") + local patch = version:match("%d+%.%d+%.(%d+)") + + if not major or not minor then + return false, ("Invalid version number: %s"):format(version) + elseif major ~= API_MAJOR then + return false, ("wrong major version number - expected v%d.x.x got v%s"):format(API_MAJOR, version) + -- return msg.error("parser", parser.name, "has wrong major version number, expected", ("v%d.x.x"):format(API_MAJOR), "got", 'v'..version) + elseif minor > API_MINOR then + return false, ("wrong minor version number - expected v%d.0.x - v%d.%d.x got v%s"):format(API_MAJOR, API_MAJOR, API_MINOR, version) + -- msg.warn("parser", parser.name, "has newer minor version number than API, expected", ("v%d.%d.x"):format(API_MAJOR, API_MINOR), "got", 'v'..version) + elseif patch and patch > API_PATCH then + return true, ("wrong patch number - expected v%d.%d.0 - v%d.%d.%d got v%s"):format(API_MAJOR, API_MINOR, API_MAJOR, API_MINOR, API_PATCH, version) + end + return true +end + --implements table.pack if on lua 5.1 if not table.pack then table.unpack = unpack @@ -1728,6 +1751,15 @@ end --inserting the custom keybind into the keybind array for declaration when file-browser is opened --custom keybinds with matching names will overwrite eachother local function insert_custom_keybind(keybind) + local success, err = API.check_version(keybind.version) + if not success then + msg.warn(utils.to_string(keybind)) + msg.error(('failed to load keybind %s: %s'):format(keybind.name, err)) + return + elseif err then + msg.warn(('%s\nwarning loading keybind %s: %s'):format(utils.to_string(keybind), keybind.name, err)) + end + --we'll always save the keybinds as either an array of command arrays or a function if type(keybind.command) == "table" and type(keybind.command[1]) ~= "table" then keybind.command = {keybind.command} @@ -1945,24 +1977,6 @@ end -------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------- -local API_MAJOR, API_MINOR, API_PATCH = API_VERSION:match("(%d+)%.(%d+)%.(%d+)") - ---checks if the given parser has a valid version number -local function check_api_version(parser) - local version = parser.version or "1.0.0" - - local major, minor = version:match("(%d+)%.(%d+)") - - if not major or not minor then - return msg.error("Invalid version number") - elseif major ~= API_MAJOR then - return msg.error("parser", parser.name, "has wrong major version number, expected", ("v%d.x.x"):format(API_MAJOR), "got", 'v'..version) - elseif minor > API_MINOR then - msg.warn("parser", parser.name, "has newer minor version number than API, expected", ("v%d.%d.x"):format(API_MAJOR, API_MINOR), "got", 'v'..version) - end - return true -end - --create a unique id for the given parser local function set_parser_id(parser) local name = parser.name @@ -2027,7 +2041,13 @@ local function setup_parser(parser, file) parser.name = parser.name or file:gsub("%-browser%.lua$", ""):gsub("%.lua$", "") set_parser_id(parser) - if not check_api_version(parser) then return msg.error("aborting load of parser", parser:get_id(), "from", file) end + local valid_version, err = API.check_version(parser.version) + if not valid_version then + msg.error(parser:get_id()..':', err) + return msg.error("aborting load of parser", parser:get_id(), "from", file) + elseif err then + msg.warn(("warning loading parser %s: %s"):format(parser:get_id(), err)) + end msg.verbose("imported parser", parser:get_id(), "from", file)