mirror of
https://github.com/minetest-mods/skinsdb.git
synced 2024-12-23 07:50:18 +01:00
312780c82e
Supersedes the 'fsep' setting by automatically detecting the texture name in both, the skin list and the updater scripts. New, automatically fetched skins will now always use the '.' delimiter to avoid player name issues. In case of ambiguous texture names, a warning is logged.
152 lines
4.2 KiB
Lua
152 lines
4.2 KiB
Lua
-- Skins update script
|
|
|
|
local S = minetest.get_translator("skinsdb")
|
|
local _ID_ = "Lua Skins Updater"
|
|
|
|
local internal = {}
|
|
internal.errors = {}
|
|
|
|
-- Binary downloads are required
|
|
if not core.features.httpfetch_binary_data then
|
|
internal.errors[#internal.errors + 1] =
|
|
"Feature 'httpfetch_binary_data' is missing. Update Minetest."
|
|
end
|
|
|
|
-- Insecure environment for saving textures and meta
|
|
local ie, http = skins.ie, skins.http
|
|
if not ie or not http then
|
|
internal.errors[#internal.errors + 1] = "Insecure environment is required. " ..
|
|
"Please add skinsdb to `secure.trusted_mods` in minetest.conf"
|
|
end
|
|
|
|
minetest.register_chatcommand("skinsdb_download_skins", {
|
|
params = S("<skindb start page> <amount of pages>"),
|
|
description = S("Downloads the specified range of skins and shuts down the server"),
|
|
privs = {server=true},
|
|
func = function(name, param)
|
|
if #internal.errors > 0 then
|
|
return false, "Cannot run " .. _ID_ .. ":\n\t" ..
|
|
table.concat(internal.errors, "\n\t")
|
|
end
|
|
|
|
local parts = string.split(param, " ")
|
|
local start = tonumber(parts[1])
|
|
local len = tonumber(parts[2])
|
|
if not (start and len and len > 0) then
|
|
return false, "Invalid page number or amount of pages"
|
|
end
|
|
|
|
internal.get_pages_count(internal.fetch_function, start, len)
|
|
return true, "Started downloading..."
|
|
end,
|
|
})
|
|
|
|
|
|
if #internal.errors > 0 then
|
|
return -- Nonsense to load something that's not working
|
|
end
|
|
|
|
-- http://minetest.fensta.bplaced.net/api/apidoku.md
|
|
local root_url = "http://skinsdb.terraqueststudios.net"
|
|
local page_url = root_url .. "/api/v1/content?client=mod&page=%i" -- [1] = Page#
|
|
|
|
local download_path = skins.modpath
|
|
local meta_path = download_path .. "/meta/"
|
|
local skins_path = download_path .. "/textures/"
|
|
|
|
-- Fancy debug wrapper to download an URL
|
|
local function fetch_url(url, callback)
|
|
http.fetch({
|
|
url = url,
|
|
user_agent = _ID_
|
|
}, function(result)
|
|
if result.succeeded then
|
|
if result.code ~= 200 then
|
|
core.log("warning", ("%s: STATUS=%i URL=%s"):format(
|
|
_ID_, result.code, url))
|
|
end
|
|
return callback(result.data)
|
|
end
|
|
core.log("warning", ("%s: Failed to download URL=%s"):format(
|
|
_ID_, url))
|
|
end)
|
|
end
|
|
|
|
-- Insecure workaround since meta/ and textures/ cannot be written to
|
|
local function unsafe_file_write(path, contents)
|
|
local f = ie.io.open(path, "wb")
|
|
f:write(contents)
|
|
f:close()
|
|
end
|
|
|
|
-- Takes a valid skin table from the Skins Database and saves it
|
|
local function save_single_skin(skin)
|
|
local meta = {
|
|
skin.name,
|
|
skin.author,
|
|
skin.license
|
|
}
|
|
|
|
local name = "character." .. skin.id
|
|
do
|
|
local legacy_name = "character_" .. skin.id
|
|
local fh = ie.io.open(skins_path .. legacy_name .. ".png", "r")
|
|
-- Use the old name if either the texture ...
|
|
if fh then
|
|
name = legacy_name
|
|
end
|
|
end
|
|
|
|
-- core.safe_file_write does not work here
|
|
unsafe_file_write(
|
|
meta_path .. name .. ".txt",
|
|
table.concat(meta, "\n")
|
|
)
|
|
|
|
unsafe_file_write(
|
|
skins_path .. name .. ".png",
|
|
core.decode_base64(skin.img)
|
|
)
|
|
core.log("action", ("%s: Completed skin %s"):format(_ID_, name))
|
|
end
|
|
|
|
-- Get total pages since it'll just return the last page all over again
|
|
internal.get_pages_count = function(callback, ...)
|
|
local vars = {...}
|
|
fetch_url(page_url:format(1) .. "&per_page=1", function(data)
|
|
local list = core.parse_json(data)
|
|
-- "per_page" defaults to 20 if left away (docs say something else, though)
|
|
callback(math.ceil(list.pages / 20), unpack(vars))
|
|
end)
|
|
end
|
|
|
|
-- Function to fetch a range of pages
|
|
internal.fetch_function = function(pages_total, start_page, len)
|
|
start_page = math.max(start_page, 1)
|
|
local end_page = math.min(start_page + len - 1, pages_total)
|
|
|
|
for page_n = start_page, end_page do
|
|
local page_cpy = page_n
|
|
fetch_url(page_url:format(page_n), function(data)
|
|
core.log("action", ("%s: Page %i"):format(_ID_, page_cpy))
|
|
|
|
local list = core.parse_json(data)
|
|
for i, skin in pairs(list.skins) do
|
|
assert(skin.type == "image/png")
|
|
assert(skin.id ~= "")
|
|
|
|
if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb
|
|
save_single_skin(skin)
|
|
end
|
|
end
|
|
|
|
if page_cpy == end_page then
|
|
local log = _ID_ .. " finished downloading all skins. " ..
|
|
"Shutting down server to reload media cache"
|
|
core.log("action", log)
|
|
core.request_shutdown(log, true, 3 --[[give some time for pending requests]])
|
|
end
|
|
end)
|
|
end
|
|
end
|