pixelplanet/utils/areaDownload.py

209 lines
7.0 KiB
Python
Executable File

#!/usr/bin/python3
import PIL.Image
import sys, os, io
import asyncio
import aiohttp
import requests
import math
apime = requests.get('https://pixelplanet.fun/api/me').json()
class Color(object):
def __init__(self, index, rgb):
self.rgb = rgb
self.index = index
class EnumColorPixelplanet:
ENUM = []
def getColors(canvasid):
colors = apime['canvases'][canvasid]['colors']
for i, color in enumerate(colors):
EnumColorPixelplanet.ENUM.append(Color(i, tuple(color)))
@staticmethod
def index(i):
for color in EnumColorPixelplanet.ENUM:
if i == color.index:
return color
return EnumColorPixelplanet.ENUM[0]
class Matrix:
def __init__(self):
self.start_x = None
self.start_y = None
self.width = None
self.height = None
self.matrix = {}
def add_coords(self, x, y, w, h):
if self.start_x is None or self.start_x > x:
self.start_x = x
if self.start_y is None or self.start_y > y:
self.start_y = y
end_x_a = x + w
end_y_a = y + h
if self.width is None or self.height is None:
self.width = w
self.height = h
else:
end_x_b = self.start_x + self.width
end_y_b = self.start_y + self.height
self.width = max(end_x_b, end_x_a) - self.start_x
self.height = max(end_y_b, end_y_a) - self.start_y
def create_image(self, filename = None):
img = PIL.Image.new('RGBA', (self.width, self.height), (255, 0, 0, 0))
pxls = img.load()
for x in range(self.width):
for y in range(self.height):
try:
color = self.matrix[x + self.start_x][y + self.start_y].rgb
pxls[x, y] = color
except (IndexError, KeyError, AttributeError):
pass
if filename is not None:
if filename == 'b':
b = io.BytesIO()
img.save(b, "PNG")
b.seek(0)
return b
else:
img.save(filename)
else:
img.show()
img.close()
def set_pixel(self, x, y, color):
if x >= self.start_x and x < (self.start_x + self.width) and y >= self.start_y and y < (self.start_y + self.height):
if x not in self.matrix:
self.matrix[x] = {}
self.matrix[x][y] = color
async def fetch(session, canvasID, canvasoffset, ix, iy, target_matrix):
url = f'https://pixelplanet.fun/chunks/{canvasID}/{ix}/{iy}.bmp'
attempts = 0
while True:
try:
async with session.get(url) as resp:
data = await resp.read()
offset = int(-canvasoffset * canvasoffset / 2)
off_x = ix * 256 + offset
off_y = iy * 256 + offset
if len(data) == 0:
clr = EnumColorPixelplanet.index(0)
for i in range(256*256):
tx = off_x + i % 256
ty = off_y + i // 256
target_matrix.set_pixel(tx, ty, clr)
else:
i = 0
for b in data:
tx = off_x + i % 256
ty = off_y + i // 256
bcl = b & 0x7F
target_matrix.set_pixel(tx, ty, EnumColorPixelplanet.index(bcl))
i += 1
print(f"Loaded {url} with {i} pixels")
break
except:
if attempts > 3:
raise
attempts += 1
pass
async def get_area(canvasID, x, y, w, h):
target_matrix = Matrix()
target_matrix.add_coords(x, y, w, h)
canvasoffset = math.pow(apime["canvases"][f"{canvasID}"]["size"], 0.5)
offset = int(-canvasoffset * canvasoffset / 2)
xc = (x - offset) // 256
wc = (x + w - offset) // 256
yc = (y - offset) // 256
hc = (y + h - offset) // 256
print(f"Loading from {xc} / {yc} to {wc + 1} / {hc + 1} PixelGetter")
tasks = []
async with aiohttp.ClientSession() as session:
for iy in range(yc, hc + 1):
for ix in range(xc, wc + 1):
tasks.append(fetch(session, canvasID, canvasoffset, ix, iy, target_matrix))
await asyncio.gather(*tasks)
return target_matrix
def validateCoorRange(ulcoor: str, brcoor: str, canvasSize: int): # stolen from hf with love
if not ulcoor or not brcoor:
return 'Not all coordinates defined'
splitCoords = ulcoor.strip().split('_')
if not len(splitCoords) == 2:
return 'Invalid Coordinate Format for top-left corner'
x, y = map(lambda z: int(math.floor(float(z))), splitCoords)
splitCoords = brcoor.strip().split('_')
if not len(splitCoords) == 2:
return 'Invalid Coordinate Format for top-left corner'
u, v = map(lambda z: int(math.floor(float(z))), splitCoords)
error = None
if (math.isnan(x)):
error = 'x of top-left corner is not a valid number'
elif (math.isnan(y)):
error = 'y of top-left corner is not a valid number'
elif (math.isnan(u)):
error = 'x of bottom-right corner is not a valid number'
elif (math.isnan(v)):
error = 'y of bottom-right corner is not a valid number'
elif (u < x or v < y):
error = 'Corner coordinates are aligned wrong'
if not error is None:
return error
canvasMaxXY = canvasSize / 2
canvasMinXY = -canvasMaxXY
if (x < canvasMinXY or y < canvasMinXY or x >= canvasMaxXY or y >= canvasMaxXY):
return 'Coordinates of top-left corner are outside of canvas'
if (u < canvasMinXY or v < canvasMinXY or u >= canvasMaxXY or v >= canvasMaxXY):
return 'Coordinates of bottom-right corner are outside of canvas'
return (x, y, u, v)
if __name__ == "__main__":
if len(sys.argv) != 5:
print("Download an area of pixelplanet")
print("Usage: areaDownload.py canvasID startX_startY endX_endY filename.png")
print("(use R key on pixelplanet to copy coordinates)")
print("Canvas ID: ", end='')
for canvas in apime['canvases']:
if canvas == '2':
continue
print(f"{canvas} = {apime['canvases'][f'{canvas}']['title']}", end=' ')
print()
else:
canvasID = sys.argv[1]
if canvasID == '2':
print('Can\'t get area for 3D canvas')
sys.exit()
parseCoords = validateCoorRange(sys.argv[2], sys.argv[3], apime["canvases"][f"{canvasID}"]["size"])
if (type(parseCoords) is str):
print(parseCoords)
sys.exit()
else:
x, y, w, h = parseCoords
w = w - x + 1
h = h - y + 1
EnumColorPixelplanet.getColors(canvasID)
filename = sys.argv[4]
loop = asyncio.new_event_loop()
matrix = loop.run_until_complete(get_area(canvasID, x, y, w, h))
matrix.create_image(filename)
print("Done!")