mirror of
				https://github.com/minetest-mods/skinsdb.git
				synced 2025-11-04 05:15:32 +01:00 
			
		
		
		
	remove all updater to solve CC-BY-NC-SA and Newtonsoft license issues
Fix for #19
This commit is contained in:
		
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							@@ -4,7 +4,6 @@ This Minetest mod offers changeable player skins with a graphical interface for
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
- Download scripts included for the [Minetest skin database](http://minetest.fensta.bplaced.net)
 | 
			
		||||
- Flexible skins API to manage the database
 | 
			
		||||
- [character_creator](https://github.com/minetest-mods/character_creator) support for custom skins
 | 
			
		||||
- Skin change menu for sfinv (in minetest_game) and [unified_inventory](https://forum.minetest.net/viewtopic.php?t=12767)
 | 
			
		||||
@@ -16,23 +15,8 @@ This Minetest mod offers changeable player skins with a graphical interface for
 | 
			
		||||
- Full [3d_armor](https://forum.minetest.net/viewtopic.php?t=4654) support
 | 
			
		||||
- Compatible to 1.0 and 1.8 Minecraft skins format
 | 
			
		||||
 | 
			
		||||
## Update tools
 | 
			
		||||
 | 
			
		||||
In order to download the skins from the skin database,
 | 
			
		||||
you may use one of the listed update tools below.
 | 
			
		||||
They are located in the `updater/` directory.
 | 
			
		||||
 | 
			
		||||
- `update_skins_db.sh` bash and jq required
 | 
			
		||||
- `update_from_db.py` python3 required
 | 
			
		||||
- `MT_skins_updater.*` windows or mono (?) required
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
If nothing else is specified, it is licensed as GPLv3.
 | 
			
		||||
 | 
			
		||||
Fritigern:
 | 
			
		||||
  - update_skins_db.sh (CC-BY-NC-SA 4.0)
 | 
			
		||||
## License:
 | 
			
		||||
- GPLv3
 | 
			
		||||
 | 
			
		||||
### Credits
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,200 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
//Json.NET library (http://json.codeplex.com/)
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
// MT skins updater for the skins mod
 | 
			
		||||
// Creator: Krock
 | 
			
		||||
// License: zlib (http://www.zlib.net/zlib_license.html)
 | 
			
		||||
namespace MT_skins_updater {
 | 
			
		||||
	class Program {
 | 
			
		||||
		static void Main(string[] args) {
 | 
			
		||||
			Console.WriteLine("Welcome to the MT skins updater!");
 | 
			
		||||
			Console.WriteLine("# Created by: Krock (2014-07-10)");
 | 
			
		||||
			Engine e = new Engine();
 | 
			
		||||
			Console.WriteLine(@"Path to the skins mod: (ex. 'E:\Minetest\mods\skinsdb\skins\')");
 | 
			
		||||
			string path = Console.ReadLine();
 | 
			
		||||
			Console.WriteLine("Start updating at page: ('0' to update everything)");
 | 
			
		||||
			int page = getInt(Console.ReadLine());
 | 
			
		||||
			e.Start(path, page);
 | 
			
		||||
			Console.WriteLine("Press any key to exit.");
 | 
			
		||||
			Console.ReadKey(false);
 | 
			
		||||
		}
 | 
			
		||||
		public static int getInt(string i) {
 | 
			
		||||
			int ret = 0;
 | 
			
		||||
			int.TryParse(i, out ret);
 | 
			
		||||
			return (ret > 0)? ret : 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	class Engine {
 | 
			
		||||
		string root = "http://minetest.fensta.bplaced.net";
 | 
			
		||||
		bool alternate = true; //should it use the special version of medadata saving?
 | 
			
		||||
 | 
			
		||||
		public void Start(string path, int page) {
 | 
			
		||||
			if (path.Length < 5) {
 | 
			
		||||
				Console.WriteLine("Too short path. STOP.");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (path[path.Length - 1] != '\\') {
 | 
			
		||||
				path += '\\';
 | 
			
		||||
			}
 | 
			
		||||
			if(!Directory.Exists(path + "meta")){
 | 
			
		||||
				Console.WriteLine("Folder 'meta' not found. STOP.");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if(!Directory.Exists(path + "textures")){
 | 
			
		||||
				Console.WriteLine("Folder 'textures' not found. STOP.");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			WebClient cli = new WebClient();
 | 
			
		||||
			//add useragent to identify
 | 
			
		||||
			cli.Headers.Add("User-Agent", "MT_skin_grabber 1.1");
 | 
			
		||||
			
 | 
			
		||||
			bool firstSkin = true;
 | 
			
		||||
			List<string> skin_local = new List<string>();
 | 
			
		||||
			int pages = page,
 | 
			
		||||
				updated = 0;
 | 
			
		||||
 | 
			
		||||
			for (; page <= pages; page++) {
 | 
			
		||||
				string contents = "";
 | 
			
		||||
				try {
 | 
			
		||||
					contents = cli.DownloadString(root + "/api/get.json.php?getlist&page=" + page);
 | 
			
		||||
				} catch(WebException e) { 
 | 
			
		||||
					Console.WriteLine("Whoops! Error at page ID: " + page + ". WebClient sais: " + e.Message);
 | 
			
		||||
					Console.WriteLine("Press any key to skip this page.");
 | 
			
		||||
					Console.ReadKey(false);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				Data o = JsonConvert.DeserializeObject<Data>(contents);
 | 
			
		||||
				if (o.pages != pages) {
 | 
			
		||||
					pages = o.pages;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Console.WriteLine("# Page " + page + " (" + o.per_page + " skins)");
 | 
			
		||||
				for (int i = 0; i < o.skins.Length; i++) {
 | 
			
		||||
					int id = o.skins[i].id;
 | 
			
		||||
					if(o.skins[i].type != "image/png"){
 | 
			
		||||
						Console.WriteLine("Image type '" + o.skins[i].type + "' not supported at skin ID: " + id);
 | 
			
		||||
						Console.WriteLine("Press any key to continue.");
 | 
			
		||||
						Console.ReadKey(false);
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					//eliminate special chars!
 | 
			
		||||
					o.skins[i].name = WebUtility.HtmlDecode(o.skins[i].name);
 | 
			
		||||
					o.skins[i].author = WebUtility.HtmlDecode(o.skins[i].author);
 | 
			
		||||
					
 | 
			
		||||
					//to delete old, removed skins
 | 
			
		||||
					if (firstSkin) {
 | 
			
		||||
						firstSkin = false;
 | 
			
		||||
 | 
			
		||||
						string[] files = Directory.GetFiles(path + "textures\\");
 | 
			
		||||
						for (int f = 0; f < files.Length; f++) {
 | 
			
		||||
							string[] filePath = stringSplitLast(files[f], '\\'),
 | 
			
		||||
								fileName = stringSplitLast(filePath[1], '.'),
 | 
			
		||||
								fileVer = stringSplitLast(fileName[0], '_');
 | 
			
		||||
							if (fileVer[1] == "" || fileVer[0] != "character") continue;
 | 
			
		||||
							
 | 
			
		||||
							int skinNr = Program.getInt(fileVer[1]);
 | 
			
		||||
							if (skinNr <= id) continue;
 | 
			
		||||
							skin_local.Add(fileName[0]);
 | 
			
		||||
						}
 | 
			
		||||
					} else skin_local.Remove("character_" + id);
 | 
			
		||||
					
 | 
			
		||||
					//get file size, only override changed
 | 
			
		||||
					FileInfo localImg = new FileInfo(path + "textures\\character_" + id + ".png");
 | 
			
		||||
					byte[] imageData = Convert.FromBase64String(o.skins[i].img);
 | 
			
		||||
					bool isDif = true;
 | 
			
		||||
					if (localImg.Exists) isDif = (Math.Abs(imageData.Length - localImg.Length) >= 3);
 | 
			
		||||
 | 
			
		||||
					if (isDif) {
 | 
			
		||||
						File.WriteAllBytes(localImg.FullName, imageData);
 | 
			
		||||
						imageData = null;
 | 
			
		||||
						//previews
 | 
			
		||||
						try {
 | 
			
		||||
							cli.DownloadFile(root + "/skins/1/" + id + ".png", path + "textures\\character_" + id + "_preview.png");
 | 
			
		||||
						} catch (WebException e) {
 | 
			
		||||
							Console.WriteLine("Whoops! Error at skin ID: " + id + ". WebClient sais: " + e.Message);
 | 
			
		||||
							Console.WriteLine("Press any key to continue.");
 | 
			
		||||
							Console.ReadKey(false);
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						Console.WriteLine("[SKIP] character_" + id);
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					string meta = "";
 | 
			
		||||
					if (!alternate) {
 | 
			
		||||
						meta = "name = \"" + o.skins[i].name + "\",\n";
 | 
			
		||||
						meta += "author = \"" + o.skins[i].author + "\",\n";
 | 
			
		||||
						meta += "comment = \"" + o.skins[i].license + '"';
 | 
			
		||||
					} else {
 | 
			
		||||
						meta = o.skins[i].name + '\n' + o.skins[i].author + '\n' + o.skins[i].license;
 | 
			
		||||
					}
 | 
			
		||||
					File.WriteAllText(path + "meta\\character_" + id + ".txt", meta);
 | 
			
		||||
					updated++;
 | 
			
		||||
					Console.WriteLine("[" + id + "] " + shorten(o.skins[i].name, 20) + "\t by: " + o.skins[i].author + "\t (" + o.skins[i].license + ")");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			foreach (string fileName in skin_local) {
 | 
			
		||||
				if(File.Exists(path + "textures\\" + fileName + ".png")) {
 | 
			
		||||
					File.Delete(path + "textures\\" + fileName + ".png");
 | 
			
		||||
				}
 | 
			
		||||
				if(File.Exists(path + "textures\\" + fileName + "_preview.png")) {
 | 
			
		||||
					File.Delete(path + "textures\\" + fileName + "_preview.png");
 | 
			
		||||
				}
 | 
			
		||||
				if(File.Exists(path + "meta\\" + fileName + ".txt")) {
 | 
			
		||||
					File.Delete(path + "meta\\" + fileName + ".txt");
 | 
			
		||||
				}
 | 
			
		||||
				Console.WriteLine("[DEL] " + fileName + " (deleted skin)");
 | 
			
		||||
			}
 | 
			
		||||
			Console.WriteLine("Done. Updated " + updated + " skins!");
 | 
			
		||||
		}
 | 
			
		||||
		string shorten(string inp, int len) {
 | 
			
		||||
			char[] shr = new char[len];
 | 
			
		||||
			for (int i = 0; i < len; i++) {
 | 
			
		||||
				if (i < inp.Length) {
 | 
			
		||||
					shr[i] = inp[i];
 | 
			
		||||
				} else shr[i] = ' ';
 | 
			
		||||
			}
 | 
			
		||||
			return new string(shr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		string[] stringSplitLast(string path, char limiter) {
 | 
			
		||||
			int found = 0;
 | 
			
		||||
			int totalLen = path.Length - 1;
 | 
			
		||||
			for (int i = totalLen; i >= 0; i--) {
 | 
			
		||||
				if (path[i] == limiter) {
 | 
			
		||||
					found = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (found == 0) {
 | 
			
		||||
				return new string[] { "", "" };
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			int len = totalLen - found;
 | 
			
		||||
			char[] str_1 = new char[found],
 | 
			
		||||
				str_2 = new char[len];
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < path.Length; i++) {
 | 
			
		||||
				if (i == found) continue;
 | 
			
		||||
				if (i < found) {
 | 
			
		||||
					str_1[i] = path[i];
 | 
			
		||||
				} else {
 | 
			
		||||
					str_2[i - found - 1] = path[i];
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return new string[] { new string(str_1), new string(str_2) };
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	class Data {
 | 
			
		||||
		public Skins_data[] skins;
 | 
			
		||||
		public int page, pages, per_page;
 | 
			
		||||
	}
 | 
			
		||||
	class Skins_data {
 | 
			
		||||
		public string name, author, uploaded, type, license, img;
 | 
			
		||||
		public int id, license_id;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,180 +0,0 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
from http.client import HTTPConnection,HTTPException,BadStatusLine,_CS_IDLE
 | 
			
		||||
import json
 | 
			
		||||
import base64
 | 
			
		||||
from contextlib import closing
 | 
			
		||||
import sys,os,shutil,time
 | 
			
		||||
 | 
			
		||||
def die(message,code=23):
 | 
			
		||||
		print(message,file=sys.stderr)
 | 
			
		||||
		raise SystemExit(code)
 | 
			
		||||
 | 
			
		||||
server = "minetest.fensta.bplaced.net"
 | 
			
		||||
skinsdir = "../textures/"
 | 
			
		||||
metadir = "../meta/"
 | 
			
		||||
curskin = 0
 | 
			
		||||
curpage = 1
 | 
			
		||||
pages = None
 | 
			
		||||
 | 
			
		||||
def replace(location,base,encoding=None,path=None):
 | 
			
		||||
	if path is None:
 | 
			
		||||
		path = os.path.join(location,base)
 | 
			
		||||
	mode = "wt" if encoding else "wb"
 | 
			
		||||
	# an unpredictable temp name only needed for a+rwxt directories
 | 
			
		||||
	tmp = os.path.join(location,'.'+base+'-tmp')
 | 
			
		||||
	def deco(handle):
 | 
			
		||||
		with open(tmp,mode,encoding=encoding) as out:
 | 
			
		||||
			handle(out)
 | 
			
		||||
		os.rename(tmp,path)
 | 
			
		||||
	return deco
 | 
			
		||||
 | 
			
		||||
def maybeReplace(location,base,encoding=None):
 | 
			
		||||
	def deco(handle):
 | 
			
		||||
		path = os.path.join(location,base)
 | 
			
		||||
		if os.path.exists(path): return
 | 
			
		||||
		return replace(location,base,encoding=encoding,path=path)(handle)
 | 
			
		||||
	return deco
 | 
			
		||||
 | 
			
		||||
class Penguin:
 | 
			
		||||
	"idk"
 | 
			
		||||
	def __init__(self, url, recv, diemessage):
 | 
			
		||||
		self.url = url
 | 
			
		||||
		self.recv = recv
 | 
			
		||||
		self.diemessage = diemessage		
 | 
			
		||||
 | 
			
		||||
class Pipeline(list):
 | 
			
		||||
	"Gawd why am I being so elaborate?"
 | 
			
		||||
	def __init__(self, threshold=10):
 | 
			
		||||
		"threshold is how many requests in parallel to pipeline"
 | 
			
		||||
		self.threshold = threshold
 | 
			
		||||
		self.sent = True
 | 
			
		||||
	def __enter__(self):
 | 
			
		||||
		self.reopen()
 | 
			
		||||
		return self
 | 
			
		||||
	def __exit__(self,typ,exn,trace):
 | 
			
		||||
		self.send()
 | 
			
		||||
		self.drain()
 | 
			
		||||
	def reopen(self):
 | 
			
		||||
		self.c = HTTPConnection(server)
 | 
			
		||||
		self.send()
 | 
			
		||||
	def append(self,url,recv,diemessage):
 | 
			
		||||
		self.sent = False
 | 
			
		||||
		super().append(Penguin(url,recv,diemessage))
 | 
			
		||||
		if len(self) > self.threshold:			
 | 
			
		||||
			self.send()
 | 
			
		||||
			self.drain()
 | 
			
		||||
	def trydrain(self):		
 | 
			
		||||
		for penguin in self:
 | 
			
		||||
			print('drain',penguin.url)
 | 
			
		||||
			try:
 | 
			
		||||
				penguin.response.begin()
 | 
			
		||||
				penguin.recv(penguin.response)
 | 
			
		||||
			except BadStatusLine as e:
 | 
			
		||||
				print('derped requesting',penguin.url)
 | 
			
		||||
				return False			
 | 
			
		||||
			except HTTPException as e:
 | 
			
		||||
				die(penguin.diemessage+' '+repr(e)+' (url='+penguin.url+')')
 | 
			
		||||
		self.clear()
 | 
			
		||||
		return True
 | 
			
		||||
	def drain(self):
 | 
			
		||||
		print('draining pipeline...',len(self))
 | 
			
		||||
		assert self.sent, "Can't drain without sending the requests!"
 | 
			
		||||
		self.sent = False
 | 
			
		||||
		while self.trydrain() is not True:
 | 
			
		||||
			self.c.close()
 | 
			
		||||
			print('drain failed, trying again')
 | 
			
		||||
			time.sleep(1)
 | 
			
		||||
			self.reopen()
 | 
			
		||||
	def trysend(self):
 | 
			
		||||
		for penguin in pipeline:
 | 
			
		||||
			print('fill',penguin.url)
 | 
			
		||||
			try:
 | 
			
		||||
				self.c.request("GET", penguin.url)
 | 
			
		||||
				self.c._HTTPConnection__state = _CS_IDLE
 | 
			
		||||
				penguin.response = self.c.response_class(self.c.sock,
 | 
			
		||||
														 method="GET")
 | 
			
		||||
				# begin LATER so we can send multiple requests w/out response headers
 | 
			
		||||
			except BadStatusLine:
 | 
			
		||||
				return False
 | 
			
		||||
			except HTTPException as e:
 | 
			
		||||
				die(diemessage+' because of a '+repr(e))
 | 
			
		||||
		return True
 | 
			
		||||
	def send(self):
 | 
			
		||||
		if self.sent: return
 | 
			
		||||
		print('filling pipeline...',len(self))
 | 
			
		||||
		while self.trysend() is not True:
 | 
			
		||||
			self.c.close()
 | 
			
		||||
			print('derped resending')
 | 
			
		||||
			time.sleep(1)
 | 
			
		||||
			self.reopen()
 | 
			
		||||
		self.sent = True
 | 
			
		||||
		
 | 
			
		||||
with Pipeline() as pipeline:
 | 
			
		||||
	# two connections is okay, right? one for json, one for preview images
 | 
			
		||||
	c = HTTPConnection(server)
 | 
			
		||||
	def addpage(page):
 | 
			
		||||
		global curskin, pages
 | 
			
		||||
		print("Page: " + str(page))
 | 
			
		||||
		r = 0
 | 
			
		||||
		try:
 | 
			
		||||
			c.request("GET", "/api/get.json.php?getlist&page=" + str(page) + "&outformat=base64")
 | 
			
		||||
			r = c.getresponse()
 | 
			
		||||
		except Exception:
 | 
			
		||||
			if r != 0:
 | 
			
		||||
				if r.status != 200:
 | 
			
		||||
					die("Error", r.status)
 | 
			
		||||
			return
 | 
			
		||||
		
 | 
			
		||||
		data = r.read().decode()
 | 
			
		||||
		l = json.loads(data)
 | 
			
		||||
		if not l["success"]:
 | 
			
		||||
			die("Success != True")
 | 
			
		||||
		r = 0
 | 
			
		||||
		pages = int(l["pages"])
 | 
			
		||||
		foundOne = False
 | 
			
		||||
		for s in l["skins"]:
 | 
			
		||||
			# make sure to increment this, even if the preview exists!
 | 
			
		||||
			curskin = curskin + 1
 | 
			
		||||
			previewbase = "character_" + str(curskin) + "_preview.png"
 | 
			
		||||
			preview = os.path.join(skinsdir, previewbase)
 | 
			
		||||
			if os.path.exists(preview):
 | 
			
		||||
				print('skin',curskin,'already retrieved')
 | 
			
		||||
				continue
 | 
			
		||||
			print('updating skin',curskin,'id',s["id"])
 | 
			
		||||
			foundOne = True
 | 
			
		||||
			@maybeReplace(skinsdir, "character_" + str(curskin) + ".png")
 | 
			
		||||
			def go(f):
 | 
			
		||||
				f.write(base64.b64decode(bytes(s["img"], 'utf-8')))
 | 
			
		||||
				f.close()
 | 
			
		||||
				
 | 
			
		||||
			@maybeReplace(metadir, "character_" + str(curskin) + ".txt",
 | 
			
		||||
						  encoding='utf-8')
 | 
			
		||||
			def go(f):
 | 
			
		||||
				f.write(str(s["name"]) + '\n')
 | 
			
		||||
				f.write(str(s["author"]) + '\n')
 | 
			
		||||
				f.write(str(s["license"]))
 | 
			
		||||
			url = "/skins/1/" + str(s["id"]) + ".png"
 | 
			
		||||
			def closure(skinsdir,previewbase,preview,s):
 | 
			
		||||
				"explanation: python sucks"
 | 
			
		||||
				def tryget(r):
 | 
			
		||||
					print('replacing',s["id"])
 | 
			
		||||
					if r.status != 200:
 | 
			
		||||
						print("Error", r.status)
 | 
			
		||||
						return
 | 
			
		||||
					@replace(skinsdir,previewbase,path=preview)
 | 
			
		||||
					def go(f):
 | 
			
		||||
						shutil.copyfileobj(r,f)
 | 
			
		||||
				return tryget
 | 
			
		||||
					
 | 
			
		||||
			pipeline.append(url,closure(skinsdir,previewbase,preview,s),
 | 
			
		||||
							"Couldn't get {} because of a".format(
 | 
			
		||||
								s["id"]))
 | 
			
		||||
		if not foundOne:
 | 
			
		||||
			print("No skins updated on this page. Seems we're done?")
 | 
			
		||||
			#raise SystemExit
 | 
			
		||||
	addpage(curpage)
 | 
			
		||||
	while pages > curpage:
 | 
			
		||||
		curpage = curpage + 1
 | 
			
		||||
		addpage(curpage)
 | 
			
		||||
	print("Skins have been updated!")
 | 
			
		||||
	
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
####
 | 
			
		||||
# Licenced under Attribution-NonCommercial-ShareAlike 4.0 International 
 | 
			
		||||
# http://creativecommons.org/licenses/by-nc-sa/4.0/
 | 
			
		||||
#### ATTENTION ####
 | 
			
		||||
## This script requires that jq and coreutils are installed on your system ##
 | 
			
		||||
## In Debian-based distros, open a terminal and run 
 | 
			
		||||
## 	sudo apt-get install jq coreutils
 | 
			
		||||
###################
 | 
			
		||||
 | 
			
		||||
# == Set variables ===
 | 
			
		||||
# ====================
 | 
			
		||||
NUMPAGES="1"	# Number of pages. Default is 1 page
 | 
			
		||||
PERPAGE="2000"  # Number of items per page. Default is 2000.
 | 
			
		||||
JSONURL="http://minetest.fensta.bplaced.net/api/get.json.php?getlist&page=$NUMPAGES&outformat=base64&per_page=$PERPAGE"	# The URL to the database
 | 
			
		||||
PREVIEWURL="http://minetest.fensta.bplaced.net/skins/1/"	# The url to the location of the previews.
 | 
			
		||||
curpath="$(dirname $0)"		# all path are relative to this script place
 | 
			
		||||
temp="$curpath"/tmp			# Where the temp folder will be. Default is $PWD/tmp, which means that the tmp folder will be put in the current folder
 | 
			
		||||
METADEST="$curpath"/../meta		# This is the folder where the meta data will be saved
 | 
			
		||||
TEXTUREDEST="$curpath"/../textures	# This is the folder where the skins and the previews will be saved
 | 
			
		||||
 | 
			
		||||
# === Make a bunch of folders and download the db ===
 | 
			
		||||
# ===================================================
 | 
			
		||||
if [ -d "$temp" ]; then
 | 
			
		||||
    rm -r $temp				# If the temp dir exists we will remove it and its contents.
 | 
			
		||||
fi
 | 
			
		||||
mkdir "$temp"				# Make a new temp dir. Redundant? No. We will get rid of it later.
 | 
			
		||||
 | 
			
		||||
if [ ! -d "$METADEST" ]; then		# Check to see if the meta dir exists, and if not, create it
 | 
			
		||||
  mkdir "$METADEST"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ ! -d "$TEXTUREDEST" ]; then	# Check to see if the textures dir exists, and if not, create it
 | 
			
		||||
  mkdir "$TEXTUREDEST"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
wget "$JSONURL" -O "$temp"/rawdb.txt	# Download the entire database
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# === Do the JSON thing ===
 | 
			
		||||
# =========================
 | 
			
		||||
i="0" 	# This will be the counter.
 | 
			
		||||
while true; do
 | 
			
		||||
   ID=$(cat "$temp"/rawdb.txt | jq ".skins[$i].id")
 | 
			
		||||
   if [ "$ID" == "null" ]; then
 | 
			
		||||
       break
 | 
			
		||||
   fi
 | 
			
		||||
 | 
			
		||||
   if [ ! -f "$METADEST"/character_$ID.txt ] || [ "$1" == "all" ]; then
 | 
			
		||||
      # The next lines are kinda complex. sed is being used to strip the quotes from the variables. I had help...
 | 
			
		||||
      meta_name="$(jq ".skins[$i].name" < "$temp"/rawdb.txt | sed 's/^"//;s/"$//')"
 | 
			
		||||
      meta_author="$(jq ".skins[$i].author" <"$temp"/rawdb.txt | sed 's/^"//;s/"$//')"
 | 
			
		||||
      meta_license="$(jq ".skins[$i].license" <"$temp"/rawdb.txt | sed 's/^"//;s/"$//')"
 | 
			
		||||
 | 
			
		||||
      echo "# $ID name: $meta_name author: $meta_author license: $meta_license"  # Verbosity to show that the script is working.
 | 
			
		||||
 | 
			
		||||
      echo "$meta_name" > "$METADEST"/character_$ID.txt			# Save the meta data to files, this line overwrites the data inside the file
 | 
			
		||||
      echo "$meta_author"  >> "$METADEST"/character_$ID.txt		# Save the meta data to files, this line is added to the file
 | 
			
		||||
      echo "$meta_license" >> "$METADEST"/character_$ID.txt		# Save the meta data to files, and this line is added to the file as well.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      # === Extract and save the image from the JSON file ===
 | 
			
		||||
      # ======================================================
 | 
			
		||||
      skin=$(jq ".skins[$i].img" < "$temp"/rawdb.txt | sed 's/^"//;s/"$//') 	# Strip the quotes from the base64 encoded string
 | 
			
		||||
      echo "$skin" | base64 --decode > "$TEXTUREDEST"/character_"$ID".png	# Decode the string, and save it as a .png file
 | 
			
		||||
 | 
			
		||||
      # === Download a preview image whilst we're at it ===
 | 
			
		||||
      # ====================================================
 | 
			
		||||
      wget -nv "$PREVIEWURL/$ID".png -O "$TEXTUREDEST"/character_"$ID"_preview.png	# Downloads a preview of the skin that we just saved.
 | 
			
		||||
   else
 | 
			
		||||
      echo -n "."
 | 
			
		||||
   fi
 | 
			
		||||
   i=$[$i+1] 	# Increase the counter by one.
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# === Now we'll clean up the mess ===
 | 
			
		||||
# ===================================
 | 
			
		||||
rm -r "$temp"	# Remove the temp dir and its contents.
 | 
			
		||||
 | 
			
		||||
exit # Not strictly needed, but i like to use it to wrap things up.
 | 
			
		||||
		Reference in New Issue
	
	Block a user