mirror of
https://github.com/Findus23/cr-search.git
synced 2024-09-11 06:03:45 +02:00
server side template rendering
This commit is contained in:
parent
ba6bf5befa
commit
13594241cf
11 changed files with 226 additions and 5 deletions
2
app.py
2
app.py
|
@ -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)
|
||||
|
|
6
data.py
6
data.py
|
@ -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
54
poetry.lock
generated
|
@ -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"},
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
143
ssr.py
Normal 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
12
templates/header.html
Normal 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"/>
|
BIN
web/src/assets/background_small.png
Normal file
BIN
web/src/assets/background_small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
|
@ -11,7 +11,7 @@ export default new Router({
|
|||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
redirect: "/campaign2/10/",
|
||||
redirect: "/campaign3/10/",
|
||||
name: "home"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
6
web/vue.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* @type {import('@vue/cli-service').ProjectOptions}
|
||||
*/
|
||||
module.exports = {
|
||||
assetsDir: "assets/"
|
||||
}
|
Loading…
Reference in a new issue