1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
|