mirror of
https://github.com/minetest-mods/skinsdb.git
synced 2024-12-25 00:40:17 +01:00
8e1e525340
The base64 data from the decoder to the file writer is in binary, using only the "w" flag would work in most unix systems but its incorrect and for all systems with crazy line_feed/Carrier_return or any character conversion the right way to save a binary data is using the "wb" flag.
148 lines
4.2 KiB
Lua
148 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 = "<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://minetest.fensta.bplaced.net"
|
|
local page_url = root_url .. "/api/v2/get.json.php?getlist&page=%i&outformat=base64" -- [1] = Page#
|
|
local preview_url = root_url .. "/skins/1/%i.png" -- [1] = ID
|
|
|
|
local mod_path = skins.modpath
|
|
local meta_path = mod_path .. "/meta/"
|
|
local skins_path = mod_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 safe_single_skin(skin)
|
|
local meta = {
|
|
skin.name,
|
|
skin.author,
|
|
skin.license
|
|
}
|
|
|
|
local name = "character_" .. skin.id
|
|
|
|
-- 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)
|
|
)
|
|
fetch_url(preview_url:format(skin.id), function(preview)
|
|
unsafe_file_write(skins_path .. name .. "_preview.png", preview)
|
|
end)
|
|
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
|
|
safe_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
|