#!/usr/bin/python3 import os import flask import argparse import sys import PIL.Image import werkzeug.utils import json app = flask.Flask("Picture factory app", static_folder=None) PICTURE_DIR = "pictures/" app.config['MAX_CONTENT_PATH'] = 32+1000*1000 app.config['UPLOAD_FOLDER'] = PICTURE_DIR app.config['UPLOAD_ENABLED'] = os.path.isfile("./upload.enable") def generatePicture(pathToOrig, scaleX, scaleY, encoding, crop): '''Generate an pictures with the requested scales and encoding if it doesn't already exist''' CACHE_DIR = os.path.join("cache/") if os.path.isfile(CACHE_DIR): raise OSError("Picture cache dir is occupied by a file!") if not os.path.isdir(CACHE_DIR): os.mkdir(CACHE_DIR) filename, extension = os.path.splitext(os.path.basename(pathToOrig)) if not encoding: encoding = extension.strip(".") # just python things... # if encoding.lower() == "jpg": encoding = "jpeg" # open image # try: image = PIL.Image.open(os.path.join(PICTURE_DIR, pathToOrig)) except FileNotFoundError: return (None, False) # ensure sizes are valid # x, y = image.size if not scaleY: scaleY = y if not scaleX: scaleX = x scaleX = min(x, scaleX) scaleY = min(y, scaleY) # generate new paths # newFile = "x-{x}-y-{y}-{fname}.{ext}".format(x=scaleX, y=scaleY, fname=filename, ext=encoding) newPath = os.path.join(CACHE_DIR, newFile) # check for cache print(newPath) if os.path.isfile(newPath): return (newPath, True) # save image with new size and encoding # if image.mode in ("RGBA", "P") and encoding in ("jpeg"): image = image.convert("RGB") if crop: image.crop((0, 0, scaleX, scaleY)) print(scaleX, scaleY) else: print("scale") image.thumbnail((scaleX, scaleY), PIL.Image.ANTIALIAS) image.save(newPath, encoding) # strip the STATIC_DIR because we will use send_from_directory for safety # REPLACE_ONCE = 1 return (newPath.replace(PICTURE_DIR, "", REPLACE_ONCE), False) @app.route("/media/") @app.route("/m/") @app.route("/picture/") @app.route("/pictures/") @app.route("/image/") @app.route("/images/") def sendPicture(path): max_age = 2592000 y1 = flask.request.args.get("scaley") x1 = flask.request.args.get("scalex") y2 = flask.request.args.get("y") x2 = flask.request.args.get("x") # check variables # scaleY, scaleX = (None, None) if y1: scaleY = round(float(y1)) elif y2: scaleY = round(float(y2)) if x1: scaleX = round(float(x1)) elif x2: scaleX = round(float(x2)) pathDebug = path if path.endswith(".svg"): return flask.send_from_directory(".", path) encoding = flask.request.args.get("encoding") path, cacheHit = generatePicture(path, scaleX, scaleY, encoding, bool(flask.request.args.get("crop"))) if not path: return ("File not found: {}".format(os.path.join(PICTURE_DIR, pathDebug)), 404) raw = flask.send_from_directory(".", path, max_age=max_age) response = flask.make_response(raw) response.headers['X-PICTURE-FACTORY-INTERNAL-FID'] = path response.headers['X-PICTURE-FACTORY-INTERNAL-CACHE-HIT'] = cacheHit # check for a cacheTimeout # cacheTimeout = flask.request.args.get("cache-timeout") if not cacheTimeout: cacheTimeout = flask.request.args.get("ct") if cacheTimeout: response.headers['Cache-Control'] = "max-age=" + str(cacheTimeout) else: response.headers['Cache-Control'] = "max-age=" + "3600" if encoding: response.headers['Content-Type'] = "image/{}".format(encoding) return response @app.route("/") def list(): retStringArr = [] for root, dirs, files in os.walk(PICTURE_DIR): path = root.split(os.sep) for f in files: retStringArr += [os.path.join(os.path.basename(root), f)] return flask.render_template("index.html", paths=retStringArr) @app.route("/upload", methods = ['GET', 'POST']) def upload(): if not app.config['UPLOAD_ENABLED']: return ("Upload Disabled", 403) if flask.request.method == 'POST': f = flask.request.files['file'] fname = werkzeug.utils.secure_filename(f.filename) sfName = os.path.join(PICTURE_DIR, fname) if not os.path.isfile(sfName): f.save(sfName) realHostname = flask.request.headers.get("X-REAL-HOSTNAME") if realHostname: return flask.redirect("/media/" + fname) else: return ('Success', 204) else: return ('Conflicting File', 409) else: return flask.render_template("upload.html") if __name__ == "__main__": parser = argparse.ArgumentParser(description='Picture Factory', formatter_class=argparse.ArgumentDefaultsHelpFormatter) # general parameters # parser.add_argument("-i", "--interface", default="127.0.0.1", help="Interface to listen on") parser.add_argument("-p", "--port", default="5000", help="Port to listen on") # startup # args = parser.parse_args() app.run(host=args.interface, port=args.port)