Ethan Dalool
3c4f69f647
Sometimes it's easy to forget whether its the name or ID, and I like the consistency of specifying.
165 lines
4.2 KiB
Python
165 lines
4.2 KiB
Python
import flask; from flask import request
|
|
import os
|
|
import mimetypes
|
|
import traceback
|
|
|
|
import etiquette
|
|
|
|
from voussoirkit import pathclass
|
|
|
|
from . import jsonify
|
|
from . import sessions
|
|
|
|
|
|
root_dir = pathclass.Path(__file__).parent.parent
|
|
|
|
TEMPLATE_DIR = root_dir.with_child('templates')
|
|
STATIC_DIR = root_dir.with_child('static')
|
|
FAVICON_PATH = STATIC_DIR.with_child('favicon.png')
|
|
|
|
site = flask.Flask(
|
|
__name__,
|
|
template_folder=TEMPLATE_DIR.absolute_path,
|
|
static_folder=STATIC_DIR.absolute_path,
|
|
)
|
|
site.config.update(
|
|
SEND_FILE_MAX_AGE_DEFAULT=180,
|
|
TEMPLATES_AUTO_RELOAD=True,
|
|
)
|
|
site.jinja_env.add_extension('jinja2.ext.do')
|
|
site.jinja_env.trim_blocks = True
|
|
site.jinja_env.lstrip_blocks = True
|
|
site.debug = True
|
|
|
|
P = etiquette.photodb.PhotoDB()
|
|
|
|
session_manager = sessions.SessionManager()
|
|
|
|
|
|
def P_wrapper(function):
|
|
def P_wrapped(thingid, response_type='html'):
|
|
try:
|
|
return function(thingid)
|
|
|
|
except etiquette.exceptions.EtiquetteException as exc:
|
|
if isinstance(exc, etiquette.exceptions.NoSuch):
|
|
status = 404
|
|
else:
|
|
status = 400
|
|
|
|
if response_type == 'html':
|
|
flask.abort(status, exc.error_message)
|
|
else:
|
|
response = etiquette.jsonify.exception(exc)
|
|
response = jsonify.make_json_response(response, status=status)
|
|
flask.abort(response)
|
|
|
|
except Exception as exc:
|
|
traceback.print_exc()
|
|
if response_type == 'html':
|
|
flask.abort(500)
|
|
else:
|
|
flask.abort(jsonify.make_json_response({}, status=500))
|
|
|
|
return P_wrapped
|
|
|
|
@P_wrapper
|
|
def P_album(album_id):
|
|
return P.get_album(album_id)
|
|
|
|
@P_wrapper
|
|
def P_bookmark(bookmarkid):
|
|
return P.get_bookmark(bookmarkid)
|
|
|
|
@P_wrapper
|
|
def P_photo(photo_id):
|
|
return P.get_photo(photo_id)
|
|
|
|
@P_wrapper
|
|
def P_tag(tagname):
|
|
return P.get_tag(name=tagname)
|
|
|
|
@P_wrapper
|
|
def P_user(username):
|
|
return P.get_user(username=username)
|
|
|
|
@P_wrapper
|
|
def P_user_id(user_id):
|
|
return P.get_user(id=user_id)
|
|
|
|
|
|
def back_url():
|
|
return request.args.get('goto') or request.referrer or '/'
|
|
|
|
def send_file(filepath, override_mimetype=None):
|
|
'''
|
|
Range-enabled file sending.
|
|
'''
|
|
try:
|
|
file_size = os.path.getsize(filepath)
|
|
except FileNotFoundError:
|
|
flask.abort(404)
|
|
|
|
outgoing_headers = {}
|
|
if override_mimetype is not None:
|
|
mimetype = override_mimetype
|
|
else:
|
|
mimetype = mimetypes.guess_type(filepath)[0]
|
|
|
|
if mimetype is not None:
|
|
if 'text/' in mimetype:
|
|
mimetype += '; charset=utf-8'
|
|
outgoing_headers['Content-Type'] = mimetype
|
|
|
|
if 'range' in request.headers:
|
|
desired_range = request.headers['range'].lower()
|
|
desired_range = desired_range.split('bytes=')[-1]
|
|
|
|
int_helper = lambda x: int(x) if x.isdigit() else None
|
|
if '-' in desired_range:
|
|
(desired_min, desired_max) = desired_range.split('-')
|
|
range_min = int_helper(desired_min)
|
|
range_max = int_helper(desired_max)
|
|
else:
|
|
range_min = int_helper(desired_range)
|
|
|
|
if range_min is None:
|
|
range_min = 0
|
|
if range_max is None:
|
|
range_max = file_size
|
|
|
|
# because ranges are 0-indexed
|
|
range_max = min(range_max, file_size - 1)
|
|
range_min = max(range_min, 0)
|
|
|
|
range_header = 'bytes {min}-{max}/{outof}'.format(
|
|
min=range_min,
|
|
max=range_max,
|
|
outof=file_size,
|
|
)
|
|
outgoing_headers['Content-Range'] = range_header
|
|
status = 206
|
|
else:
|
|
range_max = file_size - 1
|
|
range_min = 0
|
|
status = 200
|
|
|
|
outgoing_headers['Accept-Ranges'] = 'bytes'
|
|
outgoing_headers['Content-Length'] = (range_max - range_min) + 1
|
|
|
|
if request.method == 'HEAD':
|
|
outgoing_data = bytes()
|
|
else:
|
|
outgoing_data = etiquette.helpers.read_filebytes(
|
|
filepath,
|
|
range_min=range_min,
|
|
range_max=range_max,
|
|
chunk_size=P.config['file_read_chunk'],
|
|
)
|
|
|
|
response = flask.Response(
|
|
outgoing_data,
|
|
status=status,
|
|
headers=outgoing_headers,
|
|
)
|
|
return response
|