Tightened up how we serve images.

master
Pacman Ghost 5 years ago
parent b10adaac99
commit 15f693fc05
  1. 2
      vasl_templates/webapp/data/default-template-pack/atmm.j2
  2. 2
      vasl_templates/webapp/data/default-template-pack/baz.j2
  3. 2
      vasl_templates/webapp/data/default-template-pack/extras/kgs/grenade-bundles.j2
  4. 2
      vasl_templates/webapp/data/default-template-pack/extras/kgs/molotov-cocktails.j2
  5. 2
      vasl_templates/webapp/data/default-template-pack/mol-p.j2
  6. 2
      vasl_templates/webapp/data/default-template-pack/mol.j2
  7. 2
      vasl_templates/webapp/data/default-template-pack/ob_ordnance.j2
  8. 2
      vasl_templates/webapp/data/default-template-pack/ob_ordnance_ma_notes.j2
  9. 2
      vasl_templates/webapp/data/default-template-pack/ob_ordnance_note.j2
  10. 2
      vasl_templates/webapp/data/default-template-pack/ob_setup.j2
  11. 2
      vasl_templates/webapp/data/default-template-pack/ob_vehicle_note.j2
  12. 2
      vasl_templates/webapp/data/default-template-pack/ob_vehicles.j2
  13. 2
      vasl_templates/webapp/data/default-template-pack/ob_vehicles_ma_notes.j2
  14. 2
      vasl_templates/webapp/data/default-template-pack/pf.j2
  15. 2
      vasl_templates/webapp/data/default-template-pack/piat.j2
  16. 4
      vasl_templates/webapp/data/default-template-pack/players.j2
  17. 2
      vasl_templates/webapp/data/default-template-pack/psk.j2
  18. 7
      vasl_templates/webapp/files.py
  19. 59
      vasl_templates/webapp/snippets.py
  20. 58
      vasl_templates/webapp/utils.py
  21. 29
      vasl_templates/webapp/vo_notes.py

@ -16,7 +16,7 @@ td { margin: 0 ; padding: 0 ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}Anti-Tank Magnetic Mines
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}Anti-Tank Magnetic Mines
<tr>
<td style="padding:2px 5px;">

@ -18,7 +18,7 @@ td.r { text-align: right ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}Bazooka {%if BAZ_TYPE%} ('{{BAZ_TYPE}}) {%endif%}
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}Bazooka {%if BAZ_TYPE%} ('{{BAZ_TYPE}}) {%endif%}
<tr>

@ -14,7 +14,7 @@ td { margin: 0 ; padding: 0 ; }
<tr>
<td colspan="2" style="background: {{PLAYER_COLORS["german"][0]}} ; border-bottom: 1px solid {{PLAYER_COLORS["german"][2]}} ; padding: 2px 5px ; font-weight: bold ;">
{%if PLAYER_FLAGS["german"]%}<img src="{{PLAYER_FLAGS["german"]}}?height=11">&nbsp;{%endif%}Grenade Bundles
{%if PLAYER_FLAGS["german"]%}<img src="{{PLAYER_FLAGS["german"]}}">&nbsp;{%endif%}Grenade Bundles
<tr>
<td style="padding:2px 5px;">

@ -15,7 +15,7 @@ ul { margin: 0 0 0 10px ; padding: 0 ; }
<tr>
<td colspan="2" style="background: {{PLAYER_COLORS["german"][0]}} ; border-bottom: 1px solid {{PLAYER_COLORS["german"][2]}} ; padding: 2px 5px ; font-weight: bold ;">
{%if PLAYER_FLAGS["german"]%}<img src="{{PLAYER_FLAGS["german"]}}?height=11">&nbsp;{%endif%}Molotov Cocktails
{%if PLAYER_FLAGS["german"]%}<img src="{{PLAYER_FLAGS["german"]}}">&nbsp;{%endif%}Molotov Cocktails
<tr>
<td style="padding:0 5px;">

@ -19,7 +19,7 @@ ul { margin: 0 0 0 10px ; padding: 0 ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}MOL Projector
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}MOL Projector
<tr>

@ -17,7 +17,7 @@ ul { margin: 0 0 0 10px ; padding: 0 ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}Molotov Cocktail
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}Molotov Cocktail
<tr>
<td style="padding:0 5px;">

@ -20,7 +20,7 @@ sup { font-size: 75% ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{PLAYER_NAME}} Ordnance
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME}} Ordnance
{%for ord in OB_ORDNANCE%}
<tr style="border-bottom:1px dotted #e0e0e0;">

@ -21,7 +21,7 @@ sup { font-size: 75% ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{PLAYER_NAME}} Ordnance Notes
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME}} Ordnance Notes
{%if OB_ORDNANCE_MA_NOTES%}
<tr> <td style="padding: 0 5px;">

@ -13,7 +13,7 @@
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{ORDNANCE_NAME}}
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{ORDNANCE_NAME}}
<tr>
<td> <img src="{{ORDNANCE_NOTE_URL}}">

@ -14,7 +14,7 @@
font-weight: bold ;
{%if OB_SETUP_WIDTH%} width: {{OB_SETUP_WIDTH}} ; {%endif%}
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{OB_SETUP}}
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{OB_SETUP}}
</table>

@ -13,7 +13,7 @@
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{VEHICLE_NAME}}
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{VEHICLE_NAME}}
<tr>
<td> <img src="{{VEHICLE_NOTE_URL}}">

@ -20,7 +20,7 @@ sup { font-size: 75% ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{PLAYER_NAME}} Vehicles
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME}} Vehicles
{%for veh in OB_VEHICLES%}
<tr style="border-bottom:1px dotted #e0e0e0;">

@ -21,7 +21,7 @@ sup { font-size: 75% ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}{{PLAYER_NAME}} Vehicle Notes
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME}} Vehicle Notes
{%if OB_VEHICLES_MA_NOTES%}
<tr> <td style="padding: 0 5px;">

@ -18,7 +18,7 @@ td.r { text-align: right ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}Panzerfaust
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}Panzerfaust
<tr>

@ -18,7 +18,7 @@ td.r { text-align: right ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}PIAT
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}PIAT
<tr>

@ -10,8 +10,8 @@
">
<td style="font-weight:bold;">
{%if PLAYER_FLAG_1%}<img src="{{PLAYER_FLAG_1}}?height=11">&nbsp;{%endif%}{{PLAYER_1_NAME}}: <br>
{%if PLAYER_FLAG_2%}<img src="{{PLAYER_FLAG_2}}?height=11">&nbsp;{%endif%}{{PLAYER_2_NAME}}:
{%if PLAYER_FLAG_1%}<img src="{{PLAYER_FLAG_1}}">&nbsp;{%endif%}{{PLAYER_1_NAME}}: <br>
{%if PLAYER_FLAG_2%}<img src="{{PLAYER_FLAG_2}}">&nbsp;{%endif%}{{PLAYER_2_NAME}}:
<td>
ELR: {{PLAYER_1_ELR}} <br>
ELR: {{PLAYER_2_ELR}}

@ -18,7 +18,7 @@ td.r { text-align: right ; }
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}?height=11">&nbsp;{%endif%}Panzerschrek
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}Panzerschrek
<tr>

@ -10,6 +10,7 @@ from flask import send_file, send_from_directory, jsonify, redirect, url_for, ab
from vasl_templates.webapp import app
from vasl_templates.webapp.file_server.vasl_mod import get_vasl_mod
from vasl_templates.webapp.utils import resize_image_response
# ---------------------------------------------------------------------
@ -24,6 +25,9 @@ class FileServer:
def serve_file( self, path ):
"""Serve a file."""
# NOTE: We return a Flask Response object, instead of the file data, so that (1) we can use
# send_from_directory() and (2) if we have to download a file from a URL, we can include
# the MIME type in what we return.
if FileServer.is_remote_path( self.base_dir ):
url = "{}/{}".format( self.base_dir, path )
# NOTE: We download the target file and serve it ourself (instead of just redirecting)
@ -55,7 +59,8 @@ def get_user_file( path ):
dname = app.config.get( "USER_FILES_DIR" )
if not dname:
abort( 404 )
return FileServer( dname ).serve_file( path )
resp = FileServer( dname ).serve_file( path )
return resize_image_response( resp )
# ---------------------------------------------------------------------

@ -6,6 +6,7 @@ import re
import zipfile
import io
import base64
import threading
from flask import request, jsonify, send_file, abort
from PIL import Image
@ -137,6 +138,9 @@ def make_snippet_image():
# ---------------------------------------------------------------------
_flag_image_cache = {}
_flag_image_cache_lock = threading.Lock()
@app.route( "/flags/<nat>" )
def get_flag( nat ):
"""Get a flag image."""
@ -145,25 +149,36 @@ def get_flag( nat ):
if not re.search( "^[-a-z]+$", nat ):
abort( 404 )
fname = "static/images/flags/{}.png".format( nat )
with app.open_resource( fname, "rb" ) as fp:
# load the image
img = Image.open( fp )
# check if we should resize the image
# NOTE: Resizing images in the HTML snippets looks dreadful (presumably
# because VASSAL's HTML engine is so ancient), so we do it ourself :-/
height = request.args.get( "height" )
if height:
height = int( height )
if height > 0:
width = img.size[0] / ( float(img.size[1]) / height )
width = int( width + 0.5 )
img = img.resize( (width,height), Image.ANTIALIAS )
# return the image
buf = io.BytesIO()
img.save( buf, format="PNG" )
buf.seek( 0 )
return send_file( buf, mimetype="image/png" )
# check how we should resize the image
# NOTE: Resizing images in the HTML snippets looks dreadful (presumably
# because VASSAL's HTML engine is so ancient), so we do it ourself :-/
default_height = app.config.get( "DEFAULT_FLAG_HEIGHT", 11 )
height = int( request.args.get( "height", default_height ) )
if height <= 0:
abort( 400 )
with _flag_image_cache_lock:
# check if we have the image in the cache
cache_key = ( nat, height )
if cache_key not in _flag_image_cache:
# nope - load it
fname = "static/images/flags/{}.png".format( nat )
with app.open_resource( fname, "rb" ) as fp:
img = Image.open( fp )
# resize the image
height = int( height )
if height > 0:
width = img.size[0] / ( float(img.size[1]) / height )
width = int( width + 0.5 )
img = img.resize( (width,height), Image.ANTIALIAS )
# add the image to the cache
buf = io.BytesIO()
img.save( buf, format="PNG" )
buf.seek( 0 )
_flag_image_cache[ cache_key ] = buf.read()
# return the flag image
img_data =_flag_image_cache[ cache_key ]
return send_file( io.BytesIO(img_data), mimetype="image/png" )

@ -1,10 +1,14 @@
""" Miscellaneous utilities. """
import os
import io
import tempfile
import pathlib
from collections import defaultdict
from flask import request, Response, send_file
from PIL import Image
# ---------------------------------------------------------------------
class MsgStore:
@ -75,6 +79,60 @@ class TempFile:
# ---------------------------------------------------------------------
def resize_image_response( resp, default_width=None, default_height=None, default_scaling=None ):
"""Resize an image that will be returned as a Flask response."""
assert isinstance( resp, Response )
def get_image():
"""Get the the image from the Flask response that was passed in."""
resp.direct_passthrough = False
buf = io.BytesIO()
buf.write( resp.get_data() )
buf.seek( 0 )
return Image.open( buf )
# check if the caller specified a width and/or height
width = request.args.get( "width", default_width )
height = request.args.get( "height", default_height )
if width and height:
# width and height were specified, just use them as-is
img = get_image()
width = int( width )
height = int( height )
elif width and not height:
# width only, calculate the height
img = get_image()
aspect_ratio = float(img.size[0]) / float(img.size[1])
height = int(width) / aspect_ratio
elif not width and height:
# height only, calculate the width
img = get_image()
aspect_ratio = float(img.size[0]) / float(img.size[1])
width = int(height) * aspect_ratio
elif not width and not height:
# check if the caller specified a scaling factor
scaling = request.args.get( "scaling", default_scaling )
if scaling and scaling != 100:
img = get_image()
width = img.size[0] * float(scaling)/100
height = img.size[1] * float(scaling)/100
# check if we need to resize the image
if width or height:
assert width and height
# yup - make it so
img = img.resize( (int(width),int(height)), Image.ANTIALIAS )
buf = io.BytesIO()
img.save( buf, format="PNG" )
buf.seek( 0 )
return send_file( buf, mimetype="image/png" )
else:
# nope - return the image as-is
return resp
# ---------------------------------------------------------------------
def change_extn( fname, extn ):
"""Change a filename's extension."""
return pathlib.Path( fname ).with_suffix( extn )

@ -3,16 +3,14 @@
import os
import threading
import io
import logging
from collections import defaultdict
from flask import request, render_template, send_file, jsonify, abort
from PIL import Image
from flask import render_template, jsonify, abort
from vasl_templates.webapp import app
from vasl_templates.webapp.files import FileServer
from vasl_templates.webapp.utils import is_image_file
from vasl_templates.webapp.utils import is_image_file, resize_image_response
_vo_notes_lock = threading.RLock() # nb: this controls the cached V/O notes and the FileServer
_cached_vo_notes = None
@ -127,27 +125,8 @@ def get_vo_note( vo_type, nat, key ):
fname = vo_notes.get( nat, {} ).get( key )
resp = _vo_notes_file_server.serve_file( fname )
# check if we should resize the file
scaling = request.args.get( "scaling" ) # nb: allow individual notes to set their scaling
if not scaling:
scaling = app.config.get( "CHAPTER_H_NOTE_SCALING", 100 )
if scaling == 100:
# nope - just return the file as it is
return resp
else:
# yup - make it so
buf = io.BytesIO()
resp.direct_passthrough = False
buf.write( resp.get_data() )
buf.seek( 0 )
img = Image.open( buf )
width = int( img.size[0] * float(scaling) / 100 )
height = int( img.size[1] * float(scaling) / 100 )
img = img.resize( (width,height), Image.ANTIALIAS )
buf = io.BytesIO()
img.save( buf, format="PNG" )
buf.seek( 0 )
return send_file( buf, mimetype="image/png" )
default_scaling = app.config.get( "CHAPTER_H_NOTE_SCALING", 100 )
return resize_image_response( resp, default_scaling=default_scaling )
# ---------------------------------------------------------------------

Loading…
Cancel
Save