diff options
Diffstat (limited to 'mpv/scripts/file-browser/modules/apis')
| -rw-r--r-- | mpv/scripts/file-browser/modules/apis/fb.lua | 198 | ||||
| -rw-r--r-- | mpv/scripts/file-browser/modules/apis/parse-state.lua | 34 | ||||
| -rw-r--r-- | mpv/scripts/file-browser/modules/apis/parser.lua | 40 |
3 files changed, 272 insertions, 0 deletions
diff --git a/mpv/scripts/file-browser/modules/apis/fb.lua b/mpv/scripts/file-browser/modules/apis/fb.lua new file mode 100644 index 0000000..a7f5b35 --- /dev/null +++ b/mpv/scripts/file-browser/modules/apis/fb.lua @@ -0,0 +1,198 @@ +local mp = require 'mp' +local msg = require 'mp.msg' +local utils = require 'mp.utils' + +local o = require 'modules.options' +local g = require 'modules.globals' +local fb_utils = require 'modules.utils' +local ass = require 'modules.ass' +local directory_movement = require 'modules.navigation.directory-movement' +local scanning = require 'modules.navigation.scanning' +local controls = require 'modules.controls' + +---@class FbAPI: fb_utils +local fb = setmetatable({}, { __index = setmetatable({}, { __index = fb_utils }) }) +package.loaded["file-browser"] = setmetatable({}, { __index = fb }) + +--these functions we'll provide as-is +fb.redraw = ass.update_ass +fb.browse_directory = controls.browse_directory + +---Clears the directory cache. +---@return thread +function fb.rescan() + return scanning.rescan() +end + +---@async +---@return thread +function fb.rescan_await() + local co = scanning.rescan(nil, fb_utils.coroutine.callback()) + coroutine.yield() + return co +end + +---@param directories? string[] +function fb.clear_cache(directories) + if directories then + mp.commandv('script-message-to', mp.get_script_name(), 'cache/clear', utils.format_json(directories)) + else + mp.commandv('script-message-to', mp.get_script_name(), 'cache/clear') + end +end + +---A wrapper around scan_directory for addon API. +---@async +---@param directory string +---@param parse_state ParseStateTemplate +---@return Item[]|nil +---@return Opts +function fb.parse_directory(directory, parse_state) + if not parse_state then parse_state = { source = "addon" } + elseif not parse_state.source then parse_state.source = "addon" end + return scanning.scan_directory(directory, parse_state) +end + +---Register file extensions which can be opened by the browser. +---@param ext string +function fb.register_parseable_extension(ext) + g.parseable_extensions[string.lower(ext)] = true +end + +---Deregister file extensions which can be opened by the browser. +---@param ext string +function fb.remove_parseable_extension(ext) + g.parseable_extensions[string.lower(ext)] = nil +end + +---Add a compatible extension to show through the filter, only applies if run during the setup() method. +---@param ext string +function fb.add_default_extension(ext) + table.insert(g.compatible_file_extensions, ext) +end + +---Add item to root at position pos. +---@param item Item +---@param pos? number +function fb.insert_root_item(item, pos) + msg.debug("adding item to root", item.label or item.name, pos) + item.ass = item.ass or fb.ass_escape(item.label or item.name) + item.type = "dir" + table.insert(g.root, pos or (#g.root + 1), item) +end + +---Add a new mapping to the given directory. +---@param directory string +---@param mapping string +---@param pattern? boolean +---@return string +function fb.register_directory_mapping(directory, mapping, pattern) + if not pattern then mapping = '^'..fb_utils.pattern_escape(mapping) end + g.directory_mappings[mapping] = directory + msg.verbose('registering directory alias', mapping, directory) + + directory_movement.set_current_file(g.current_file.original_path) + return mapping +end + +---Remove all directory mappings that map to the given directory. +---@param directory string +---@return string[] +function fb.remove_all_mappings(directory) + local removed = {} + for mapping, target in pairs(g.directory_mappings) do + if target == directory then + g.directory_mappings[mapping] = nil + table.insert(removed, mapping) + end + end + return removed +end + +---A newer API for adding items to the root. +---Only adds the item if the same item does not already exist in the root. +---@param item Item|string +---@param priority? number Specifies the insertion location, a lower priority +--- is placed higher in the list and the default is 100. +---@return boolean +function fb.register_root_item(item, priority) + msg.verbose('registering root item:', utils.to_string(item)) + if type(item) == 'string' then + item = {name = item, type = 'dir'} + end + + -- if the item is already in the list then do nothing + if fb.list.some(g.root, function(r) + return fb.get_full_path(r, '') == fb.get_full_path(item, '') + end) then return false end + + ---@type table<Item,number> + local priorities = {} + + priorities[item] = priority + for i, v in ipairs(g.root) do + if (priorities[v] or 100) > (priority or 100) then + fb.insert_root_item(item, i) + return true + end + end + fb.insert_root_item(item) + return true +end + +--providing getter and setter functions so that addons can't modify things directly + + +---@param key string +---@return boolean|string|number +function fb.get_opt(key) return o[key] end + +function fb.get_script_opts() return fb.copy_table(o) end +function fb.get_platform() return g.PLATFORM end +function fb.get_extensions() return fb.copy_table(g.extensions) end +function fb.get_sub_extensions() return fb.copy_table(g.sub_extensions) end +function fb.get_audio_extensions() return fb.copy_table(g.audio_extensions) end +function fb.get_parseable_extensions() return fb.copy_table(g.parseable_extensions) end +function fb.get_state() return fb.copy_table(g.state) end +function fb.get_parsers() return fb.copy_table(g.parsers) end +function fb.get_root() return fb.copy_table(g.root) end +function fb.get_directory() return g.state.directory end +function fb.get_list() return fb.copy_table(g.state.list) end +function fb.get_current_file() return fb.copy_table(g.current_file) end +function fb.get_current_parser() return g.state.parser:get_id() end +function fb.get_current_parser_keyname() return g.state.parser.keybind_name or g.state.parser.name end +function fb.get_selected_index() return g.state.selected end +function fb.get_selected_item() return fb.copy_table(g.state.list[g.state.selected]) end +function fb.get_open_status() return not g.state.hidden end +function fb.get_parse_state(co) return g.parse_states[co or coroutine.running() or ""] end +function fb.get_history() return fb.copy_table(g.history.list) end +function fb.get_history_index() return g.history.position end + +---@deprecated +---@return string|nil +function fb.get_dvd_device() + local dvd_device = mp.get_property('dvd-device') + if not dvd_device or dvd_device == '' then return nil end + return fb_utils.fix_path(dvd_device, true) +end + +---@param str string +function fb.set_empty_text(str) + g.state.empty_text = str + fb.redraw() +end + +---@param index number +---@return number|false +function fb.set_selected_index(index) + if type(index) ~= "number" then return false end + if index < 1 then index = 1 end + if index > #g.state.list then index = #g.state.list end + g.state.selected = index + fb.redraw() + return index +end + +fb.set_history_index = directory_movement.goto_history + +return fb diff --git a/mpv/scripts/file-browser/modules/apis/parse-state.lua b/mpv/scripts/file-browser/modules/apis/parse-state.lua new file mode 100644 index 0000000..6db5658 --- /dev/null +++ b/mpv/scripts/file-browser/modules/apis/parse-state.lua @@ -0,0 +1,34 @@ + +local msg = require 'mp.msg' + +local g = require 'modules.globals' + +---@class ParseStateAPI +local parse_state_API = {} + +---A wrapper around coroutine.yield that aborts the coroutine if +--the parse request was cancelled by the user. +--the coroutine is +---@async +---@param self ParseState +---@param ... any +---@return unknown ... +function parse_state_API:yield(...) + local co = coroutine.running() + local is_browser = co == g.state.co + + local result = table.pack(coroutine.yield(...)) + if is_browser and co ~= g.state.co then + msg.verbose("browser no longer waiting for list - aborting parse for", self.directory) + error(g.ABORT_ERROR) + end + return table.unpack(result, 1, result.n) +end + +---Checks if the current coroutine is the one handling the browser's request. +---@return boolean +function parse_state_API:is_coroutine_current() + return coroutine.running() == g.state.co +end + +return parse_state_API diff --git a/mpv/scripts/file-browser/modules/apis/parser.lua b/mpv/scripts/file-browser/modules/apis/parser.lua new file mode 100644 index 0000000..9c3fbeb --- /dev/null +++ b/mpv/scripts/file-browser/modules/apis/parser.lua @@ -0,0 +1,40 @@ +local msg = require 'mp.msg' + +local g = require 'modules.globals' +local scanning = require 'modules.navigation.scanning' +local fb = require 'modules.apis.fb' + +---@class ParserAPI: FbAPI +local parser_api = setmetatable({}, { __index = fb }) + +---Returns the index of the parser. +---@return number +function parser_api:get_index() return g.parsers[self].index end + +---Returns the ID of the parser +---@return string +function parser_api:get_id() return g.parsers[self].id end + +---A newer API for adding items to the root. +---Only adds the item if the same item does not already exist in the root. +---Wrapper around `fb.register_root_item`. +---@param item Item|string +---@param priority? number The priority for the added item. Uses the parsers priority by default. +---@return boolean +function parser_api:register_root_item(item, priority) + return fb.register_root_item(item, priority or g.parsers[self:get_id()].priority) +end + +---Runs choose_and_parse starting from the next parser. +---@async +---@param directory string +---@return Item[]? +---@return Opts? +function parser_api:defer(directory) + msg.trace("deferring to other parsers...") + local list, opts = scanning.choose_and_parse(directory, self:get_index() + 1) + fb.get_parse_state().already_deferred = true + return list, opts +end + +return parser_api |
