This commit is contained in:
2026-04-03 10:15:46 +02:00
parent a00398dd6c
commit 9bc7887bd6
241 changed files with 0 additions and 95907 deletions
@@ -1,206 +0,0 @@
--[[
An addon for mpv-file-browser which adds a Favourites path that can be loaded from the ROOT
]]--
local mp = require "mp"
local msg = require "mp.msg"
local fb = require 'file-browser'
local save_path = mp.command_native({"expand-path", "~~/script-opts/file_browser_favourites.txt"}) --[[@as string]]
do
local file = io.open(save_path, "a+")
if not file then
msg.error("cannot access file", ("%q"):format(save_path), "make sure that the directory exists")
return {}
end
file:close()
end
---@type Item[]
local favourites = {}
local favourites_loaded = false
---@type ParserConfig
local favs = {
api_version = "1.8.0",
priority = 30,
cursor = 1
}
local use_virtual_directory = true
---@type table<string,string>
local full_paths = {}
---@param str string
---@return Item
local function create_favourite_object(str)
local item = {
type = str:sub(-1) == "/" and "dir" or "file",
path = str,
redirect = not use_virtual_directory,
name = str:match("([^/]+/?)$")
}
full_paths[str:match("([^/]+)/?$")] = str
return item
end
---@param self Parser
function favs:setup()
self:register_root_item('Favourites/')
end
local function update_favourites()
local file = io.open(save_path, "r")
if not file then return end
favourites = {}
for str in file:lines() do
table.insert(favourites, create_favourite_object(str))
end
file:close()
favourites_loaded = true
end
function favs:can_parse(directory)
return directory:find("Favourites/") == 1
end
---@async
---@param self Parser
---@param directory string
---@return List?
---@return Opts?
function favs:parse(directory)
if not favourites_loaded then update_favourites() end
if directory == "Favourites/" then
local opts = {
filtered = true,
sorted = true
}
return favourites, opts
end
if use_virtual_directory then
-- converts the relative favourite path into a full path
local name = directory:match("Favourites/([^/]+)/?")
local _, finish = directory:find("Favourites/([^/]+/?)")
local full_path = (full_paths[name] or "")..directory:sub(finish+1)
local list, opts = self:defer(full_path or "")
if not list then return nil end
opts = opts or {}
opts.id = self:get_id()
if opts.directory_label then
opts.directory_label = opts.directory_label:gsub(full_paths[name], "Favourites/"..name..'/')
if opts.directory_label:find("Favourites/") ~= 1 then opts.directory_label = nil end
end
for _, item in ipairs(list) do
if not item.path then item.redirect = false end
item.path = item.path or full_path..item.name
end
return list, opts
end
local path = full_paths[ directory:match("([^/]+/?)$") or "" ]
local list, opts = self:defer(path)
if not list then return nil end
opts = opts or {}
opts.directory = opts.directory or path
return list, opts
end
---@param path string
---@return integer?
---@return Item?
local function get_favourite(path)
for index, value in ipairs(favourites) do
if value.path == path then return index, value end
end
end
--update the browser with new contents of the file
---@async
local function update_browser()
if favs.get_directory():find("^[fF]avourites/$") then
local cursor = favs.get_selected_index()
fb.rescan_await()
fb.set_selected_index(cursor)
else
fb.clear_cache({'favourites/', 'Favourites/'})
end
end
--write the contents of favourites to the file
local function write_to_file()
local file = io.open(save_path, "w+")
if not file then return msg.error(file, "could not open favourites file") end
for _, item in ipairs(favourites) do
file:write(string.format("%s\n", item.path))
end
file:close()
end
local function add_favourite(path)
if get_favourite(path) then return end
update_favourites()
table.insert(favourites, create_favourite_object(path))
write_to_file()
end
local function remove_favourite(path)
update_favourites()
local index = get_favourite(path)
if not index then return end
table.remove(favourites, index)
write_to_file()
end
local function move_favourite(path, direction)
update_favourites()
local index, item = get_favourite(path)
if not index or not favourites[index + direction] then return end
favourites[index] = favourites[index + direction]
favourites[index + direction] = item
write_to_file()
end
---@async
local function toggle_favourite(cmd, state, co)
local path = fb.get_full_path(state.list[state.selected], state.directory)
if state.directory:find("[fF]avourites/$") then remove_favourite(path)
else add_favourite(path) end
update_browser()
end
---@async
local function move_key(cmd, state, co)
if not state.directory:find("[fF]avourites/") then return false end
local path = fb.get_full_path(state.list[state.selected], state.directory)
local cursor = fb.get_selected_index()
if cmd.name == favs:get_id().."/move_up" then
move_favourite(path, -1)
fb.set_selected_index(cursor-1)
else
move_favourite(path, 1)
fb.set_selected_index(cursor+1)
end
update_browser()
end
update_favourites()
favs.keybinds = {
{ "F", "toggle_favourite", toggle_favourite, {}, },
{ "Ctrl+UP", "move_up", move_key, {repeatable = true} },
{ "Ctrl+DOWN", "move_down", move_key, {repeatable = true} },
}
return favs
@@ -1,39 +0,0 @@
--[[
An addon for file-browser which decodes URLs so that they are more readable
]]
---@type ParserConfig
local urldecode = {
priority = 5,
api_version = "1.0.0"
}
--decodes a URL address
--this piece of code was taken from: https://stackoverflow.com/questions/20405985/lua-decodeuri-luvit/20406960#20406960
---@type fun(s: string): string
local decodeURI
do
local char, gsub, tonumber = string.char, string.gsub, tonumber
local function _(hex) return char(tonumber(hex, 16)) end
function decodeURI(s)
s = gsub(s, '%%(%x%x)', _)
return s
end
end
function urldecode:can_parse(directory)
return self.get_protocol(directory) ~= nil
end
---@async
function urldecode:parse(directory)
local list, opts = self:defer(directory)
opts = opts or {}
if opts.directory and not self.get_protocol(opts.directory) then return list, opts end
opts.directory_label = decodeURI(opts.directory_label or (opts.directory or directory))
return list, opts
end
return urldecode
@@ -1,206 +0,0 @@
--[[
An addon for mpv-file-browser which adds a Favourites path that can be loaded from the ROOT
]]--
local mp = require "mp"
local msg = require "mp.msg"
local fb = require 'file-browser'
local save_path = mp.command_native({"expand-path", "~~/script-opts/file_browser_favourites.txt"}) --[[@as string]]
do
local file = io.open(save_path, "a+")
if not file then
msg.error("cannot access file", ("%q"):format(save_path), "make sure that the directory exists")
return {}
end
file:close()
end
---@type Item[]
local favourites = {}
local favourites_loaded = false
---@type ParserConfig
local favs = {
api_version = "1.8.0",
priority = 30,
cursor = 1
}
local use_virtual_directory = true
---@type table<string,string>
local full_paths = {}
---@param str string
---@return Item
local function create_favourite_object(str)
local item = {
type = str:sub(-1) == "/" and "dir" or "file",
path = str,
redirect = not use_virtual_directory,
name = str:match("([^/]+/?)$")
}
full_paths[str:match("([^/]+)/?$")] = str
return item
end
---@param self Parser
function favs:setup()
self:register_root_item('Favourites/')
end
local function update_favourites()
local file = io.open(save_path, "r")
if not file then return end
favourites = {}
for str in file:lines() do
table.insert(favourites, create_favourite_object(str))
end
file:close()
favourites_loaded = true
end
function favs:can_parse(directory)
return directory:find("Favourites/") == 1
end
---@async
---@param self Parser
---@param directory string
---@return List?
---@return Opts?
function favs:parse(directory)
if not favourites_loaded then update_favourites() end
if directory == "Favourites/" then
local opts = {
filtered = true,
sorted = true
}
return favourites, opts
end
if use_virtual_directory then
-- converts the relative favourite path into a full path
local name = directory:match("Favourites/([^/]+)/?")
local _, finish = directory:find("Favourites/([^/]+/?)")
local full_path = (full_paths[name] or "")..directory:sub(finish+1)
local list, opts = self:defer(full_path or "")
if not list then return nil end
opts = opts or {}
opts.id = self:get_id()
if opts.directory_label then
opts.directory_label = opts.directory_label:gsub(full_paths[name], "Favourites/"..name..'/')
if opts.directory_label:find("Favourites/") ~= 1 then opts.directory_label = nil end
end
for _, item in ipairs(list) do
if not item.path then item.redirect = false end
item.path = item.path or full_path..item.name
end
return list, opts
end
local path = full_paths[ directory:match("([^/]+/?)$") or "" ]
local list, opts = self:defer(path)
if not list then return nil end
opts = opts or {}
opts.directory = opts.directory or path
return list, opts
end
---@param path string
---@return integer?
---@return Item?
local function get_favourite(path)
for index, value in ipairs(favourites) do
if value.path == path then return index, value end
end
end
--update the browser with new contents of the file
---@async
local function update_browser()
if favs.get_directory():find("^[fF]avourites/$") then
local cursor = favs.get_selected_index()
fb.rescan_await()
fb.set_selected_index(cursor)
else
fb.clear_cache({'favourites/', 'Favourites/'})
end
end
--write the contents of favourites to the file
local function write_to_file()
local file = io.open(save_path, "w+")
if not file then return msg.error(file, "could not open favourites file") end
for _, item in ipairs(favourites) do
file:write(string.format("%s\n", item.path))
end
file:close()
end
local function add_favourite(path)
if get_favourite(path) then return end
update_favourites()
table.insert(favourites, create_favourite_object(path))
write_to_file()
end
local function remove_favourite(path)
update_favourites()
local index = get_favourite(path)
if not index then return end
table.remove(favourites, index)
write_to_file()
end
local function move_favourite(path, direction)
update_favourites()
local index, item = get_favourite(path)
if not index or not favourites[index + direction] then return end
favourites[index] = favourites[index + direction]
favourites[index + direction] = item
write_to_file()
end
---@async
local function toggle_favourite(cmd, state, co)
local path = fb.get_full_path(state.list[state.selected], state.directory)
if state.directory:find("[fF]avourites/$") then remove_favourite(path)
else add_favourite(path) end
update_browser()
end
---@async
local function move_key(cmd, state, co)
if not state.directory:find("[fF]avourites/") then return false end
local path = fb.get_full_path(state.list[state.selected], state.directory)
local cursor = fb.get_selected_index()
if cmd.name == favs:get_id().."/move_up" then
move_favourite(path, -1)
fb.set_selected_index(cursor-1)
else
move_favourite(path, 1)
fb.set_selected_index(cursor+1)
end
update_browser()
end
update_favourites()
favs.keybinds = {
{ "F", "toggle_favourite", toggle_favourite, {}, },
{ "Ctrl+UP", "move_up", move_key, {repeatable = true} },
{ "Ctrl+DOWN", "move_down", move_key, {repeatable = true} },
}
return favs
@@ -1,45 +0,0 @@
local fb = require "file-browser"
local opt = require "mp.options"
local o = {
--list of absolute paths separated by the root separators
paths = ""
}
--config file stored in ~~/script-opts/file-browser/filter.conf
opt.read_options(o, "file-browser/filter")
local parser = {
priority = 10,
api_version = "1.3.0"
}
local paths = {}
for str in fb.iterate_opt(o.paths) do
paths[str] = true
end
local function filter(path)
return paths[path]
end
function parser:can_parse()
return true
end
function parser:parse(directory)
local list, opts = self:defer(directory)
if not list then return list, opts end
directory = opts.directory or directory
for i=#list, 1, -1 do
if filter( fb.get_full_path(list[i], directory) ) then
table.remove(list, i)
end
end
return list, opts
end
return parser
@@ -1,41 +0,0 @@
local mp = require 'mp'
local msg = require 'mp.msg'
local fb = require 'file-browser'
local isos = {
name = 'iso-loader',
priority = 20,
api_version = '1.5'
}
function isos:setup()
fb.add_default_extension('iso')
end
function isos:can_parse()
return true
end
function isos:parse(directory, parse_state)
local list, opts = self:defer(directory, parse_state)
if not list or #list == 0 then return list, opts end
for _, item in ipairs(list) do
local path = fb.get_full_path(item, opts.directory or directory)
if fb.get_extension(path) == 'iso' then
item.mpv_options = { ['bluray-device'] = path, ['dvd-device'] = path }
item.path = 'bd://'
end
end
return list, opts
end
mp.add_hook('on_load_fail', 50, function()
if mp.get_property('stream-open-filename') == 'bd://' then
msg.info('failed to load bluray-device, attempting dvd-device')
mp.set_property('stream-open-filename', 'dvd://')
end
end)
return isos
@@ -1,124 +0,0 @@
--[[
This file is an internal file-browser addon.
It should not be imported like a normal module.
Allows searching the current directory.
]]--
local msg = require "mp.msg"
local fb = require "file-browser"
local input_loaded, input = pcall(require, "mp.input")
local user_input_loaded, user_input = pcall(require, "user-input-module")
---@type ParserConfig
local find = {
api_version = "1.3.0"
}
---@type thread|nil
local latest_coroutine = nil
---@type State
local global_fb_state = getmetatable(fb.get_state()).__original
---@param name string
---@param query string
---@return boolean
local function compare(name, query)
if name:find(query) then return true end
if name:lower():find(query) then return true end
if name:upper():find(query) then return true end
return false
end
---@async
---@param key Keybind
---@param state State
---@param co thread
---@return boolean?
local function main(key, state, co)
if not state.list then return false end
---@type string
local text
if key.name == "find/find" then text = "Find: enter search string"
else text = "Find: enter advanced search string" end
if input_loaded then
input.get({
prompt = text .. "\n>",
id = "file-browser/find",
submit = fb.coroutine.callback(),
})
elseif user_input_loaded then
user_input.get_user_input( fb.coroutine.callback(), { text = text, id = "find", replace = true } )
end
local query, error = coroutine.yield()
if input_loaded then input.terminate() end
if not query then return msg.debug(error) end
-- allow the directory to be changed before this point
local list = fb.get_list()
local parse_id = global_fb_state.co
if key.name == "find/find" then
query = fb.pattern_escape(query)
end
local results = {}
for index, item in ipairs(list) do
if compare(item.label or item.name, query) then
table.insert(results, index)
end
end
if (#results < 1) then
msg.warn("No matching items for '"..query.."'")
return
end
--keep cycling through the search results if any are found
--putting this into a separate coroutine removes any passthrough ambiguity
--the final return statement should return to `step_find` not any other function
---@async
fb.coroutine.run(function()
latest_coroutine = coroutine.running()
---@type number
local rindex = 1
while (true) do
if rindex == 0 then rindex = #results
elseif rindex == #results + 1 then rindex = 1 end
fb.set_selected_index(results[rindex])
local direction = coroutine.yield(true) --[[@as number]]
rindex = rindex + direction
if parse_id ~= global_fb_state.co then
latest_coroutine = nil
return
end
end
end)
end
local function step_find(key)
if not latest_coroutine then return false end
---@type number
local direction = 0
if key.name == "find/next" then direction = 1
elseif key.name == "find/prev" then direction = -1 end
return fb.coroutine.resume_err(latest_coroutine, direction)
end
find.keybinds = {
{"Ctrl+f", "find", main, {}},
{"Ctrl+F", "find_advanced", main, {}},
{"n", "next", step_find, {}},
{"N", "prev", step_find, {}},
}
return find
@@ -1,31 +0,0 @@
--[[
An addon for mpv-file-browser which displays ~/ for the home directory instead of the full path
]]--
local mp = require "mp"
local fb = require "file-browser"
local home = fb.fix_path(mp.command_native({"expand-path", "~/"}) --[[@as string]], true)
---@type ParserConfig
local home_label = {
priority = 100,
api_version = "1.0.0"
}
function home_label:can_parse(directory)
if not fb.get_opt('home_label') then return false end
return directory:sub(1, home:len()) == home
end
---@async
function home_label:parse(directory)
local list, opts = self:defer(directory)
if not opts then opts = {} end
if (not opts.directory or opts.directory == directory) and not opts.directory_label then
opts.directory_label = "~/"..(directory:sub(home:len()+1) or "")
end
return list, opts
end
return home_label
@@ -1,218 +0,0 @@
--[[
An addon for mpv-file-browser which uses the Windows dir command to parse native directories
This behaves near identically to the native parser, but IO is done asynchronously.
Available at: https://github.com/CogentRedTester/mpv-file-browser/tree/master/addons
]]--
local mp = require "mp"
local msg = require "mp.msg"
local fb = require "file-browser"
local PLATFORM = fb.get_platform()
---@param bytes string
---@return fun(): number, number
local function byte_iterator(bytes)
---@async
---@return number?
local function iter()
for i = 1, #bytes do
coroutine.yield(bytes:byte(i), i)
end
error('malformed utf16le string - expected byte but found end of string')
end
return coroutine.wrap(iter)
end
---@param bits number
---@param by number
---@return number
local function lshift(bits, by)
return bits * 2^by
end
---@param bits number
---@param by number
---@return integer
local function rshift(bits, by)
return math.floor(bits / 2^by)
end
---@param bits number
---@param i number
---@return number
local function bits_below(bits, i)
return bits % 2^i
end
---@param bits number
---@param i number exclusive
---@param j number inclusive
---@return integer
local function bits_between(bits, i, j)
return rshift(bits_below(bits, j), i)
end
---@param bytes string
---@return number[]
local function utf16le_to_unicode(bytes)
msg.trace('converting from utf16-le to unicode codepoints')
---@type number[]
local codepoints = {}
local get_byte = byte_iterator(bytes)
while true do
-- start of a char
local success, little, i = pcall(get_byte)
if not success then break end
local big = get_byte()
local codepoint = little + lshift(big, 8)
if codepoint < 0xd800 or codepoint > 0xdfff then
table.insert(codepoints, codepoint)
else
-- handling surrogate pairs
-- grab the next two bytes to grab the low surrogate
local high_pair = codepoint
local low_pair = get_byte() + lshift(get_byte(), 8)
if high_pair >= 0xdc00 then
error(('malformed utf16le string at byte #%d (0x%04X) - high surrogate pair should be < 0xDC00'):format(i, high_pair))
elseif low_pair < 0xdc00 then
error(('malformed utf16le string at byte #%d (0x%04X) - low surrogate pair should be >= 0xDC00'):format(i+2, low_pair))
end
-- The last 10 bits of each surrogate are the two halves of the codepoint
-- https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF
local high_bits = bits_below(high_pair, 10)
local low_bits = bits_below(low_pair, 10)
local surrogate_par = (low_bits + lshift(high_bits, 10)) + 0x10000
table.insert(codepoints, surrogate_par)
end
end
return codepoints
end
---@param codepoints number[]
---@return string
local function unicode_to_utf8(codepoints)
---@type number[]
local bytes = {}
-- https://en.wikipedia.org/wiki/UTF-8#Description
for i, codepoint in ipairs(codepoints) do
if codepoint >= 0xd800 and codepoint <= 0xdfff then
error(('codepoint %d (U+%05X) is within the reserved surrogate pair range (U+D800-U+DFFF)'):format(i, codepoint))
elseif codepoint <= 0x7f then
table.insert(bytes, codepoint)
elseif codepoint <= 0x7ff then
table.insert(bytes, 0xC0 + rshift(codepoint, 6))
table.insert(bytes, 0x80 + bits_below(codepoint, 6))
elseif codepoint <= 0xffff then
table.insert(bytes, 0xE0 + rshift(codepoint, 12))
table.insert(bytes, 0x80 + bits_between(codepoint, 6, 12))
table.insert(bytes, 0x80 + bits_below(codepoint, 6))
elseif codepoint <= 0x10ffff then
table.insert(bytes, 0xF0 + rshift(codepoint, 18))
table.insert(bytes, 0x80 + bits_between(codepoint, 12, 18))
table.insert(bytes, 0x80 + bits_between(codepoint, 6, 12))
table.insert(bytes, 0x80 + bits_below(codepoint, 6))
else
error(('codepoint %d (U+%05X) is larger than U+10FFFF'):format(i, codepoint))
end
end
return string.char(table.unpack(bytes))
end
local function utf8(text)
return unicode_to_utf8(utf16le_to_unicode(text))
end
---@type ParserConfig
local dir = {
priority = 109,
api_version = "1.9.0",
name = "cmd-dir",
keybind_name = "file"
}
---@async
---@param args string[]
---@param parse_state ParseState
---@return string|nil
local function command(args, parse_state)
local async = mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args,
}, fb.coroutine.callback(30) )
---@type boolean, boolean, MPVSubprocessResult
local completed, _, cmd = parse_state:yield()
if not completed then
msg.warn('read timed out for:', table.unpack(args))
mp.abort_async_command(async)
return nil
end
local success = xpcall(function()
cmd.stdout = utf8(cmd.stdout) or ''
cmd.stderr = utf8(cmd.stderr) or ''
end, fb.traceback)
if not success then return msg.error('failed to convert utf16-le string to utf8') end
--dir returns this exact error message if the directory is empty
if cmd.status == 1 and cmd.stderr == "File Not Found\r\n" then cmd.status = 0 end
if cmd.status ~= 0 then return msg.error(cmd.stderr) end
return cmd.status == 0 and cmd.stdout or nil
end
function dir:can_parse(directory)
if not fb.get_opt('windir_parser') then return false end
return PLATFORM == 'windows' and directory ~= '' and not fb.get_protocol(directory)
end
---@async
function dir:parse(directory, parse_state)
local list = {}
-- the dir command expects backslashes for our paths
directory = string.gsub(directory, "/", "\\")
local dirs = command({ "cmd", "/U", "/c", "dir", "/b", "/ad", directory }, parse_state)
if not dirs then return end
local files = command({ "cmd", "/U", "/c", "dir", "/b", "/a-d", directory }, parse_state)
if not files then return end
for name in dirs:gmatch("[^\n\r]+") do
name = name.."/"
if fb.valid_dir(name) then
table.insert(list, { name = name, type = "dir" })
msg.trace(name)
end
end
for name in files:gmatch("[^\n\r]+") do
if fb.valid_file(name) then
table.insert(list, { name = name, type = "file" })
msg.trace(name)
end
end
return list, { filtered = true }
end
return dir
@@ -1,62 +0,0 @@
--[[
This file is an internal file-browser addon.
It should not be imported like a normal module.
Automatically populates the root with windows drives on startup.
Ctrl+r will add new drives mounted since startup.
Drives will only be added if they are not already present in the root.
]]
local mp = require 'mp'
local msg = require 'mp.msg'
local fb = require 'file-browser'
local PLATFORM = fb.get_platform()
---returns a list of windows drives
---@return string[]?
local function get_drives()
---@type MPVSubprocessResult?, string?
local result, err = mp.command_native({
name = 'subprocess',
playback_only = false,
capture_stdout = true,
args = {'fsutil', 'fsinfo', 'drives'}
})
if not result then return msg.error(err) end
if result.status ~= 0 then return msg.error('could not read windows root') end
local root = {}
for drive in result.stdout:gmatch("(%a:)\\") do
table.insert(root, drive..'/')
end
return root
end
-- adds windows drives to the root if they are not already present
local function import_drives()
if fb.get_opt('auto_detect_windows_drives') and PLATFORM ~= 'windows' then return end
local drives = get_drives()
if not drives then return end
for _, drive in ipairs(drives) do
fb.register_root_item(drive)
end
end
local keybind = {
key = 'Ctrl+r',
name = 'import_root_drives',
command = import_drives,
parser = 'root',
passthrough = true
}
---@type ParserConfig
return {
api_version = '1.9.0',
setup = import_drives,
keybinds = { keybind }
}
@@ -1,86 +0,0 @@
local msg = require 'mp.msg'
local utils = require 'mp.utils'
local fb = require 'file-browser'
local parser = {
priority = 105,
api_version = '1.2.0'
}
-- stores a table of the parsers loaded by file-browser
-- we will use this to check if a parser is for a local file system
local parsers
local sort_mode = 0
function parser:setup()
parsers = fb.get_parsers()
end
function parser:parse(directory)
if sort_mode == 0 or fb.get_protocol(directory) then return end
local list, opts = self:defer(directory)
if not list then return list, opts end
-- Only run this on parsers that are for the local filesystem.
-- We assume that custom addons for the local filesystem are setting the keybind_name field to 'file'
-- for compatability.
if parsers[opts.id] then
if parsers[opts.id].keybind_name ~= 'file' and parsers[opts.id].name ~= 'file' then
return list, opts
end
end
directory = opts.directory or directory
local cache = {}
-- gets the file info of an item
-- uses memoisation to speed things up
function get_file_info(item)
if cache[item] then return cache[item] end
local path = fb.get_full_path(item, directory)
local file_info = utils.file_info(path)
if not file_info then
msg.warn('failed to read file info for', path)
return {}
end
cache[item] = file_info
return file_info
end
-- sorts the items based on the latest modification time
-- if mtime is undefined due to a file read failure then use 0
table.sort(list, function(a, b)
-- `dir` will compare as less than `file`
if a.type ~= b.type then return a.type < b.type end
if sort_mode == 1 then
return (get_file_info(a).mtime or 0) < (get_file_info(b).mtime or 0)
elseif sort_mode == 2 then
return (get_file_info(a).mtime or 0) > (get_file_info(b).mtime or 0)
elseif sort_mode == 3 then
return (get_file_info(a).size or 0) < (get_file_info(b).size or 0)
elseif sort_mode == 4 then
return (get_file_info(a).size or 0) > (get_file_info(b).size or 0)
end
end)
opts.sorted = true
return list, opts
end
-- adds the keybind to toggle sorting
parser.keybinds = {
{
key = '^',
name = 'toggle_sort',
command = function()
sort_mode = sort_mode + 1
if sort_mode > 4 then sort_mode = 0 end
fb.rescan()
end
}
}
return parser
@@ -1,39 +0,0 @@
--[[
An addon for file-browser which decodes URLs so that they are more readable
]]
---@type ParserConfig
local urldecode = {
priority = 5,
api_version = "1.0.0"
}
--decodes a URL address
--this piece of code was taken from: https://stackoverflow.com/questions/20405985/lua-decodeuri-luvit/20406960#20406960
---@type fun(s: string): string
local decodeURI
do
local char, gsub, tonumber = string.char, string.gsub, tonumber
local function _(hex) return char(tonumber(hex, 16)) end
function decodeURI(s)
s = gsub(s, '%%(%x%x)', _)
return s
end
end
function urldecode:can_parse(directory)
return self.get_protocol(directory) ~= nil
end
---@async
function urldecode:parse(directory)
local list, opts = self:defer(directory)
opts = opts or {}
if opts.directory and not self.get_protocol(opts.directory) then return list, opts end
opts.directory_label = decodeURI(opts.directory_label or (opts.directory or directory))
return list, opts
end
return urldecode
@@ -1,51 +0,0 @@
local fb = require "file-browser"
local fb_utils = require 'modules.utils'
local PLATFORM = fb.get_platform()
-- Only enable Windows-specific sorting on Windows platforms
if PLATFORM == 'windows' then
-- this code is based on https://github.com/mpvnet-player/mpv.net/issues/575#issuecomment-1817413401
local ffi = require "ffi"
local winapi = {
ffi = ffi,
C = ffi.C,
CP_UTF8 = 65001,
shlwapi = ffi.load("shlwapi"),
}
-- ffi code from https://github.com/po5/thumbfast, Mozilla Public License Version 2.0
ffi.cdef[[
int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr,
int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
int __stdcall StrCmpLogicalW(wchar_t *psz1, wchar_t *psz2);
]]
winapi.utf8_to_wide = function(utf8_str)
if utf8_str then
local utf16_len = winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, utf8_str, -1, nil, 0)
if utf16_len > 0 then
local utf16_str = winapi.ffi.new("wchar_t[?]", utf16_len)
if winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, utf8_str, -1, utf16_str, utf16_len) > 0 then
return utf16_str
end
end
end
return ""
end
fb_utils.sort = function (t)
table.sort(t, function(a, b)
local a_wide = winapi.utf8_to_wide(a.type:sub(1, 1) .. (a.label or a.name))
local b_wide = winapi.utf8_to_wide(b.type:sub(1, 1) .. (b.label or b.name))
return winapi.shlwapi.StrCmpLogicalW(a_wide, b_wide) == -1
end)
return t
end
end
return { api_version = '1.2.0' }