Advertisement
Asmund_11

Untitled

Mar 11th, 2021
1,288
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.03 KB | None | 0 0
  1. #!/usr/bin/python3
  2.  
  3. import PIL.Image
  4. import sys, io, os
  5. import datetime
  6. import asyncio
  7. import aiohttp
  8. import json
  9.  
  10. # how many frames to skip
  11. #  1 means none
  12. #  2 means that every second frame gets captured
  13. #  3 means every third
  14. #  [...]
  15. frameskip = 1
  16.  
  17. canvases = [
  18.         {
  19.             "canvas_name": "earth",
  20.             "canvas_size": 256*256,
  21.             "canvas_id": 0,
  22.             "bkg": (202, 227, 255),
  23.         },
  24.         {
  25.             "canvas_name": "moon",
  26.             "canvas_size": 4096,
  27.             "canvas_id": 1,
  28.             "bkg": (49, 46, 47),
  29.         },
  30.         {
  31.         }   ,
  32.         {
  33.             "canvas_name": "corona",
  34.             "canvas_size": 256,
  35.             "canvas_id": 3,
  36.             "bkg": (33, 28, 15),
  37.         },
  38.         {
  39.             "canvas_name": "compass",
  40.             "canvas_size": 1024,
  41.             "canvas_id": 4,
  42.             "bkg": (196, 196, 196),
  43.         },
  44.         {
  45.         },
  46.         {
  47.         },
  48.         {
  49.             "canvas_name": "1bit",
  50.             "canvas_size": 4096,
  51.             "canvas_id": 7,
  52.             "bkg": (0, 0, 0),
  53.         },
  54.     ]
  55.  
  56. async def fetch(session, url, offx, offy, image, bkg, needed = False):
  57.     attempts = 0
  58.     while True:
  59.         try:
  60.             async with session.get(url) as resp:
  61.                 if resp.status == 404:
  62.                     if needed:
  63.                         img = PIL.Image.new('RGB', (256, 256), color=bkg)
  64.                         image.paste(img, (offx, offy))
  65.                         img.close()
  66.                     return
  67.                 if resp.status != 200:
  68.                     if needed:
  69.                         continue
  70.                     return
  71.                 data = await resp.read()
  72.                 img = PIL.Image.open(io.BytesIO(data)).convert('RGBA')
  73.                 image.paste(img, (offx, offy), img)
  74.                 img.close()
  75.                 return
  76.         except:
  77.             if attempts > 3:
  78.                 raise
  79.             attempts += 1
  80.             pass
  81.  
  82. async def get_area(canvas, x, y, w, h, start_date, end_date):
  83.     canvas_data = canvases[canvas]
  84.     canvas_id = canvas_data["canvas_id"]
  85.     canvas_size = canvas_data["canvas_size"]
  86.     bkg = canvas_data["bkg"]
  87.  
  88.     offset = int(-canvas_size / 2)
  89.     xc = (x - offset) // 256
  90.     wc = (x + w - offset) // 256
  91.     yc = (y - offset) // 256
  92.     hc = (y + h - offset) // 256
  93.     print("Load from %s / %s to %s / %s" % (xc, yc, wc + 1, hc + 1))
  94.     delta = datetime.timedelta(days=1)
  95.     end_date = end_date.strftime("%Y%m%d")
  96.     iter_date = None
  97.     cnt = 0
  98.     #frames = []
  99.     previous_day = PIL.Image.new('RGB', (w, h), color=bkg)
  100.     while iter_date != end_date:
  101.         iter_date = start_date.strftime("%Y%m%d")
  102.         print('------------------------------------------------')
  103.         print('Getting frames for date %s' % (iter_date))
  104.         start_date = start_date + delta
  105.         tasks = []
  106.         async with aiohttp.ClientSession() as session:
  107.             image = PIL.Image.new('RGBA', (w, h))
  108.             for iy in range(yc, hc + 1):
  109.                 for ix in range(xc, wc + 1):
  110.                     url = 'https://storage.pixelplanet.fun/%s/%s/tiles/%s/%s.png' % (iter_date, canvas_id, ix, iy)
  111.                     offx = ix * 256 + offset - x
  112.                     offy = iy * 256 + offset - y
  113.                     tasks.append(fetch(session, url, offx, offy, image, bkg, True))
  114.             await asyncio.gather(*tasks)
  115.             print('Got start of day')
  116.             # check if image is all just one color to lazily detect if whole full backup was 404
  117.             clr = image.getcolors(1)
  118.             if clr is not None:
  119.                 print("Got faulty full-backup frame, using last frame from previous day instead.")
  120.                 image = previous_day.copy()
  121.             cnt += 1
  122.             #frames.append(image.copy())
  123.             image.save('./timelapse/t%s.png' % (cnt))
  124.             while True:
  125.                 async with session.get('https://pixelplanet.fun/api/history?day=%s&id=%s' % (iter_date, canvas_id)) as resp:
  126.                     try:
  127.                         time_list = json.loads(await resp.text())
  128.                         break
  129.                     except:
  130.                         print('Couldn\'t decode json for day %s, trying again' % (iter_date))
  131.             i = 0
  132.             for time in time_list:
  133.                 i += 1
  134.                 if (i % frameskip) != 0:
  135.                     continue
  136.                 if time == '0000':
  137.                     # 0000 incremential backups are faulty
  138.                     continue
  139.                 tasks = []
  140.                 image_rel = image.copy()
  141.                 for iy in range(yc, hc + 1):
  142.                     for ix in range(xc, wc + 1):
  143.                         url = 'https://storage.pixelplanet.fun/%s/%s/%s/%s/%s.png' % (iter_date, canvas_id, time, ix, iy)
  144.                         offx = ix * 256 + offset - x
  145.                         offy = iy * 256 + offset - y
  146.                         tasks.append(fetch(session, url, offx, offy, image_rel, bkg))
  147.                 await asyncio.gather(*tasks)
  148.                 print('Got time %s' % (time))
  149.                 cnt += 1
  150.                 #frames.append(image.copy())
  151.                 image_rel.save('./timelapse/t%s.png' % (cnt))
  152.                 if time == time_list[-1]:
  153.                     # if last element of day, copy it to previous_day to reuse it when needed
  154.                     print("Remembering last frame of day.")
  155.                     previous_day.close()
  156.                     previous_day = image_rel.copy();
  157.                 image_rel.close()
  158.             image.close()
  159.     previous_day.close()
  160.     # this would save a gif right out of the script, but giffs are huge and not good
  161.     #frames[0].save('timelapse.png', save_all=True, append_images=frames[1:], duration=100, loop=0, default_image=False, blend=1)
  162.  
  163.  
  164. if __name__ == "__main__":
  165.     if len(sys.argv) != 5 and len(sys.argv) != 6:
  166.         print("Download history of an area of pixelplanet - useful for timelapses")
  167.         print("")
  168.         print("Usage:    historyDownload.py canvasId startX_startY endX_endY start_date [end_date]")
  169.         print("")
  170.         print("→start_date and end_date are in YYYY-MM-dd formate")
  171.         print("→user R key on pixelplanet to copy coordinates)")
  172.         print("→images will be saved into timelapse folder)")
  173.         print("-----------")
  174.         print("You can create a timelapse from the resulting files with ffmpeg like that:")
  175.         print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
  176.         print("or lossless example:")
  177.         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")
  178.     else:
  179.         canvas = int(sys.argv[1])
  180.         start = sys.argv[2].split('_')
  181.         end = sys.argv[3].split('_')
  182.         start_date = datetime.date.fromisoformat(sys.argv[4])
  183.         if len(sys.argv) == 6:
  184.             end_date = datetime.date.fromisoformat(sys.argv[5])
  185.         else:
  186.             end_date = datetime.date.today()
  187.         x = int(start[0])
  188.         y = int(start[1])
  189.         w = int(end[0]) - x + 1
  190.         h = int( end[1]) - y + 1
  191.         loop = asyncio.get_event_loop()
  192.         if not os.path.exists('./timelapse'):
  193.             os.mkdir('./timelapse')
  194.         loop.run_until_complete(get_area(canvas, x, y, w, h, start_date, end_date))
  195.         print("Done!")
  196.         print("to create a timelapse from it:")
  197.         print("ffmpeg -framerate 15 -f image2 -i timelapse/t%d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm")
  198.         print("example with scaling *3 and audio track:")
  199.         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")
  200.         print("lossless example:")
  201.         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")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement