first commit

This commit is contained in:
2025-06-14 20:26:14 +02:00
commit 1edfd60dbd
351 changed files with 34592 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
# addons
Add-ons are ways to add extra features to file-browser, for example adding support for network file servers like ftp, or implementing virtual directories in the root like recently opened files.
They can be enabled by setting `addon` script-opt to yes, and placing the addon file into the `~~/script-modules/file-browser-addons/` directory.
Browsing filesystems provided by add-ons should feel identical to the normal handling of the script,
but they may require extra commandline tools be installed.
Since addons are loaded programatically from the addon directory it is possible for anyone to write their own addon.
Instructions on how to do this are available [here](../docs/addons.md).
For a list of available addons see the [wiki](https://github.com/CogentRedTester/mpv-file-browser/wiki/Addon-List).

View File

@@ -0,0 +1,89 @@
--[[
An addon for mpv-file-browser which adds support for apache http directory indexes
]]--
local mp = require 'mp'
local msg = require 'mp.msg'
local utils = require 'mp.utils'
local fb = require "file-browser"
--decodes a URL address
--this piece of code was taken from: https://stackoverflow.com/questions/20405985/lua-decodeuri-luvit/20406960#20406960
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
local apache = {
priority = 80,
api_version = "1.1.0"
}
function apache:can_parse(name)
return name:find("^https?://")
end
--send curl errors through the browser empty_text
function apache:send_error(str)
return {}, {empty_text = "curl error: "..str}
end
local function execute(args)
msg.trace(utils.to_string(args))
local _, cmd = fb.get_parse_state():yield(
mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args
}, fb.coroutine.callback())
)
return cmd
end
function apache:parse(directory)
msg.verbose(directory)
local test = execute({"curl", "-k", "-l", "-I", directory})
local response = test.stdout:match("(%d%d%d [^\n\r]+)")
if test.stdout:match("Content%-Type: ([^\r\n/]+)") ~= "text" then return nil end
if response ~= "200 OK" then return self:send_error(response) end
local html = execute({"curl", "-k", "-l", directory})
if html.status ~= 0 then return self:send_error(tostring(html.status))
elseif not html.stdout:find("%[PARENTDIR%]") then return nil end
html = html.stdout
local list = {}
for str in string.gmatch(html, "[^\r\n]+") do
local valid = true
if str:sub(1,4) ~= "<tr>" then valid = false end
local link = str:match('href="(.-)"')
local alt = str:match('alt="%[(.-)%]"')
if valid and not alt or not link then valid = false end
if valid and alt == "PARENTDIR" or alt == "ICO" then valid = false end
if valid and link:find("[:?<>|]") then valid = false end
local is_dir = (alt == "DIR")
if valid and is_dir and not self.valid_dir(link) then valid = false end
if valid and not is_dir and not self.valid_file(link) then valid = false end
if valid then
msg.trace(alt..": "..link)
table.insert(list, { name = link, type = (is_dir and "dir" or "file"), label = decodeURI(link) })
end
end
return list, {filtered = true, directory_label = decodeURI(directory)}
end
return apache

View File

@@ -0,0 +1,191 @@
--[[
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 utils = require "mp.utils"
local save_path = mp.command_native({"expand-path", "~~/script-opts/file_browser_favourites.txt"})
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
local favourites = nil
local favs = {
api_version = "1.4.0",
priority = 30,
cursor = 1
}
local use_virtual_directory = true
local full_paths = {}
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
function favs:setup()
self:register_root_item('Favourites/')
end
local function update_favourites()
favourites = {}
local file = io.open(save_path, "r")
if not file then return end
for str in file:lines() do
table.insert(favourites, create_favourite_object(str))
end
file:close()
end
function favs:can_parse(directory)
return directory:find("Favourites/") == 1
end
function favs:parse(directory)
if not favourites then update_favourites() end
if directory == "Favourites/" then
local opts = {
filtered = true,
sorted = true
}
if self.cursor ~= 1 then opts.selected_index = self.cursor ; self.cursor = 1 end
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.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.directory = opts.directory or path
return list, opts
end
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
local function update_browser()
if favs.get_directory():find("[fF]avourites/") then
if favs.get_directory():find("[fF]avourites/$") then
local cursor = favs.get_selected_index()
favs.rescan()
favs.set_selected_index(cursor)
favs.redraw()
else
favs.clear_cache()
end
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
local function toggle_favourite(cmd, state, co)
local path = favs.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
local function move_key(cmd, state, co)
if not state.directory:find("[fF]avourites/") then return false end
local path = favs.get_full_path(state.list[state.selected], state.directory)
local cursor = favs.get_selected_index()
if cmd.name == favs:get_id().."/move_up" then
move_favourite(path, -1)
favs.set_selected_index(cursor-1)
else
move_favourite(path, 1)
favs.set_selected_index(cursor+1)
end
update_browser()
end
update_favourites()
mp.register_script_message("favourites/add_favourite", add_favourite)
mp.register_script_message("favourites/remove_favourite", remove_favourite)
mp.register_script_message("favourites/move_up", function(path) move_favourite(path, -1) end)
mp.register_script_message("favourites/move_down", function(path) move_favourite(path, 1) end)
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

View File

@@ -0,0 +1,104 @@
--[[
An addon for mpv-file-browser for searching the current directory
Available at: https://github.com/CogentRedTester/mpv-file-browser/tree/master/addons
Requires mpv-user-input: https://github.com/CogentRedTester/mpv-user-input
Keybinds:
Ctrl+f open search box
Ctrl+F open advanced search box (supports Lua patterns)
n cycle to next valid item
]]--
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")
local find = {
api_version = "1.3.0"
}
local latest_coroutine = nil
local global_fb_state = getmetatable(fb.get_state()).__original
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
local function main(key, state, co)
if not state.list then return false end
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
fb.coroutine.run(function()
latest_coroutine = coroutine.running()
while (true) do
for _, index in ipairs(results) do
fb.set_selected_index(index)
coroutine.yield(true)
if parse_id ~= global_fb_state.co then
latest_coroutine = nil
return false
end
end
end
end)
end
local function step_find()
if not latest_coroutine then return false end
return fb.coroutine.resume_err(latest_coroutine)
end
find.keybinds = {
{"Ctrl+f", "find", main, {}},
{"Ctrl+F", "find_advanced", main, {}},
{"n", "next", step_find, {}},
}
return find

View File

@@ -0,0 +1,86 @@
--[[
An addon for mpv-file-browser which adds support for ftp servers
]]--
local mp = require 'mp'
local msg = require 'mp.msg'
local utils = require 'mp.utils'
local fb = require 'file-browser'
local ftp = {
priority = 100,
api_version = "1.1.0"
}
function ftp:can_parse(directory)
return directory:sub(1, 6) == "ftp://"
end
--in my experience curl has been somewhat unreliable when it comes to ftp requests
--this fuction retries the request a few times just in case
local function execute(args)
msg.debug(utils.to_string(args))
local _, cmd = fb.get_parse_state():yield(
mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args
}, fb.coroutine.callback())
)
return cmd
end
-- encodes special characters using the URL percent encoding format
function urlEncode(url)
local domain, path = string.match(url, '(ftp://[^/]-/)(.*)')
if not path then return url end
-- these are the unreserved URI characters according to RFC 3986
-- https://www.rfc-editor.org/rfc/rfc3986#section-2.3
path = string.gsub(path, '[^%w.~_%-]', function(c)
return ('%%%x'):format(string.byte(c))
end)
return domain..path
end
function ftp:parse(directory)
msg.verbose(directory)
local ftp = execute({"curl", "-k", "-g", "--retry", "4", urlEncode(directory)})
local entries = execute({"curl", "-k", "-g", "-l", "--retry", "4", urlEncode(directory)})
if entries.status == 28 then
msg.error(entries.stderr)
elseif entries.status ~= 0 or ftp.status ~= 0 then
msg.error(entries.stderr)
return
end
local response = {}
for str in string.gmatch(ftp.stdout, "[^\r\n]+") do
table.insert(response, str)
end
local list = {}
local i = 1
for str in string.gmatch(entries.stdout, "[^\r\n]+") do
if str and response[i] then
msg.trace(str .. ' | ' .. response[i])
if response[i]:sub(1,1) == "d" then
table.insert(list, { name = str..'/', type = "dir" })
else
table.insert(list, { name = str, type = "file" })
end
i = i+1
end
end
return list
end
return ftp

View File

@@ -0,0 +1,27 @@
--[[
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", "~/"}), true)
local home_label = {
priority = 100,
api_version = "1.0.0"
}
function home_label:can_parse(directory)
return directory:sub(1, home:len()) == home
end
function home_label:parse(directory)
local list, opts = self:defer(directory)
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

View File

@@ -0,0 +1,56 @@
--[[
An addon for mpv-file-browser which stores the last opened directory and
sets it as the opened directory the next time mpv is opened.
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 state_file = mp.command_native({'expand-path', '~~state/last_opened_directory'})
msg.verbose('using', state_file)
local function write_directory(directory)
local file = io.open(state_file, 'w+')
if not file then return msg.error('could not open', state_file, 'for writing') end
directory = directory or fb.get_directory()
msg.verbose('writing', directory, 'to', state_file)
file:write(directory)
file:close()
end
local addon = {
api_version = '1.7.0',
priority = 0,
}
function addon:setup()
local file = io.open(state_file, "r")
if not file then
return msg.error('failed to open', state_file, 'for reading')
end
local dir = file:read("*a")
msg.verbose('setting default directory to', dir)
fb.browse_directory(dir, false)
file:close()
end
function addon:can_parse(dir, parse_state)
if parse_state.source == 'browser' then write_directory(dir) end
return false
end
function addon:parse()
return nil
end
mp.register_event('shutdown', function() write_directory() end)
return addon

View File

@@ -0,0 +1,57 @@
--[[
An addon for mpv-file-browser which uses the Linux ls 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 ls = {
priority = 109,
api_version = "1.1.0",
name = "ls",
keybind_name = "file"
}
local function command(args, parse_state)
local _, cmd = parse_state:yield(
mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args
}, fb.coroutine.callback())
)
return cmd.status == 0 and cmd.stdout or nil
end
function ls:can_parse(directory)
return directory ~= '' and not fb.get_protocol(directory)
end
function ls:parse(directory, parse_state)
local list = {}
local files = command({"ls", "-1", "-p", "-A", "-N", "--zero", directory}, parse_state)
if not files then return nil end
for str in files:gmatch("%Z+") do
local is_dir = str:sub(-1) == "/"
msg.trace(str)
if is_dir and fb.valid_dir(str) then
table.insert(list, {name = str, type = "dir"})
elseif fb.valid_file(str) then
table.insert(list, {name = str, type = "file"})
end
end
return list, {filtered = true}
end
return ls

View File

@@ -0,0 +1,54 @@
--[[
An addon for mpv-file-browser which adds support for m3u playlists
If the first entry of a playlist isn't working it is because some playlists are created with random invisible unicode in the first line
Vim makes it easy to detect these
This addon requires that my API mpv-read-file be available in ~~/script-modules/
https://github.com/CogentRedTester/mpv-read-file
]]--
local rf = require "read-file"
local m3u = {
priority = 100,
api_version = "1.0.0",
name = "m3u"
}
local full_paths = {}
function m3u:setup()
self.register_parseable_extension("m3u")
self.register_parseable_extension("m3u8")
end
function m3u:can_parse(directory)
return directory:find("m3u8?/?$")
end
function m3u:parse(directory)
directory = directory:gsub("/$", "")
local list = {}
local path = full_paths[ directory ] or directory
local playlist = rf.get_file_handler( path )
--if we can't read the path then stop here
if not playlist then return {}, {sorted = true, filtered = true, empty_text = "Could not read filepath"} end
local parent = self.fix_path(path:match("^(.+/[^/]+)/"), true)
local lines = playlist:read("*a")
for item in lines:gmatch("[^%c]+") do
item = self.fix_path(item)
local fullpath = self.join_path(parent, item)
local name = ( self.get_protocol(item) and item or fullpath:match("([^/]+)/?$") )
table.insert(list, {name = name, path = fullpath, type = "file"})
end
return list, {filtered = true, sorted = true}
end
return m3u

View File

@@ -0,0 +1,81 @@
--[[
An addon for mpv-file-browser which uses powershell commands to parse native directories
This is slower than the default parser for local drives, but faster for network drives
The drive_letters array below is used to list the drives to use this parser for
]]--
--list the drive letters to use here (case sensitive)
local drive_letters = {
"Y", "Z"
}
local mp = require "mp"
local msg = require "mp.msg"
local fb = require "file-browser"
local wn = {
priority = 109,
api_version = "1.1.0",
name = "powershell",
keybind_name = "file"
}
local drives = {}
for _, letter in ipairs(drive_letters) do
drives[letter] = true
end
local function command(args, parse_state)
local _, cmd = parse_state:yield(
mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args
}, fb.coroutine.callback())
)
return cmd.status == 0 and cmd.stdout or nil, cmd.stderr
end
function wn:can_parse(directory)
return directory ~= '' and not self.get_protocol(directory) and drives[ directory:sub(1,1) ]
end
function wn:parse(directory, parse_state)
local list = {}
local files, err = command({"powershell", "-noprofile", "-command", [[
$dirs = Get-ChildItem -LiteralPath ]]..string.format("%q", directory)..[[ -Directory
$files = Get-ChildItem -LiteralPath ]]..string.format("%q", directory)..[[ -File
foreach ($n in $dirs.Name) {
$n += "/"
$u8clip = [System.Text.Encoding]::UTF8.GetBytes($n)
[Console]::OpenStandardOutput().Write($u8clip, 0, $u8clip.Length)
Write-Host ""
}
foreach ($n in $files.Name) {
$u8clip = [System.Text.Encoding]::UTF8.GetBytes($n)
[Console]::OpenStandardOutput().Write($u8clip, 0, $u8clip.Length)
Write-Host ""
}
]]}, parse_state)
if not files then msg.debug(err) ; return nil end
for str in files:gmatch("[^\n\r]+") do
local is_dir = str:sub(-1) == "/"
if is_dir and self.valid_dir(str) then
table.insert(list, {name = str, type = "dir"})
elseif self.valid_file(str) then
table.insert(list, {name = str, type = "file"})
end
end
return self.sort(list), {filtered = true, sorted = true}
end
return wn

View File

@@ -0,0 +1,54 @@
--[[
An addon that loads root items from a `~~/script-opts/file-browser-root.json` file.
The contents of this file will override the root script-opt.
The json file takes the form of a list array as defined by the addon API:
https://github.com/CogentRedTester/mpv-file-browser/blob/master/addons/addons.md#the-list-array
The main purpose of this addon is to allow for users to customise the appearance of their root items
using the label or ass fields:
[
{ "name": "Favourites/" },
{ "label": "~/", "name": "C:/Users/User/" },
{ "label": "1TB HDD", "name": "D:/" },
{ "ass": "{\\c&H007700&}Green Text", "name": "E:/" },
{ "label": "FTP Server", name: "ftp://user:password@server.com/" }
]
Make sure local directories always end with `/`.
`path` and `name` behave the same in the root but either name or label should have a value.
ASS styling codes: https://aegi.vmoe.info/docs/3.0/ASS_Tags/
]]
local mp = require 'mp'
local msg = require 'mp.msg'
local utils = require 'mp.utils'
local fb = require 'file-browser'
-- loads the root json file
local config_path = mp.command_native({'expand-path', '~~/script-opts/file-browser-root.json'})
local file = io.open(config_path, 'r')
if not file then
msg.error('failed to read file', config_path)
return
end
local root_config = utils.parse_json(file:read("*a"))
if not root_config then
msg.error('failed to parse contents of', config_path, '- Check the syntax is correct.')
return
end
local function setup()
for i, item in ipairs(root_config) do
fb.register_root_item(item, item.priority)
end
end
return {
api_version = '1.4.0',
setup = setup,
priority = -1000,
}

View File

@@ -0,0 +1,35 @@
--[[
An addon for file-browser which decodes URLs so that they are more readable
]]
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
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)
end
function urldecode:parse(directory)
local list, opts = self:defer(directory)
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

View File

@@ -0,0 +1,97 @@
--[[
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"
--this is a LuaJit module this addon will not load if not using LuaJit
local ffi = require 'ffi'
ffi.cdef([[
int __stdcall WideCharToMultiByte(unsigned int CodePage, unsigned int dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, int cbMultiByte, const char *lpDefaultChar, bool *lpUsedDefaultChar);
]])
--converts a UTF16 string to a UTF8 string
--this function was adapted from https://github.com/mpv-player/mpv/issues/10139#issuecomment-1117954648
local function utf8(WideCharStr)
WideCharStr = ffi.cast("wchar_t*", WideCharStr)
if not WideCharStr then return nil end
local utf8_size = ffi.C.WideCharToMultiByte(65001, 0, WideCharStr, -1, nil, 0, nil, nil) --CP_UTF8
if utf8_size > 0 then
local utf8_path = ffi.new("char[?]", utf8_size)
local utf8_size = ffi.C.WideCharToMultiByte(65001, 0, WideCharStr, -1, utf8_path, utf8_size, nil, nil)
if utf8_size > 0 then
--removes the trailing `\0` character which can break things
return ffi.string(utf8_path, utf8_size):sub(1, -2)
end
end
end
local dir = {
priority = 109,
api_version = "1.1.0",
name = "cmd-dir",
keybind_name = "file"
}
local function command(args, parse_state)
local _, cmd = parse_state:yield(
mp.command_native_async({
name = "subprocess",
playback_only = false,
capture_stdout = true,
capture_stderr = true,
args = args,
}, fb.coroutine.callback() )
)
cmd.stdout = utf8(cmd.stdout)
cmd.stderr = utf8(cmd.stderr)
--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
return cmd.status == 0 and cmd.stdout or nil, cmd.stderr
end
function dir:can_parse(directory)
if directory == "" then return end
return not fb.get_protocol(directory)
end
function dir:parse(directory, parse_state)
local list = {}
local files, dirs, err
-- the dir command expects backslashes for our paths
directory = directory:gsub("/", "\\")
dirs, err = command({ "cmd", "/U", "/c", "dir", "/b", "/ad", directory }, parse_state)
if not dirs then return msg.error(err) end
files, err = command({ "cmd", "/U", "/c", "dir", "/b", "/a-d", directory }, parse_state)
if not files then return msg.error(err) 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

View File

@@ -0,0 +1,52 @@
--[[
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.
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'
-- returns a list of windows drives
local function get_drives()
local result = mp.command_native({
name = 'subprocess',
playback_only = false,
capture_stdout = true,
args = {'fsutil', 'fsinfo', 'drives'}
})
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()
local drives = get_drives()
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
}
return {
api_version = '1.4.0',
setup = import_drives,
keybinds = { keybind }
}