Files
eat-it/main.lua

248 lines
6.4 KiB
Lua

-- Copyright (c) 2024 squibid, see LICENSE file for more info
local mp = require('mp')
local msg = require('mp.msg')
local utils = require('mp.utils')
local proc = require('proc')
-- load the config file
local config = dofile(mp.command_native({"expand-path", "~~/eatit-cfg.lua"}))
if not config or type(config) ~= "table" then
msg.fatal("no config provided, bailing out")
return
end
local base_dir = mp.command_native({"expand-path", "~~cache/plugins"})
local packages = {}
-- make sure the base directory exists (*nix only)
if utils.file_info(base_dir) == nil then
proc.exec({ "mkdir", "-p", base_dir }, {}, function(err, message)
if err then
msg.fatal(string.format("failed to create plugin directory: %s", err))
return
end
end)
end
--- regiester a new package spec
---@param spec table package spec from config
---@return table package
local function register_pkg(spec)
if type(spec) ~= "table" then
spec = { spec }
end
local id = spec[1]
local package = packages[id]
if not package then
package = {
id = id,
exists = false,
setup = false
}
packages[id] = package
end
package.name = string.sub(package.id, string.find(package.id, "%/") + 1, #package.id)
package.url = spec.url or ("https://github.com/"..package.id..".git")
package.branch = spec.branch
package.files = spec.files
package.dir = package.files and utils.join_path(base_dir, package.name) or utils.join_path(mp.command_native({ 'expand-path', "~~/scripts" }), package.name)
package.pin = spec.pin
package.exists = utils.file_info(package.dir) ~= nil
-- validate that all files have been installed
if type(package.files) == "table" then
for filename, dest in pairs(package.files) do
if not utils.file_info(utils.join_path(package.dir, filename)) or
not utils.file_info(utils.join_path(mp.command_native({ "expand-path", dest }), filename)) then
package.exists = false
break
end
end
end
package.on_setup = spec.setup
return package
end
--- run package setup
---@param package table package
local function setup_package(package)
if type(package.on_setup) ~= "function" then
return
end
local ok, err = pcall(package.on_setup, package.dir)
if not ok then
msg.warn(string.format("error when running setup on '%s': %s", package.id, err))
return
end
package.setup = true
end
--- copy all files according to package spec
---@param package table package
local function copy_files(package)
--- copy src to dest
---@param src string path to src file
---@param dest string path to dest file
local function cp(src, dest)
local i = io.open(src, 'r')
if not i then return end
local o = io.open(dest, 'w')
if not o then return end
o:write(i:read('*a'))
o:close()
i:close()
end
if type(package.files) == "table" then
for name, loc in pairs(package.files) do
local path = mp.command_native({'expand-path', loc})
local dest = utils.join_path(path, name)
local src = utils.join_path(package.dir, name)
if not utils.file_info(src) then
msg.warn(string.format("file %s not found", name))
return
end
local ok, err = pcall(cp, src, dest)
if not ok then
msg.warn(string.format("failed to copy %s: %s", name, utils.to_string(err)))
end
end
end
end
local function validate_package()
end
--- download or update package
---@param package table package
---@param cb function callback
local function sync(package, cb)
if package.exists then
if package.pin then
cb()
return
end
--- generic error
---@param err any error
local function log_err(err)
msg.error(string.format("failed to update %s; reason: %s", package.id, err))
end
-- get current head commit hash
proc.git_rev_parse(package.dir, "HEAD", function(err, before)
if err then
log_err(before)
cb(err)
return
end
-- get the latest commit hash
proc.git_fetch(package.dir, "origin", package.branch or "HEAD", function(err, message)
if err then
log_err(message)
cb(err)
return
end
-- check the latest and current against eachother
proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after)
if err then
log_err(after)
cb(err)
return
elseif before == after then
msg.info(string.format("skipped %s", package.id))
cb(err)
return
end
-- switch HEAD to new commit
proc.git_reset(package.dir, after, function(err, message)
if err then
log_err(message)
return
end
setup_package(package)
copy_files(package)
msg.info(string.format("updated %s; %s -> %s", package.id, before, after))
cb(err)
end)
end)
end)
end)
else
-- clone repo since it doesn't exist
proc.git_clone(package.dir, package.url, package.branch, function(err, message)
if err then
msg.error(string.format("failed to install %s; reason: %s", package.id, utils.to_string(message)))
else
setup_package(package)
copy_files(package)
package.exists = true
msg.info(string.format("installed %s", package.id))
end
cb(err)
end)
end
end
--- sync a list of plugins
---@param list table list of packages
---@param cb function callback
local function sync_list(list, cb)
local progress = 0
for i in pairs(list) do
sync(list[i], function()
progress = progress + 1
cb()
end)
end
end
--- check if package spec should be synced
---@param package table package
---@return boolean
local function should_sync(package)
if config.sync == "new" or config.sync == nil then
return not package.exists
else
return config.sync == "always"
end
end
-- register all packages
for i = 1, #config do
local ok, err = pcall(register_pkg, config[i])
if not ok then
msg.warn(string.format("%s: %s", err, config[i].as))
end
end
-- check for package updates
local targets = {}
for i in pairs(packages) do
if should_sync(packages[i]) then
targets[#targets + 1] = packages[i]
end
end
sync_list(targets, function() end)
-- register script message for keybinding
mp.register_script_message("eatit-sync", function(name, value)
sync_list(packages, function() end)
end)