arivertisements/preview.py
Arija A. 0ac6df15d2
Update preview.py with new embed markup
Signed-off-by: Arija A. <ari@ari.lt>
2026-03-15 12:59:20 +02:00

185 lines
6.1 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Preview Arivertisements embed locally"""
import html
import http.server
import os
import socketserver
import sys
import threading
import typing as t
import webbrowser
from warnings import filterwarnings as filter_warnings
PORT: t.Final[int] = 8000
def parse_meta(filepath: str) -> t.Dict[str, str]:
"""Parse metadata file into a dictionary"""
meta: t.Dict[str, str] = {}
with open(filepath, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or ":" not in line:
continue
key, val = line.split(":", 1)
meta[key.lower()] = val.strip()
for rkey in ("filename", "alt", "author", "contact", "statement"):
if rkey not in meta:
raise ValueError(f"{rkey} missing in metadata {filepath!r}!")
return meta
def build_embed_html(meta: t.Dict[str, str], image_filename: str) -> str:
"""Build the exact embed HTML with metadata values injected"""
bg: str = meta.get("bg", "#fff")
fg: str = meta.get("fg", "#000")
description: str = f'{meta.get("alt")} (image by {meta.get("author")})'
license_: str = meta.get("license", "CC-BY-NC-SA 4.0")
alt_text: str = meta.get("alt", "Alternative text")
to_link: str = meta.get("to", "#")
author: str = meta.get("author", "Joe Doe")
contact: str = meta.get("contact", "example@example.com").replace(" at ", "@")
# statement: str = meta.get("statement", "")
source: t.Optional[str] = meta.get("source")
description_esc: str = html.escape(description)
license_esc: str = html.escape(license_)
alt_text_esc: str = html.escape(alt_text)
to_link_esc: str = html.escape(to_link)
author_esc: str = html.escape(author)
contact_esc: str = "".join(f"&#{ord(c)};" for c in contact)
# statement_esc = html.escape(statement)
# source_esc: str = html.escape(source) if source else ""
source_html: str = (
f'(<a href="{source}" rel="noopener noreferrer" target="_blank">source here</a>) '
if source
else ""
)
img_src = f"/img/{html.escape(image_filename)}"
return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{html.escape(image_filename)}</title>
<base target="_blank">
<meta name="description" content="{description_esc}">
{source_html}
<meta name="license" content="{license_esc}">
<meta name="theme-color" content="{bg}">
<meta name="robots" content="noindex,noarchive,noimageindex,follow">
<meta name="author" content="{author_esc}">
<meta property="og:locale" content="en_GB">
<meta property="og:url" content="http://127.0.0.1:{PORT}/">
<link rel="canonical" href="http://127.0.0.1:{PORT}/">
<style>
html,body{{overflow:hidden;margin:0;height:100%;background:{bg};color:{fg};font-family:monospace}}
body{{display:table;width:100%;text-align:center}}
.v{{display:table-cell;vertical-align:middle}}
.b{{display:inline-block}}
img{{display:block;vertical-align:middle;width:722px;max-width:100%;height:auto;border:0}}
p{{margin:.4em 0 0;font-size:.75em}}
a{{color:inherit}}
</style>
</head>
<body>
<div class="v">
<div class="b"><a href="{to_link_esc}" rel="noopener noreferrer"><img referrerpolicy="no-referrer" decoding="async" border="0" src="{img_src}" loading="lazy" width="722" height="84" alt="{alt_text_esc}"></a><p><strong>&copy; {author_esc} &lt;<a href="mailto:{contact_esc}" rel="noopener noreferrer nofollow">contact</a>&gt; under {license_esc} | <a href="https://ad.ari.lt/" rel="noopener noreferrer">Arivertisements</a></strong></p>
</div>
</div>
</body>
</html>"""
class PreviewHandler(http.server.SimpleHTTPRequestHandler):
"""HTTP handler serving the preview HTML at /"""
def __init__(self, *args: t.Any, html_content: str = "", **kwargs: t.Any) -> None:
self.html_content = html_content
super().__init__(*args, **kwargs)
def do_GET(self) -> None:
if self.path in ("/", "/index.html"):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(self.html_content.encode("utf-8"))
else:
super().do_GET()
def run_server(server: socketserver.TCPServer) -> None:
"""Run the HTTP server"""
server.serve_forever()
def main() -> int:
"""Entry point"""
if len(sys.argv) != 2:
print("Usage: python3 preview.py image-name.png")
return 1
image_name: str = sys.argv[1]
img_path: str = os.path.join("img", image_name)
meta_path: str = os.path.join("meta", os.path.splitext(image_name)[0] + ".txt")
if not os.path.isfile(img_path):
print(f"Image file '{img_path}' not found.")
return 1
if not os.path.isfile(meta_path):
print(f"Meta file '{meta_path}' not found.")
return 1
try:
meta: t.Dict[str, str] = parse_meta(meta_path)
except Exception as e:
print(f"Error parsing meta file: {e}")
return 1
html_content: str = build_embed_html(meta, image_name)
socketserver.TCPServer.allow_reuse_address = True
handler_class = lambda *args, **kwargs: PreviewHandler(
*args, html_content=html_content, **kwargs
)
with socketserver.TCPServer(("", PORT), handler_class) as httpd:
print(f"Serving preview at http://127.0.0.1:{PORT}")
thread: threading.Thread = threading.Thread(
target=run_server, args=(httpd,), daemon=True
)
thread.start()
webbrowser.open_new(f"http://127.0.0.1:{PORT}")
try:
while thread.is_alive():
thread.join(0.5)
except KeyboardInterrupt:
print("\nShutting the server down...")
httpd.shutdown()
httpd.server_close()
return 0
return 0
if __name__ == "__main__":
assert main.__annotations__.get("return") is int, "main() should return an integer"
filter_warnings("error", category=Warning)
raise SystemExit(main())