import os import os.path import html import urllib.parse import itertools from typing import Dict, Any, Callable, Tuple, List, Iterable from http import HTTPStatus from pathlib import Path import requests FRONTEND_WEB_ROOT = Path(os.environ["SZURUBOORU_WEB_ROOT"]) with open(FRONTEND_WEB_ROOT / "index.htm") as f: INDEX_HTML = f.read() BACKEND_BASE_URL = os.environ["SZURUBOORU_BASE_URL"] PUBLIC_BASE_URL = os.environ["SZURUBOORU_PUBLIC_BASE_URL"] Metadata = Iterable[Tuple[str, str]] def general_embed(server_info: Dict[str, Any]) -> Metadata: yield "og:site_name", server_info["config"]["name"] def user_embed(username: str) -> Metadata: yield "og:type", "profile" yield "profile:username", username yield "og:title", username yield "og:image:height", "128" yield "og:image:width", "128" user = requests.get(BACKEND_BASE_URL + "/api/user/" + username).json() yield "og:image:url", urllib.parse.join(PUBLIC_BASE_URL, user["avatarUrl"]) def _image_embed(post: Dict[str, Any], skip_twitter_player=False) -> Metadata: url = PUBLIC_BASE_URL + post["contentUrl"] if post["type"] == "video": prefix = "og:video" yield "og:image", PUBLIC_BASE_URL + post["thumbnailUrl"] if not skip_twitter_player: yield "twitter:card", "player" yield "twitter:player", PUBLIC_BASE_URL + f"/player/{post['id']}" yield "twitter:player:width", str(post["canvasWidth"]) yield "twitter:player:height", str(post["canvasHeight"]) else: prefix = "og:image" yield "twitter:card", "summary_large_image" yield prefix + ":width", str(post["canvasWidth"]) yield prefix + ":height", str(post["canvasHeight"]) yield prefix, url if BACKEND_BASE_URL.startswith('https://'): yield prefix + ":secure_url", url yield prefix + ":type", post["mimeType"] def _author_embed(user: Dict[str, Any]) -> Metadata: yield "article:author", PUBLIC_BASE_URL + "/user/" + user["name"] def post_embed(post_id: int, *, skip_twitter_player=False) -> Metadata: post = requests.get(BACKEND_BASE_URL + f"/api/post/{post_id}").json() yield "og:type", "article" yield from _author_embed(post["user"]) yield "og:title", post["user"]["name"] if post["tags"]: value = "Tags: " + ", ".join(tag["names"][0] for tag in post["tags"]) yield "og:description", value yield "description", value yield from _image_embed(post, skip_twitter_player) def homepage_embed(server_info: Dict[str, Any], *, skip_twitter_player=False) -> Metadata: yield "og:title", server_info["config"]["name"] yield "og:type", "website" post = server_info["featuredPost"] if post is not None: yield from _image_embed(post, skip_twitter_player) def render_embed(metadata: Metadata) -> str: out = [] for k, v in metadata: k, v = html.escape(k), html.escape(v) out.append(f'') return ''.join(out) def serve_twitter_video_player(start_response, post_id: int): r = requests.get(BACKEND_BASE_URL + f"/api/post/{post_id}") data = r.json() if r.status_code != HTTPStatus.OK: start_response(r.status_code, [("Content-Type", "text/html; charset=utf-8")]) yield f"

{html.escape(data['title'])}

{html.escape(data['description'])}

".encode("utf-8"), start_response(http_status.OK, [("Content-Type", "text/html; charset=utf-8")]) post = data yield b"" yield b"" yield b"" def application(env: Dict[str, Any], start_response: Callable[[str, Any], Any]) -> Tuple[bytes]: def serve_file(path): start_response(str(int(HTTPStatus.OK)), [("X-Accel-Redirect", path)]) return () def serve_without_embed(): return serve_file("/index.htm") method = env["REQUEST_METHOD"] if method != "GET": start_response(str(int(HTTPStatus.BAD_REQUEST)), [("Content-Type", "text/plain")]) return (b"Bad request",) path = env["PATH_INFO"].lstrip("/") path = path.encode("latin-1").decode("utf-8") # PEP-3333 if path and (FRONTEND_WEB_ROOT / path).exists(): return serve_file("/" + path) path = "/" + path path_components = path.split("/") if path_components[1] not in {"post", "user", "", "player"}: # serve index.htm like normal return serve_without_embed() if path_components[1] == "player" and path_components[2]: try: post_id = int(path_components[2]) except ValueError: pass else: return serve_twitter_video_player(start_response, post_id) server_info = requests.get(BACKEND_BASE_URL + "/api/info").json() privileges = server_info["config"]["privileges"] # Telegram prefers twitter:card to og:video, so we need to skip the former in order for videos to play inline skip_twitter_player = env["HTTP_USER_AGENT"].startswith("TelegramBot") if path_components[1] == "user": username = path_components[2] if privileges["users:view"] != "anonymous" or not username: return serve_without_embed() metadata = user_embed(username) elif path_components[1] == "post": try: post_id = int(path_components[2]) except ValueError: return serve_without_embed() if privileges["posts:view"] != "anonymous": return serve_without_embed() metadata = post_embed(post_id, skip_twitter_player=skip_twitter_player) elif path_components[1] == "": metadata = homepage_embed(server_info, skip_twitter_player=skip_twitter_player) metadata = itertools.chain(general_embed(server_info), metadata) body = INDEX_HTML.replace("", render_embed(metadata)).encode("utf-8") start_response(str(int(HTTPStatus.OK)), [("Content-Type", "text/html"), ("Content-Length", str(len(body)))]) return (body,)