1
0
Fork 0
mirror of https://github.com/Findus23/cr-search.git synced 2024-09-19 15:23:44 +02:00

server side template rendering

This commit is contained in:
Lukas Winkler 2021-11-28 19:37:43 +01:00
parent ba6bf5befa
commit 13594241cf
Signed by: lukas
GPG key ID: 54DE4D798D244853
11 changed files with 226 additions and 5 deletions

2
app.py
View file

@ -23,7 +23,7 @@ else:
CACHE_TYPE = "NullCache"
# Create a Flask WSGI app and configure it using values from the module.
app = Flask(__name__)
app = Flask(__name__,static_folder='web/dist/assets/',static_url_path='/assets/')
app.config.from_object(__name__)
cache = Cache(app)
flask_db = FlaskDB(app)

View file

@ -310,5 +310,9 @@ series_data = [
slug="ClubOfMisfits",
videos=["PRmVQKOy9Bo"]
)
]
series_data_by_slug = {}
for series in series_data:
series_data_by_slug[series.slug] = series

54
poetry.lock generated
View file

@ -119,6 +119,7 @@ spacy = ">=3.2.0,<3.3.0"
[package.source]
type = "url"
url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.2.0/en_core_web_md-3.2.0.tar.gz"
[[package]]
name = "flask"
version = "2.0.2"
@ -275,6 +276,14 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "pillow"
version = "8.4.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "preshed"
version = "3.0.6"
@ -653,7 +662,7 @@ python-versions = "*"
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "e78a950da9152281b77bedf02d5880ca8585cee5c4cff9795be982b9118dd68e"
content-hash = "03b797097ee3ce958b736a02b1c868e627fad600b5f669eeaf287d4aa318419b"
[metadata.files]
about-time = [
@ -853,6 +862,49 @@ pathy = [
peewee = [
{file = "peewee-3.14.8.tar.gz", hash = "sha256:01bd7f734defb08d7a3346a0c0ca7011bc8d0d685934ec0e001b3371d522ec53"},
]
pillow = [
{file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"},
{file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"},
{file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"},
{file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"},
{file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"},
{file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"},
{file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"},
{file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"},
{file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"},
{file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"},
{file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"},
{file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"},
{file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"},
{file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"},
{file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"},
{file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"},
{file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"},
{file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"},
{file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"},
{file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"},
{file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"},
{file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"},
{file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"},
{file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"},
{file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"},
{file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"},
{file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"},
{file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"},
{file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"},
{file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"},
{file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"},
{file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"},
{file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"},
{file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"},
{file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"},
{file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"},
{file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"},
{file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"},
{file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"},
{file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"},
{file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
]
preshed = [
{file = "preshed-3.0.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a9683730127658b531120b4ed5cff1f2a567318ab75e9ab0f22cc84ae1486c23"},
{file = "preshed-3.0.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c98f725d8478f3ade4ab1ea00f50a92d2d9406d37276bc46fd8bab1d47452c4"},

View file

@ -21,6 +21,7 @@ blinker = "^1.4" # tmp workaround to make flask-sentry work
Flask-Caching = "^1.10.1"
redis = "^4.0.2"
prettytable = "^2.2.1"
Pillow = "^8.4.0"
[tool.poetry.dev-dependencies]

View file

@ -12,9 +12,11 @@ from models import *
# logger = logging.getLogger('peewee')
# logger.addHandler(logging.StreamHandler())
# logger.setLevel(logging.DEBUG)
from ssr import ssr_routes
from stats import TotalWords, MostCommonNounChunks, LongestNounChunks, LinesPerPerson, aggregate_stats
from suggestions import suggestions
app.register_blueprint(ssr_routes)
def add_cors(response: Response) -> Response:
header = response.headers

143
ssr.py Normal file
View file

@ -0,0 +1,143 @@
"""
do some kind of pseudo SSR, modifying the index.html to contain metadata
"""
import textwrap
from io import BytesIO
from textwrap import shorten
from urllib.parse import unquote
from PIL import Image, ImageFont, ImageDraw
from flask import Blueprint, redirect, render_template, abort, request, send_file
from playhouse.flask_utils import get_object_or_404
from app import cache
from data import series_data_by_slug
from models import Episode, Series
ssr_routes = Blueprint("ssr_routes", __name__, template_folder="templates")
with open("./web/dist/index.html") as f:
index_html = f.read()
placeholder_token = '<title>CR Search</title>'
def draw_image(text: str, description: str, subtitle=None) -> BytesIO:
text = text.replace("| CR Search", "").strip()
text = shorten(text, 25, placeholder=" ...")
if subtitle:
text += "\n" + subtitle
width, height = (1200, 600)
img = Image.open("./web/src/assets/background_small.png")
mr_eves = ImageFont.truetype("web/fonts/Mr Eaves/Mr Eaves Small Caps.otf", 50)
title_font = ImageFont.truetype("web/fonts/Nodesto Caps Condensed/Nodesto Caps Condensed.otf", 120)
small_font = ImageFont.truetype("web/fonts/Scaly Sans/Scaly Sans.otf", 30)
draw = ImageDraw.Draw(img)
w, h = draw.multiline_textsize(text, title_font)
draw.multiline_text(((width - w) / 2, (height - h) / 4), text,
font=title_font, align="center",
fill="#58180d")
description = textwrap.fill(description, 50)
w, h = draw.multiline_textsize(description, mr_eves)
draw.multiline_text(((width - w) / 2, (height - h) / 3 * 2), description,
font=mr_eves, align="center",
fill="black")
footer_text = "Critical Role Search"
w, h = draw.multiline_textsize(footer_text, small_font)
draw.multiline_text(((width - w - 10), (height - h - 10)), footer_text,
font=small_font, align="center",
fill="black")
url = request.url.split("?")[0]
url = unquote(url)
if len(url) > 50:
url = "/".join(url.split("/")[:-1])
w, h = draw.multiline_textsize(url, small_font)
draw.multiline_text((10, (height - h - 10)), url,
font=small_font, align="center",
fill="black")
byte_io = BytesIO()
img.save(byte_io, "PNG", optimize=True)
byte_io.seek(0)
return byte_io
@ssr_routes.route("/", defaults={'something': None})
@ssr_routes.route("/<string:something>/")
def home_redirect(something):
return redirect("/campaign3/10/")
@ssr_routes.route("/episodes")
@cache.cached(timeout=60 * 60 * 24 * 30, query_string=True)
def episodes():
num = Episode.select().count()
description = f"Overview over {num} imported episodes of Critical Role"
title = "Episode Overview | CR Search"
if request.args.get("image", None) == "true":
return send_file(draw_image(title, description), mimetype="image/png")
additional_html = render_template(
"header.html",
description=description,
title=title,
url=request.url
)
return index_html.replace(placeholder_token, additional_html)
@ssr_routes.route("/transcript/<string:series>/<string:episode_number>/")
@cache.cached(timeout=60 * 60 * 24 * 30, query_string=True)
def transcript(series, episode_number):
episode = get_object_or_404(Episode.select(Episode, Series).where(
(Episode.episode_number == episode_number)
&
(Episode.series.slug == series)
).join(Series))
description = f"Browse through the transcript of episode {episode_number} of {episode.series.title} (“{episode.pretty_title}”)"
title = f"{episode.pretty_title} | Transcript | CR Search"
if request.args.get("image", None) == "true":
return send_file(draw_image(episode.pretty_title, description, subtitle="Transcript"), mimetype="image/png")
additional_html = render_template(
"header.html",
description=description,
title=title,
url=request.url
)
return index_html.replace(placeholder_token, additional_html)
@ssr_routes.route("/<string:series_slug>/<string:episodes>/<string:keyword>")
@ssr_routes.route("/<string:series_slug>/<string:episodes>", defaults={'keyword': None})
@ssr_routes.route("/<string:series_slug>/<string:episodes>/", defaults={'keyword': None})
@cache.cached(timeout=60 * 60 * 24 * 30, query_string=True)
def search(series_slug, episodes, keyword):
one_shot = Episode.select(Episode, Series).where(
Episode.series.slug == series_slug
).join(Series).count() == 1
try:
series = series_data_by_slug[series_slug]
except KeyError:
return abort(404)
if keyword:
if one_shot:
description = f"Search for “{keyword}” in {series.name}"
else:
description = f"Search for “{keyword}” up to episode {episodes} of {series.name}"
title = f"{keyword} | CR Search"
else:
description = f"Search through {series.name} of Critical Role"
title = f"{series.name} | CR Search"
if request.args.get("image", None) == "true":
return send_file(draw_image(title, description), mimetype="image/png")
additional_html = render_template(
"header.html",
description=description,
title=title,
url=request.url
)
print(additional_html)
return index_html.replace(placeholder_token, additional_html)

12
templates/header.html Normal file
View file

@ -0,0 +1,12 @@
<title>{{ title|default("CR Search") }}</title>
<meta name="description" content="{{ description }}">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@lw1_at">
<meta property="og:type" content="website"/>
<meta property="og:url" content="{{ url }}">
<meta property="og:title" content="{{ title }}">
<meta property="og:description" content="{{ description }}"/>
<meta property="og:image" content="{{ url }}?image=true"/>
<meta property="og:image:width" content="1200"/>
<meta property="og:image:height" content="600"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View file

@ -11,7 +11,7 @@ export default new Router({
routes: [
{
path: "/",
redirect: "/campaign2/10/",
redirect: "/campaign3/10/",
name: "home"
},
{

View file

@ -42,7 +42,8 @@
<input v-if="!isOneShot" title="search until episode number"
class="form-control" type="number" v-model="episode"
min="1" :max="seriesLength">
<span>in</span>
<span v-if="isOneShot">in</span>
<span v-if="!isOneShot">of</span>
<!-- <select title="campaign selection" class="custom-select" v-model="series">-->
<!-- <option v-for="series in serverData.series" v-bind:value="series.slug">-->
<!-- {{ series.title }}-->

6
web/vue.config.js Normal file
View file

@ -0,0 +1,6 @@
/**
* @type {import('@vue/cli-service').ProjectOptions}
*/
module.exports = {
assetsDir: "assets/"
}