Support configurable extra plugin lookup path (#2693)

* plugin loader(feat): support loading plugins from user defined directories
Extra plugin lookup paths can be set in global reader setting via key
`extra_plugin_paths`. Value of the key can either be a string or an array
of strings.
* build(fix): also purge non-exist plugins on build
* plugin: migrate debug plugin to menu sorter
pull/2728/head
Qingping Hou 7 years ago committed by Frans de Jonge
parent ba74921c4b
commit 1461574894

@ -79,6 +79,9 @@ endif
@echo "[*] Install plugins" @echo "[*] Install plugins"
@# TODO: link istead of cp? @# TODO: link istead of cp?
$(RCP) plugins/* $(INSTALL_DIR)/koreader/plugins/ $(RCP) plugins/* $(INSTALL_DIR)/koreader/plugins/
@# purge deleted plugins
for d in $$(ls $(INSTALL_DIR)/koreader/plugins); do \
test -d plugins/$$d || rm -rf $(INSTALL_DIR)/koreader/plugins/$$d ; done
@echo "[*] Installresources" @echo "[*] Installresources"
$(RCP) -pL resources/fonts/* $(INSTALL_DIR)/koreader/fonts/ $(RCP) -pL resources/fonts/* $(INSTALL_DIR)/koreader/fonts/
install -d $(INSTALL_DIR)/koreader/{screenshots,data/{dict,tessdata},fonts/host,ota} install -d $(INSTALL_DIR)/koreader/{screenshots,data/{dict,tessdata},fonts/host,ota}

@ -1,42 +1,66 @@
local lfs = require("libs/libkoreader-lfs") local lfs = require("libs/libkoreader-lfs")
local logger = require("logger") local logger = require("logger")
local PluginLoader = { local DEFAULT_PLUGIN_PATH = "plugins"
plugin_path = "plugins"
} local PluginLoader = {}
function PluginLoader:loadPlugins() function PluginLoader:loadPlugins()
if self.plugins then return self.plugins end if self.plugins then return self.plugins end
self.plugins = {} self.plugins = {}
for f in lfs.dir(self.plugin_path) do local lookup_path_list = { DEFAULT_PLUGIN_PATH }
local path = self.plugin_path.."/"..f local extra_paths = G_reader_settings:readSetting("extra_plugin_paths")
local mode = lfs.attributes(path, "mode") if extra_paths then
-- valid koreader plugin directory if type(extra_paths) == "string" then
if mode == "directory" and f:find(".+%.koplugin$") then extra_paths = { extra_paths }
local mainfile = path.."/".."main.lua" end
local package_path = package.path if type(extra_paths) == "table" then
local package_cpath = package.cpath for _,extra_path in ipairs(extra_paths) do
package.path = path.."/?.lua;"..package.path local extra_path_mode = lfs.attributes(extra_path, "mode")
package.cpath = path.."/lib/?.so;"..package.cpath if extra_path_mode == "directory" and extra_path ~= DEFAULT_PLUGIN_PATH then
local ok, plugin_module = pcall(dofile, mainfile) table.insert(lookup_path_list, extra_path)
if not ok or not plugin_module then end
logger.warn("Error when loading", mainfile, plugin_module) end
elseif type(plugin_module.disabled) ~= "boolean" or not plugin_module.disabled then else
logger.err("extra_plugin_paths config only accepts string or table value")
end
end
-- keep reference to old value so they can be restored later
local package_path = package.path
local package_cpath = package.cpath
for _,lookup_path in ipairs(lookup_path_list) do
logger.info('Loading plugins from directory:', lookup_path)
for entry in lfs.dir(lookup_path) do
local plugin_root = lookup_path.."/"..entry
local mode = lfs.attributes(plugin_root, "mode")
-- valid koreader plugin directory
if mode == "directory" and entry:find(".+%.koplugin$") then
local mainfile = plugin_root.."/main.lua"
package.path = string.format("%s/?.lua;%s", plugin_root, package_path)
package.cpath = string.format("%s/lib/?.so;%s", plugin_root, package_cpath)
local ok, plugin_module = pcall(dofile, mainfile)
if not ok or not plugin_module then
logger.warn("Error when loading", mainfile, plugin_module)
elseif type(plugin_module.disabled) ~= "boolean" or not plugin_module.disabled then
plugin_module.path = plugin_root
plugin_module.name = plugin_module.name or plugin_root:match("/(.-)%.koplugin")
table.insert(self.plugins, plugin_module)
else
logger.info("Plugin ", mainfile, " has been disabled.")
end
package.path = package_path package.path = package_path
package.cpath = package_cpath package.cpath = package_cpath
plugin_module.path = path
plugin_module.name = plugin_module.name or path:match("/(.-)%.koplugin")
table.insert(self.plugins, plugin_module)
else
logger.info("Plugin ", mainfile, " has been disabled.")
end end
end end
end end
-- set package path for all loaded plugins
for _,plugin in ipairs(self.plugins) do for _,plugin in ipairs(self.plugins) do
package.path = package.path..";"..plugin.path.."/?.lua" package.path = string.format("%s;%s/?.lua", package.path, plugin.path)
package.cpath = package.cpath..";"..plugin.path.."/lib/?.so" package.cpath = string.format("%s;%s/lib/?.so", package.cpath, plugin.path)
end end
table.sort(self.plugins, function(v1,v2) return v1.path < v2.path end) table.sort(self.plugins, function(v1,v2) return v1.path < v2.path end)

@ -23,7 +23,6 @@ local order = {
"highlight_options", "highlight_options",
"change_font", "change_font",
"hyphenation", "hyphenation",
"read_timer",
}, },
setting = { setting = {
"read_from_right_to_left", "read_from_right_to_left",
@ -43,6 +42,7 @@ local order = {
"status_bar", "status_bar",
}, },
tools = { tools = {
"read_timer",
"calibre_wireless_connection", "calibre_wireless_connection",
"evernote", "evernote",
"goodreads", "goodreads",

@ -1,4 +1,9 @@
local InfoMessage = require("ui/widget/infomessage") -- This is a debug plugin, remove the following if block to enable it
if true then
return { disabled = true, }
end
local InfoMessage = require("ui/widget/infomessage") -- luacheck:ignore
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext") local _ = require("gettext")
@ -6,22 +11,21 @@ local _ = require("gettext")
local Hello = WidgetContainer:new{ local Hello = WidgetContainer:new{
name = 'Hello', name = 'Hello',
is_doc_only = false, is_doc_only = false,
disabled = true, -- This is a debug plugin
} }
function Hello:init() function Hello:init()
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
end end
function Hello:addToMainMenu(tab_item_table) function Hello:addToMainMenu(menu_items)
table.insert(tab_item_table.plugins, { menu_items.hello_world = {
text = _("Hello World"), text = _("Hello World"),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = _("Hello, docless plugin world"), text = _("Hello, plugin world"),
}) })
end, end,
}) }
end end
return Hello return Hello

@ -9,7 +9,6 @@ local ConfirmBox = require("ui/widget/confirmbox")
local ImageWidget = require("ui/widget/imagewidget") local ImageWidget = require("ui/widget/imagewidget")
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
local Notification = require("ui/widget/notification") local Notification = require("ui/widget/notification")
local PluginLoader = require("pluginloader")
local Screen = require("device").screen local Screen = require("device").screen
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer") local WidgetContainer = require("ui/widget/container/widgetcontainer")
@ -165,7 +164,7 @@ function KoboLight:addToMainMenu(menu_items)
text = _("Frontlight gesture controller"), text = _("Frontlight gesture controller"),
callback = function() callback = function()
local image = ImageWidget:new{ local image = ImageWidget:new{
file = PluginLoader.plugin_path .. "/kobolight.koplugin/demo.png", file = self.path .. "/demo.png",
height = Screen:getHeight(), height = Screen:getHeight(),
width = Screen:getWidth(), width = Screen:getWidth(),
scale_factor = 0, scale_factor = 0,

@ -1,4 +1,3 @@
local Device = require("device") local Device = require("device")
local command local command

Loading…
Cancel
Save