Merge branch 'master' into translations
This commit is contained in:
commit
b374c3ad83
|
@ -223,7 +223,10 @@ function Converter() {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const canvas = canvases[selectedCanvas];
|
const canvas = canvases[selectedCanvas];
|
||||||
const {
|
const {
|
||||||
title, desc, colors, cli,
|
title,
|
||||||
|
desc,
|
||||||
|
colors,
|
||||||
|
cli = 0,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
fileDownload(
|
fileDownload(
|
||||||
printGIMPPalette(title, desc, colors.slice(cli)),
|
printGIMPPalette(title, desc, colors.slice(cli)),
|
||||||
|
|
|
@ -52,7 +52,7 @@ function generateMainPage(req) {
|
||||||
? assets[`client-${lang}`].js
|
? assets[`client-${lang}`].js
|
||||||
: assets.client.js;
|
: assets.client.js;
|
||||||
|
|
||||||
const headScript = `(function(){let x=[];window.WebSocket=class extends WebSocket{constructor(...args){super(...args);x=x.filter((w)=>w.readyState<=WebSocket.OPEN);if(x.length)window.location="https://discord.io/pixeltraaa";x.push(this)}};const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(!u||u.includes("glitch.me")||u.includes("touchedbydarkness"))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${JSON.stringify(ssvR)}');})();`;
|
const headScript = `(function(){let x=[];window.WebSocket=class extends WebSocket{constructor(...args){super(...args);x=x.filter((w)=>w.readyState<=WebSocket.OPEN);if(x.length && false)window.location="https://discord.io/pixeltraaa";x.push(this)}};const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(!u||u.includes("glitch.me")||u.includes("touchedbydarkness"))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${JSON.stringify(ssvR)}');})();`;
|
||||||
const scriptHash = createHash('sha256').update(headScript).digest('base64');
|
const scriptHash = createHash('sha256').update(headScript).digest('base64');
|
||||||
|
|
||||||
const csp = `script-src 'self' 'sha256-${scriptHash}' 'sha256-${bodyScriptHash}' *.tiktok.com *.ttwstatic.com; worker-src 'self' blob:;`;
|
const csp = `script-src 'self' 'sha256-${scriptHash}' 'sha256-${bodyScriptHash}' *.tiktok.com *.ttwstatic.com; worker-src 'self' blob:;`;
|
||||||
|
|
|
@ -30,11 +30,13 @@ Script to move canvas chunks, i.e. for resizing canvas
|
||||||
downloads an area of the canvas into a png file.
|
downloads an area of the canvas into a png file.
|
||||||
Usage: `areaDownload.py startX_startY endX_endY filename.png`
|
Usage: `areaDownload.py startX_startY endX_endY filename.png`
|
||||||
(note that you can copy the current coordinates in this format on the site by pressing R)
|
(note that you can copy the current coordinates in this format on the site by pressing R)
|
||||||
|
**Requires:** aiohttp, asyncio and PIL python3 packages
|
||||||
|
|
||||||
## historyDownload.py
|
## historyDownload.py
|
||||||
downloads the history from an canvas area between two dates.
|
downloads the history from an canvas area between two dates.
|
||||||
Useage: `historyDownload.py canvasId startX_startY endX_endY start_date end_date
|
Useage: `historyDownload.py canvasId startX_startY endX_endY start_date end_date
|
||||||
This is used for creating timelapses, see the cmd help to know how
|
This is used for creating timelapses, see the cmd help to know how
|
||||||
|
**Requires:** aiohttp, asyncio and PIL python3 packages
|
||||||
|
|
||||||
## pp-center\*.png
|
## pp-center\*.png
|
||||||
center logo of pixelplanet
|
center logo of pixelplanet
|
||||||
|
|
|
@ -1,59 +1,32 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
import sys, os, io
|
import sys, os, io, math
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
USER_AGENT = "ppfun areaDownload 1.0 " + ' '.join(sys.argv[1:])
|
||||||
|
PPFUN_URL = "https://pixelplanet.fun"
|
||||||
|
|
||||||
class Color(object):
|
class Color(object):
|
||||||
def __init__(self, index, name, rgb):
|
def __init__(self, index, rgb):
|
||||||
self.name = name
|
|
||||||
self.rgb = rgb
|
self.rgb = rgb
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
class EnumColorPixelplanet:
|
class EnumColorPixelplanet:
|
||||||
|
|
||||||
ENUM = [
|
ENUM = []
|
||||||
#Color 0 and 1 are unset colors.
|
|
||||||
#Bot adds +2 to color number. So subtract 2 from the browser inspector to match.
|
|
||||||
Color(0, 'aryan white', (255, 255, 255, 255)), #HEX FFFFFF
|
|
||||||
Color(1, 'light gray', (228, 228, 228, 255)), #HEX E4E4E4
|
|
||||||
Color(2, 'mid gray', (196, 196, 196, 255)), #HEX C4C4C4
|
|
||||||
Color(3, 'dark gray', (136, 136, 136, 255)), #HEX 888888
|
|
||||||
Color(4, 'darker gray', (78, 78, 78, 255)), #HEX 4E4E4E
|
|
||||||
Color(5, 'black', (0, 0, 0, 255)), #HEX 000000
|
|
||||||
Color(6, 'light peach', (244, 179, 174, 255)), #HEX F4B3AE
|
|
||||||
Color(7, 'light pink', (255, 167, 209, 255)), #HEX FFA7D1
|
|
||||||
Color(8, 'pink', (255, 84, 178, 255)), #HEX FF54B2
|
|
||||||
Color(9, 'peach', (255, 101, 101, 255)), #HEX FF6565
|
|
||||||
Color(10, 'windmill red', (229, 0, 0, 255)), #HEX E50000
|
|
||||||
Color(11, 'blood red', (154, 0, 0, 255)), #HEX 9A0000
|
|
||||||
Color(12, 'orange', (254, 164, 96, 255)), #HEX FEA460
|
|
||||||
Color(13, 'light brown', (229, 149, 0, 255)), #HEX E59500
|
|
||||||
Color(14, 'brazil skin', (160, 106, 66, 255)), #HEX A06A42
|
|
||||||
Color(15, 'nig skin', (96, 64, 40, 255)), #HEX 604028
|
|
||||||
Color(16, 'normal skin', (245, 223, 176, 255)), #HEX FEDFB0
|
|
||||||
Color(17, 'yellow', (255, 248, 137, 255)), #HEX FFF889
|
|
||||||
Color(18, 'dark yellow', (229, 217, 0, 255)), #HEX E5D900
|
|
||||||
Color(19, 'light green', (148, 224, 68, 255)), #HEX 94E044
|
|
||||||
Color(20, 'green', (2, 190, 1, 255)), #HEX 02BE01
|
|
||||||
Color(21, 'dark green', (104, 131, 56, 255)), #HEX 688338
|
|
||||||
Color(22, 'darker green', (0, 101, 19, 255)), #HEX 006513
|
|
||||||
Color(23, 'sky blew', (202, 227, 255, 255)), #HEX CAE3FF
|
|
||||||
Color(24, 'lite blew', (0, 211, 221, 255)), #HEX 00D3DD
|
|
||||||
Color(25, 'dark blew', (0, 131, 199, 255)), #HEX 0083C7
|
|
||||||
Color(26, 'blew', (0, 0, 234, 255)), #HEX 0000EA
|
|
||||||
Color(27, 'darker blew', (25, 25, 115, 255)), #HEX 191973
|
|
||||||
Color(28, 'light violette', (207, 110, 228, 255)), #HEX CF6EE4
|
|
||||||
Color(29, 'violette', (130, 0, 128, 255)) #HEX 820080
|
|
||||||
]
|
|
||||||
|
|
||||||
|
def getColors(canvas):
|
||||||
|
colors = canvas['colors']
|
||||||
|
for i, color in enumerate(colors):
|
||||||
|
EnumColorPixelplanet.ENUM.append(Color(i, tuple(color)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def index(i):
|
def index(i):
|
||||||
for color in EnumColorPixelplanet.ENUM:
|
for color in EnumColorPixelplanet.ENUM:
|
||||||
if i == color.index:
|
if i == color.index:
|
||||||
return color
|
return color
|
||||||
# White is default color
|
|
||||||
return EnumColorPixelplanet.ENUM[0]
|
return EnumColorPixelplanet.ENUM[0]
|
||||||
|
|
||||||
class Matrix:
|
class Matrix:
|
||||||
|
@ -108,77 +81,166 @@ class Matrix:
|
||||||
self.matrix[x] = {}
|
self.matrix[x] = {}
|
||||||
self.matrix[x][y] = color
|
self.matrix[x][y] = color
|
||||||
|
|
||||||
async def fetch(session, ix, iy, target_matrix):
|
async def fetchMe():
|
||||||
url = 'https://pixelplanet.fun/chunks/0/%s/%s.bmp' % (ix, iy)
|
url = f"{PPFUN_URL}/api/me"
|
||||||
|
headers = {
|
||||||
|
'User-Agent': USER_AGENT
|
||||||
|
}
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
attempts = 0
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
async with session.get(url, headers=headers) as resp:
|
||||||
|
data = await resp.json()
|
||||||
|
return data
|
||||||
|
except:
|
||||||
|
if attempts > 3:
|
||||||
|
print(f"Could not get {url} in three tries, cancelling")
|
||||||
|
raise
|
||||||
|
attempts += 1
|
||||||
|
print(f"Failed to load {url}, trying again in 5s")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def fetch(session, canvas_id, canvasoffset, ix, iy, target_matrix):
|
||||||
|
url = f"{PPFUN_URL}/chunks/{canvas_id}/{ix}/{iy}.bmp"
|
||||||
|
headers = {
|
||||||
|
'User-Agent': USER_AGENT
|
||||||
|
}
|
||||||
attempts = 0
|
attempts = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
async with session.get(url) as resp:
|
async with session.get(url, headers=headers) as resp:
|
||||||
data = await resp.read()
|
data = await resp.read()
|
||||||
offset = int(-256 * 256 / 2)
|
offset = int(-canvasoffset * canvasoffset / 2)
|
||||||
off_x = ix * 256 + offset
|
off_x = ix * 256 + offset
|
||||||
off_y = iy * 256 + offset
|
off_y = iy * 256 + offset
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
clr = EnumColorPixelplanet.index(23)
|
clr = EnumColorPixelplanet.index(0)
|
||||||
for i in range(256*256):
|
for i in range(256*256):
|
||||||
tx = off_x + i % 256
|
tx = off_x + i % 256
|
||||||
ty = off_y + i // 256
|
ty = off_y + i // 256
|
||||||
target_matrix.set_pixel(tx, ty, clr)
|
target_matrix.set_pixel(tx, ty, clr)
|
||||||
else:
|
else:
|
||||||
c = 0
|
|
||||||
i = 0
|
i = 0
|
||||||
for b in data:
|
for b in data:
|
||||||
tx = off_x + i % 256
|
tx = off_x + i % 256
|
||||||
ty = off_y + i // 256
|
ty = off_y + i // 256
|
||||||
bcl = b & 0x7F
|
bcl = b & 0x7F
|
||||||
if bcl == 0:
|
target_matrix.set_pixel(tx, ty, EnumColorPixelplanet.index(bcl))
|
||||||
c = 23
|
|
||||||
elif bcl == 1:
|
|
||||||
c = 0
|
|
||||||
else:
|
|
||||||
c = bcl - 2;
|
|
||||||
target_matrix.set_pixel(tx, ty, EnumColorPixelplanet.index(c))
|
|
||||||
i += 1
|
i += 1
|
||||||
print("Loaded %s with %s pixels" % (url, i))
|
print(f"Loaded {url} with {i} pixels")
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
if attempts > 3:
|
if attempts > 3:
|
||||||
|
print(f"Could not get {url} in three tries, cancelling")
|
||||||
raise
|
raise
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
print(f"Failed to load {url}, trying again in 3s")
|
||||||
|
await asyncio.sleep(3)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def get_area(x, y, w, h):
|
async def get_area(canvas_id, canvas, x, y, w, h):
|
||||||
target_matrix = Matrix()
|
target_matrix = Matrix()
|
||||||
target_matrix.add_coords(x, y, w, h)
|
target_matrix.add_coords(x, y, w, h)
|
||||||
offset = int(-256 * 256 / 2)
|
canvasoffset = math.pow(canvas['size'], 0.5)
|
||||||
|
offset = int(-canvasoffset * canvasoffset / 2)
|
||||||
xc = (x - offset) // 256
|
xc = (x - offset) // 256
|
||||||
wc = (x + w - offset) // 256
|
wc = (x + w - offset) // 256
|
||||||
yc = (y - offset) // 256
|
yc = (y - offset) // 256
|
||||||
hc = (y + h - offset) // 256
|
hc = (y + h - offset) // 256
|
||||||
print("Load from %s / %s to %s / %s" % (xc, yc, wc + 1, hc + 1), "PixelGetter")
|
print(f"Loading from {xc} / {yc} to {wc + 1} / {hc + 1} PixelGetter")
|
||||||
tasks = []
|
tasks = []
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
for iy in range(yc, hc + 1):
|
for iy in range(yc, hc + 1):
|
||||||
for ix in range(xc, wc + 1):
|
for ix in range(xc, wc + 1):
|
||||||
tasks.append(fetch(session, ix, iy, target_matrix))
|
tasks.append(fetch(session, canvas_id, canvasoffset, ix, iy, target_matrix))
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
return target_matrix
|
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)
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
apime = await fetchMe()
|
||||||
|
|
||||||
|
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("canvasID: ", end='')
|
||||||
|
for canvas_id, canvas in apime['canvases'].items():
|
||||||
|
if 'v' in canvas and canvas['v']:
|
||||||
|
continue
|
||||||
|
print(f"{canvas_id} = {canvas['title']}", end=', ')
|
||||||
|
print()
|
||||||
|
return
|
||||||
|
|
||||||
|
canvas_id = sys.argv[1]
|
||||||
|
|
||||||
|
if canvas_id not in apime['canvases']:
|
||||||
|
print("Invalid canvas selected")
|
||||||
|
return
|
||||||
|
|
||||||
|
canvas = apime['canvases'][canvas_id]
|
||||||
|
|
||||||
|
if 'v' in canvas and canvas['v']:
|
||||||
|
print("Can\'t get area for 3D canvas")
|
||||||
|
return
|
||||||
|
|
||||||
|
parseCoords = validateCoorRange(sys.argv[2], sys.argv[3], canvas['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(canvas)
|
||||||
|
filename = sys.argv[4]
|
||||||
|
|
||||||
|
matrix = await get_area(canvas_id, canvas, x, y, w, h)
|
||||||
|
matrix.create_image(filename)
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 4:
|
asyncio.run(main())
|
||||||
print("Download an area of pixelplanet")
|
|
||||||
print("Usage: areaDownload.py startX_startY endX_endY filename.png")
|
|
||||||
print("(user R key on pixelplanet to copy coordinates)")
|
|
||||||
else:
|
|
||||||
start = sys.argv[1].split('_')
|
|
||||||
end = sys.argv[2].split('_')
|
|
||||||
filename = sys.argv[3]
|
|
||||||
x = int(start[0])
|
|
||||||
y = int(start[1])
|
|
||||||
w = int(end[0]) - x + 1
|
|
||||||
h =int( end[1]) - y + 1
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
matrix = loop.run_until_complete(get_area(x, y, w, h))
|
|
||||||
matrix.create_image(filename)
|
|
||||||
print("Done!")
|
|
||||||
|
|
|
@ -5,7 +5,10 @@ import sys, io, os
|
||||||
import datetime
|
import datetime
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
|
||||||
|
USER_AGENT = "ppfun historyDownload 1.0 " + ' '.join(sys.argv[1:])
|
||||||
|
PPFUN_URL = "https://pixelplanet.fun"
|
||||||
|
PPFUN_STORAGE_URL = "https://storage.pixelplanet.fun"
|
||||||
|
|
||||||
# how many frames to skip
|
# how many frames to skip
|
||||||
# 1 means none
|
# 1 means none
|
||||||
|
@ -14,62 +17,35 @@ import json
|
||||||
# [...]
|
# [...]
|
||||||
frameskip = 1
|
frameskip = 1
|
||||||
|
|
||||||
canvases = [
|
async def fetchMe():
|
||||||
{
|
url = f"{PPFUN_URL}/api/me"
|
||||||
"canvas_name": "earth",
|
headers = {
|
||||||
"canvas_size": 256*256,
|
'User-Agent': USER_AGENT
|
||||||
"canvas_id": 0,
|
}
|
||||||
"bkg": (202, 227, 255),
|
async with aiohttp.ClientSession() as session:
|
||||||
},
|
attempts = 0
|
||||||
{
|
while True:
|
||||||
"canvas_name": "moon",
|
try:
|
||||||
"canvas_size": 16384,
|
async with session.get(url, headers=headers) as resp:
|
||||||
"canvas_id": 1,
|
data = await resp.json()
|
||||||
"bkg": (49, 46, 47),
|
return data
|
||||||
"historical_sizes" : [
|
except:
|
||||||
["20210417", 4096],
|
if attempts > 3:
|
||||||
]
|
print(f"Could not get {url} in three tries, cancelling")
|
||||||
},
|
raise
|
||||||
{
|
attempts += 1
|
||||||
},
|
print(f"Failed to load {url}, trying again in 5s")
|
||||||
{
|
await asyncio.sleep(5)
|
||||||
"canvas_name": "corona",
|
pass
|
||||||
"canvas_size": 256,
|
|
||||||
"canvas_id": 3,
|
|
||||||
"bkg": (33, 28, 15),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"canvas_name": "compass",
|
|
||||||
"canvas_size": 1024,
|
|
||||||
"canvas_id": 4,
|
|
||||||
"bkg": (196, 196, 196),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
},
|
|
||||||
{
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"canvas_name": "1bit",
|
|
||||||
"canvas_size": 256*256,
|
|
||||||
"canvas_id": 7,
|
|
||||||
"bkg": (0, 0, 0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"canvas_name": "top10",
|
|
||||||
"canvas_size": 2048,
|
|
||||||
"canvas_id": 8,
|
|
||||||
"bkg": (197, 204, 184),
|
|
||||||
"historical_sizes" : [
|
|
||||||
["20220626", 1024],
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
async def fetch(session, url, offx, offy, image, bkg, needed = False):
|
async def fetch(session, url, offx, offy, image, bkg, needed = False):
|
||||||
attempts = 0
|
attempts = 0
|
||||||
|
headers = {
|
||||||
|
'User-Agent': USER_AGENT
|
||||||
|
}
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
async with session.get(url) as resp:
|
async with session.get(url, headers=headers) as resp:
|
||||||
if resp.status == 404:
|
if resp.status == 404:
|
||||||
if needed:
|
if needed:
|
||||||
img = PIL.Image.new('RGB', (256, 256), color=bkg)
|
img = PIL.Image.new('RGB', (256, 256), color=bkg)
|
||||||
|
@ -91,11 +67,9 @@ async def fetch(session, url, offx, offy, image, bkg, needed = False):
|
||||||
attempts += 1
|
attempts += 1
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def get_area(canvas, x, y, w, h, start_date, end_date):
|
async def get_area(canvas_id, canvas, x, y, w, h, start_date, end_date):
|
||||||
canvas_data = canvases[canvas]
|
canvas_size = canvas["size"]
|
||||||
canvas_id = canvas_data["canvas_id"]
|
bkg = tuple(canvas['colors'][0])
|
||||||
canvas_size = canvas_data["canvas_size"]
|
|
||||||
bkg = canvas_data["bkg"]
|
|
||||||
|
|
||||||
delta = datetime.timedelta(days=1)
|
delta = datetime.timedelta(days=1)
|
||||||
end_date = end_date.strftime("%Y%m%d")
|
end_date = end_date.strftime("%Y%m%d")
|
||||||
|
@ -110,8 +84,8 @@ async def get_area(canvas, x, y, w, h, start_date, end_date):
|
||||||
start_date = start_date + delta
|
start_date = start_date + delta
|
||||||
|
|
||||||
fetch_canvas_size = canvas_size
|
fetch_canvas_size = canvas_size
|
||||||
if 'historical_sizes' in canvas_data:
|
if 'historicalSizes' in canvas:
|
||||||
for ts in canvas_data['historical_sizes']:
|
for ts in canvas['historicalSizes']:
|
||||||
date = ts[0]
|
date = ts[0]
|
||||||
size = ts[1]
|
size = ts[1]
|
||||||
if iter_date <= date:
|
if iter_date <= date:
|
||||||
|
@ -122,14 +96,14 @@ async def get_area(canvas, x, y, w, h, start_date, end_date):
|
||||||
wc = (x + w - offset) // 256
|
wc = (x + w - offset) // 256
|
||||||
yc = (y - offset) // 256
|
yc = (y - offset) // 256
|
||||||
hc = (y + h - offset) // 256
|
hc = (y + h - offset) // 256
|
||||||
print("Load from %s / %s to %s / %s" % (xc, yc, wc + 1, hc + 1))
|
print("Load from %s / %s to %s / %s with canvas size %s" % (xc, yc, wc + 1, hc + 1, fetch_canvas_size))
|
||||||
|
|
||||||
tasks = []
|
tasks = []
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
image = PIL.Image.new('RGBA', (w, h))
|
image = PIL.Image.new('RGBA', (w, h))
|
||||||
for iy in range(yc, hc + 1):
|
for iy in range(yc, hc + 1):
|
||||||
for ix in range(xc, wc + 1):
|
for ix in range(xc, wc + 1):
|
||||||
url = 'https://storage.pixelplanet.fun/%s/%s/%s/%s/tiles/%s/%s.png' % (iter_date[0:4], iter_date[4:6] , iter_date[6:], canvas_id, ix, iy)
|
url = '%s/%s/%s/%s/%s/tiles/%s/%s.png' % (PPFUN_STORAGE_URL, iter_date[0:4], iter_date[4:6] , iter_date[6:], canvas_id, ix, iy)
|
||||||
offx = ix * 256 + offset - x
|
offx = ix * 256 + offset - x
|
||||||
offy = iy * 256 + offset - y
|
offy = iy * 256 + offset - y
|
||||||
tasks.append(fetch(session, url, offx, offy, image, bkg, True))
|
tasks.append(fetch(session, url, offx, offy, image, bkg, True))
|
||||||
|
@ -143,10 +117,13 @@ async def get_area(canvas, x, y, w, h, start_date, end_date):
|
||||||
cnt += 1
|
cnt += 1
|
||||||
#frames.append(image.copy())
|
#frames.append(image.copy())
|
||||||
image.save('./timelapse/t%s.png' % (cnt))
|
image.save('./timelapse/t%s.png' % (cnt))
|
||||||
|
headers = {
|
||||||
|
'User-Agent': USER_AGENT
|
||||||
|
}
|
||||||
while True:
|
while True:
|
||||||
async with session.get('https://pixelplanet.fun/history?day=%s&id=%s' % (iter_date, canvas_id)) as resp:
|
async with session.get('%s/history?day=%s&id=%s' % (PPFUN_URL, iter_date, canvas_id), headers=headers) as resp:
|
||||||
try:
|
try:
|
||||||
time_list = json.loads(await resp.text())
|
time_list = await resp.json()
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
print('Couldn\'t decode json for day %s, trying again' % (iter_date))
|
print('Couldn\'t decode json for day %s, trying again' % (iter_date))
|
||||||
|
@ -162,7 +139,7 @@ async def get_area(canvas, x, y, w, h, start_date, end_date):
|
||||||
image_rel = image.copy()
|
image_rel = image.copy()
|
||||||
for iy in range(yc, hc + 1):
|
for iy in range(yc, hc + 1):
|
||||||
for ix in range(xc, wc + 1):
|
for ix in range(xc, wc + 1):
|
||||||
url = 'https://storage.pixelplanet.fun/%s/%s/%s/%s/%s/%s/%s.png' % (iter_date[0:4], iter_date[4:6] , iter_date[6:], canvas_id, time, ix, iy)
|
url = '%s/%s/%s/%s/%s/%s/%s/%s.png' % (PPFUN_STORAGE_URL, iter_date[0:4], iter_date[4:6] , iter_date[6:], canvas_id, time, ix, iy)
|
||||||
offx = ix * 256 + offset - x
|
offx = ix * 256 + offset - x
|
||||||
offy = iy * 256 + offset - y
|
offy = iy * 256 + offset - y
|
||||||
tasks.append(fetch(session, url, offx, offy, image_rel, bkg))
|
tasks.append(fetch(session, url, offx, offy, image_rel, bkg))
|
||||||
|
@ -183,41 +160,63 @@ async def get_area(canvas, x, y, w, h, start_date, end_date):
|
||||||
#frames[0].save('timelapse.png', save_all=True, append_images=frames[1:], duration=100, loop=0, default_image=False, blend=1)
|
#frames[0].save('timelapse.png', save_all=True, append_images=frames[1:], duration=100, loop=0, default_image=False, blend=1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
async def main():
|
||||||
|
apime = await fetchMe()
|
||||||
|
|
||||||
if len(sys.argv) != 5 and len(sys.argv) != 6:
|
if len(sys.argv) != 5 and len(sys.argv) != 6:
|
||||||
print("Download history of an area of pixelplanet - useful for timelapses")
|
print("Download history of an area of pixelplanet - useful for timelapses")
|
||||||
print("")
|
print("")
|
||||||
print("Usage: historyDownload.py canvasId startX_startY endX_endY start_date [end_date]")
|
print("Usage: historyDownload.py canvasID startX_startY endX_endY start_date [end_date]")
|
||||||
print("")
|
print("")
|
||||||
print("→start_date and end_date are in YYYY-MM-dd formate")
|
print("→start_date and end_date are in YYYY-MM-dd formate")
|
||||||
print("→user R key on pixelplanet to copy coordinates)")
|
print("→user R key on pixelplanet to copy coordinates)")
|
||||||
print("→images will be saved into timelapse folder)")
|
print("→images will be saved into timelapse folder)")
|
||||||
|
print("canvasID: ", end='')
|
||||||
|
for canvas_id, canvas in apime['canvases'].items():
|
||||||
|
if 'v' in canvas and canvas['v']:
|
||||||
|
continue
|
||||||
|
print(f"{canvas_id} = {canvas['title']}", end=', ')
|
||||||
|
print()
|
||||||
print("-----------")
|
print("-----------")
|
||||||
print("You can create a timelapse from the resulting files with ffmpeg like that:")
|
print("You can create a timelapse from the resulting files with ffmpeg like that:")
|
||||||
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
|
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
|
||||||
print("or lossless example:")
|
print("or lossless example:")
|
||||||
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuv444p -qmin 0 -qmax 0 -lossless 1 -an output.webm")
|
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuv444p -qmin 0 -qmax 0 -lossless 1 -an output.webm")
|
||||||
|
return
|
||||||
|
|
||||||
|
canvas_id = sys.argv[1]
|
||||||
|
|
||||||
|
if canvas_id not in apime['canvases']:
|
||||||
|
print("Invalid canvas selected")
|
||||||
|
return
|
||||||
|
|
||||||
|
canvas = apime['canvases'][canvas_id]
|
||||||
|
|
||||||
|
if 'v' in canvas and canvas['v']:
|
||||||
|
print("Can\'t get area for 3D canvas")
|
||||||
|
return
|
||||||
|
|
||||||
|
start = sys.argv[2].split('_')
|
||||||
|
end = sys.argv[3].split('_')
|
||||||
|
start_date = datetime.date.fromisoformat(sys.argv[4])
|
||||||
|
if len(sys.argv) == 6:
|
||||||
|
end_date = datetime.date.fromisoformat(sys.argv[5])
|
||||||
else:
|
else:
|
||||||
canvas = int(sys.argv[1])
|
end_date = datetime.date.today()
|
||||||
start = sys.argv[2].split('_')
|
x = int(start[0])
|
||||||
end = sys.argv[3].split('_')
|
y = int(start[1])
|
||||||
start_date = datetime.date.fromisoformat(sys.argv[4])
|
w = int(end[0]) - x + 1
|
||||||
if len(sys.argv) == 6:
|
h = int( end[1]) - y + 1
|
||||||
end_date = datetime.date.fromisoformat(sys.argv[5])
|
if not os.path.exists('./timelapse'):
|
||||||
else:
|
os.mkdir('./timelapse')
|
||||||
end_date = datetime.date.today()
|
await get_area(canvas_id, canvas, x, y, w, h, start_date, end_date)
|
||||||
x = int(start[0])
|
print("Done!")
|
||||||
y = int(start[1])
|
print("to create a timelapse from it:")
|
||||||
w = int(end[0]) - x + 1
|
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
|
||||||
h = int( end[1]) - y + 1
|
print("example with scaling *3 and audio track:")
|
||||||
loop = asyncio.get_event_loop()
|
print("ffmpeg -i ./audio.mp3 -framerate 8 -f image2 -i timelapse/t%d.png -map 0:a -map 1:v -vf scale=iw*3:-1 -shortest -c:v libvpx-vp9 -c:a libvorbis -pix_fmt yuva420p output.webm")
|
||||||
if not os.path.exists('./timelapse'):
|
print("lossless example:")
|
||||||
os.mkdir('./timelapse')
|
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuv444p -qmin 0 -qmax 0 -lossless 1 -an output.webm")
|
||||||
loop.run_until_complete(get_area(canvas, x, y, w, h, start_date, end_date))
|
|
||||||
print("Done!")
|
if __name__ == "__main__":
|
||||||
print("to create a timelapse from it:")
|
asyncio.run(main())
|
||||||
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
|
|
||||||
print("example with scaling *3 and audio track:")
|
|
||||||
print("ffmpeg -i ./audio.mp3 -framerate 8 -f image2 -i timelapse/t%d.png -map 0:a -map 1:v -vf scale=iw*3:-1 -shortest -c:v libvpx-vp9 -c:a libvorbis -pix_fmt yuva420p output.webm")
|
|
||||||
print("lossless example:")
|
|
||||||
print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuv444p -qmin 0 -qmax 0 -lossless 1 -an output.webm")
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user