diff --git a/handler.py b/handler.py index 08a3991..87a20a1 100755 --- a/handler.py +++ b/handler.py @@ -3,11 +3,13 @@ import re import importlib import argparse +import subprocess regs = { "\d/\d/\d_\d\.jpg": "krpanosteal", "pano\_[frblud].jpg": "krpanosteal", - "my.matterport.com/show/": "matterportsteal" + "my.matterport.com/show/": "matterportsteal", + "youtube.com": "youtubesteal" } @@ -29,18 +31,27 @@ def parse_url(url): if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('url', help='URL to process') - parser.add_argument('--title', help='title to be used for the file name') + parser.add_argument('--title', help='title to be used for the file name', default="No Title") parser.add_argument("--rotation", nargs=3, type=int, help="rotation on x/y/z axes", metavar=("x","y","z")) parser.add_argument("--resolution", type=int, nargs=2, metavar=("w","h")) - parser.add_argument("--output") + parser.add_argument("--output", default=".") args = parser.parse_args() try: handler = parse_url(args.url) image = handler(args.url, args.rotation or [0,0,0], args.resolution or [3840, 1920]) - - image.save(args.output + "/" + args.title + ".png") + if not hasattr(image, "im"): + with open(args.output + "/" + args.title + ".mkv", "wb") as video: + video.write(image) + subprocess.run(["/usr/bin/ffmpeg", + "-i", args.output + "/" + args.title + ".mkv", + "-ss", "00:00:10", + "-vframes", "1", + "-f", "image2", + args.output + "/" + args.title + ".thumb.jpg"]) + else: + image.save(args.output + "/" + args.title + ".png") except Exception as e: with open(args.output + "/" + args.title + ".err", "w") as errorfile: - errorfile.write("") + errorfile.write(str(e)) diff --git a/run.sh b/run.sh index 044ec80..28e82d8 100755 --- a/run.sh +++ b/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -mkdir /tmp/panosteal/ -p +mkdir /tmp/panosteal/youtube -p gunicorn -c gunicorn.cfg server.daemon diff --git a/server/daemon.py b/server/daemon.py index 2725e9b..492d72d 100644 --- a/server/daemon.py +++ b/server/daemon.py @@ -22,6 +22,8 @@ JSON = "application/json" XML = "text/xml" TEXT = "text/plain" PNG = "image/png" +MKV = "video/mkv" +JPG = "image/jpeg" def static(req): try: @@ -92,15 +94,15 @@ def addjob(req): return Response(status, ctype, content) def getjob(req): - jobid = req.path[-1] + jobid = req.path[-1].rstrip("-thumb").rstrip("-info") content_disposition = None - found = glob.glob("/tmp/panosteal/%s---*.png" % jobid) + found = (glob.glob("/tmp/panosteal/%s---*.png" % jobid) + glob.glob("/tmp/panosteal/%s---*.mkv" % jobid)) if not req.path[-1].endswith("thumb") else glob.glob("/tmp/panosteal/%s---*.jpg" % jobid) if found: md5 = "Not happening." while True: - content = open(found[0], "rb").read() + content = open(found[0], "rb").read() if not req.path[-1].endswith("info") else b"" newmd5 = hashlib.md5(content).hexdigest() if newmd5 == md5: break @@ -108,8 +110,8 @@ def getjob(req): time.sleep(0.5) code = HTTP200 - ctype = PNG - content_disposition = found[0].split("---")[-1] + ctype = PNG if found[0].endswith(".png") else JPG if found[0].endswith(".jpg") else MKV + content_disposition = found[0].split("---")[-1] if not req.path[-1].endswith("info") else None elif glob.glob("/tmp/panosteal/%s*err" % jobid): content = "

500 Internal Server Error

".encode() diff --git a/server/static/index.html b/server/static/index.html index 8a7a593..30901fd 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -24,7 +24,7 @@
-
URL of an image contained in a krpano panorama or of a website containing a Matterport panorama
+
x
diff --git a/server/static/worker.js b/server/static/worker.js index dffff45..150e40e 100644 --- a/server/static/worker.js +++ b/server/static/worker.js @@ -37,11 +37,23 @@ function failcard(jobid, title) { } -function finishcard(jobid, title) { +function finishcard(jobid, title, video) { deletecard(jobid); - - var text = '
Final Image
' + title + '
'; + if (!video) { + var text = '
Final Image
' + title + '
'; + } else { + var text = '
Final Video
' + title + '
'; + }; $('#cards').append(text); + + var counter = 0; + var interval = setInterval(function() { + var image = document.getElementById(jobid + '-thumb'); + image.src = "/getjob/" + jobid + "-thumb?rand=" + Math.random(); + if (++counter === 10) { + window.clearInterval(interval); + } + }, 2000); } $('#theform').submit(function(event){ @@ -64,16 +76,16 @@ $('#theform').submit(function(event){ $.ajax({ type: "GET", cache: false, - url: "/getjob/" + jobid, + url: "/getjob/" + jobid + "-info", statusCode: { 404: function() { clearInterval(interval); failcard(jobid, title); return; }, - 200: function() { + 200: function(data, tstatus, xhr) { clearInterval(interval); - finishcard(jobid, title); + finishcard(jobid, title, (xhr.getResponseHeader('content-type') == "image/png") ? false : true); return; }, 500: function() { diff --git a/youtubesteal/__init__.py b/youtubesteal/__init__.py new file mode 100644 index 0000000..fa1203d --- /dev/null +++ b/youtubesteal/__init__.py @@ -0,0 +1,50 @@ +import PIL.Image +import urllib.request +import io +import math +import subprocess +import tempfile +import pathlib +import os +import subprocess +import uuid +import glob + +from stitching import tiles_to_equirectangular_blender, multistitch + +def youtube_get_video(url): + vid = uuid.uuid4().hex + process = subprocess.Popen( + ['youtube-dl', + '--user-agent', '""', + '-o', vid, + url, + ], cwd="/tmp/panosteal/youtube/" + ) + + process.wait() + + data = open('/tmp/panosteal/youtube/%s.mkv' % vid, 'rb').read() + + for i in glob.glob("/tmp/panosteal/youtube/%s*" % vid): + os.remove(i) + + return data + +def youtube_get_file(yurl): + urllib.request.urlopen(yurl) + +def youtube_to_equirectangular(url, rotation=[0,0,0], resolution=[3840,1920]): + ''' + Takes the URL of any YouTube video, downloads it and returns the video file. + + :param url: YouTube URL + :return: File object containing the video + ''' + + rx, ry, rz = rotation + width, height = resolution + + return youtube_get_video(url) + +process_url = youtube_to_equirectangular