#!/usr/bin/env python3 """Serve a local static site inside a Streamlit Community Cloud app. Performance rules: - Inline only tiny local assets (≤8KB). Larger files load from jsDelivr/GitHub CDN. - Prefer Pexels CDN for known video IDs; shrink remote gallery images. - Keep CSS/JS inlined to avoid extra RTTs inside the Streamlit iframe. """ from __future__ import annotations import base64 import json import mimetypes import re from pathlib import Path from urllib.parse import parse_qsl, unquote, urlencode, urlparse, urlunparse _LINK_CSS = re.compile( r']+rel=["\']stylesheet["\'][^>]*href=["\']([^"\']+)["\'][^>]*/?>', re.I, ) _MODULE_PRELOAD = re.compile( r']+rel=["\']modulepreload["\'][^>]*/?>', re.I, ) _MANIFEST_OR_SW = re.compile( r']+rel=["\']manifest["\'][^>]*/?>|' r']+id=["\']vite-plugin-pwa:register-sw["\'][^>]*>\s*|' r']+src=["\'][^"\']*registerSW\.js["\'][^>]*>\s*', re.I, ) _SCRIPT_SRC = re.compile( r']*?)src=["\']([^"\']+)["\']([^>]*)>\s*', re.I, ) _ATTR_URL = re.compile( r'''((?:src|href|poster|data-src)=["'])([^"']+)(["'])''', re.I, ) _JS_FROM = re.compile( r'''(?:from\s*|import\s*\(\s*|import\s+)["']([^"']+)["']''' ) _HTML_HREF = re.compile( r'''href=(["'])([^"']+\.html(?:#[^"']*)?)\1''', re.I, ) _HREF_ANY = re.compile( r'''href=(["'])([^"']+)\1''', re.I, ) # Prefer 720p/SD Pexels CDN over Hugging Face dataset redirects (multi-second TTFB). _PEXELS_VIDEO_CDN: dict[str, str] = { "3680687": "https://videos.pexels.com/video-files/3680687/3680687-sd_960_540_25fps.mp4", "4109542": "https://videos.pexels.com/video-files/4109542/4109542-sd_960_540_25fps.mp4", "4519062": "https://videos.pexels.com/video-files/4519062/4519062-sd_960_540_25fps.mp4", "6288304": "https://videos.pexels.com/video-files/6288304/6288304-sd_960_540_25fps.mp4", "6529453": "https://videos.pexels.com/video-files/6529453/6529453-sd_960_540_30fps.mp4", "7015435": "https://videos.pexels.com/video-files/7015435/7015435-sd_960_540_30fps.mp4", "8523306": "https://videos.pexels.com/video-files/8523306/8523306-sd_960_540_30fps.mp4", "8964522": "https://videos.pexels.com/video-files/8964522/8964522-sd_960_540_25fps.mp4", "12769418": "https://videos.pexels.com/video-files/12769418/12769418-sd_960_540_25fps.mp4", "4053216": "https://videos.pexels.com/video-files/4053216/4053216-hd_1280_720_25fps.mp4", "5941023": "https://videos.pexels.com/video-files/5941023/5941023-hd_1280_720_25fps.mp4", } _INLINE_MAX = 8_192 # bytes — icons/SVG only _FAST_HEAD = """ """ def _data_uri(path: Path) -> str: mime = mimetypes.guess_type(str(path))[0] or "application/octet-stream" b64 = base64.b64encode(path.read_bytes()).decode("ascii") return f"data:{mime};base64,{b64}" def _strip_query(ref: str) -> str: return unquote(ref.split("?")[0].split("#")[0].strip()) def _resolve(base: Path, ref: str, site_root: Path | None = None) -> Path | None: ref = _strip_query(ref) if not ref or ref.startswith(("http://", "https://", "data:", "mailto:", "tel:", "//", "blob:")): return None root = (site_root or base).resolve() if ref.startswith("/"): cand = (root / ref.lstrip("/")).resolve() else: cand = (base / ref).resolve() try: cand.relative_to(root) except ValueError: return None return cand if cand.is_file() else None def _cdn_url(path: Path, site_root: Path, asset_cdn: str | None) -> str | None: if not asset_cdn: return None try: rel = path.resolve().relative_to(site_root.resolve()).as_posix() except ValueError: return None return asset_cdn.rstrip("/") + "/" + rel.lstrip("/") def _collect_modules(entry: Path) -> dict[Path, str]: paths: list[Path] = [] seen: set[Path] = set() queue = [entry.resolve()] while queue: path = queue.pop() if path in seen or not path.is_file(): continue seen.add(path) paths.append(path) text = path.read_text(encoding="utf-8", errors="replace") for m in _JS_FROM.finditer(text): spec = m.group(1) if spec.startswith(("http://", "https://", "data:", "virtual:")): continue dep = (path.parent / _strip_query(spec)).resolve() if dep.is_file() and dep not in seen: queue.append(dep) by_name: dict[str, list[Path]] = {} for p in paths: by_name.setdefault(p.name, []).append(p) out: dict[Path, str] = {} for p in paths: if len(by_name[p.name]) == 1: out[p] = f"virtual:{p.name}" else: out[p] = f"virtual:{p.as_posix()}" return out def _rewrite_module_imports(text: str, path: Path, id_for: dict[Path, str]) -> str: def repl(m: re.Match[str]) -> str: spec = m.group(1) if spec.startswith(("http://", "https://", "data:", "virtual:")): return m.group(0) dep = (path.parent / _strip_query(spec)).resolve() vid = id_for.get(dep) if not vid: return m.group(0) return m.group(0).replace(spec, vid, 1) return _JS_FROM.sub(repl, text) def _build_importmap_and_entries( html: str, base: Path, site_root: Path | None, ) -> str: entries: list[Path] = [] def find_modules(match: re.Match[str]) -> str: pre, src, post = match.group(1), match.group(2), match.group(3) attrs = f"{pre} {post}".lower() path = _resolve(base, src, site_root) if not path: return match.group(0) if "module" in attrs: entries.append(path) return f"" js = path.read_text(encoding="utf-8", errors="replace") return f"\n{js}\n" html = _SCRIPT_SRC.sub(find_modules, html) if not entries: return html id_for: dict[Path, str] = {} for entry in entries: id_for.update(_collect_modules(entry)) imports: dict[str, str] = {} for path, vid in id_for.items(): rewritten = _rewrite_module_imports( path.read_text(encoding="utf-8", errors="replace"), path, id_for ) b64 = base64.b64encode(rewritten.encode("utf-8")).decode("ascii") imports[vid] = f"data:text/javascript;base64,{b64}" blocks = [ f'' ] for entry in entries: blocks.append(f'') injection = "\n".join(blocks) if re.search(r"", html, re.I): return re.sub(r"", injection + "\n", html, count=1, flags=re.I) return injection + html def _inject_page_nav(html: str, routes: dict[str, str]) -> str: if not routes: return html home = routes.get("index.html", "home") def page_for(href: str) -> str | None: path_part = href.split("#", 1)[0] name = Path(urlparse(path_part).path).name if not name or name in (".", "./"): name = "index.html" return routes.get(name) def repl_href(m: re.Match[str]) -> str: quote, href = m.group(1), m.group(2) page = page_for(href) if not page: return m.group(0) frag = href.split("#", 1)[1] if "#" in href else "" q = f"?page={page}" + (f"#{frag}" if frag else "") return f'href={quote}{q}{quote} target="_top"' html = _HTML_HREF.sub(repl_href, html) html = re.sub( r'''href=(["'])\./\1''', rf'href=\1?page={home}\1 target="_top"', html, ) html = re.sub( r'''href=(["'])\./#(.*?)\1''', rf'href=\1?page={home}#\2\1 target="_top"', html, ) return html def _optimize_remote_url(url: str) -> str: """Shrink gallery images and swap HF-hosted Pexels clips onto videos.pexels.com.""" if not url or url.startswith("data:"): return url # HF / local filenames that embed a Pexels id m = re.search(r"pexels-video-(\d+)\.mp4", url, re.I) if not m: m = re.search(r"/videos/(?:hero/)?(\d+)\.mp4", url, re.I) if not m: m = re.search(r"video-files/(\d+)/", url, re.I) if m: pid = m.group(1) mapped = _PEXELS_VIDEO_CDN.get(pid) if mapped: return mapped # Prefer 720p over 1080p when already on Pexels CDN url = re.sub( rf"{pid}-hd_1920_1080_\d+fps\.mp4", f"{pid}-hd_1280_720_25fps.mp4", url, ) if "images.pexels.com" in url.lower(): parsed = urlparse(url) q = dict(parse_qsl(parsed.query, keep_blank_values=True)) # Gallery thumbs: small + compressed. Keep auto=compress. w = int(q.get("w") or "0") or 800 q["auto"] = "compress" q["cs"] = "tinysrgb" q["w"] = str(min(w, 480)) # Drop huge intrinsic hints that fight responsive layouts return urlunparse(parsed._replace(query=urlencode(q))) return url def _speed_tune_html(html: str) -> str: def repl(m: re.Match[str]) -> str: pre, url, post = m.group(1), m.group(2), m.group(3) return f"{pre}{_optimize_remote_url(url)}{post}" html = _ATTR_URL.sub(repl, html) # Also rewrite playlist JSON / inline strings that hold video URLs for pid, mapped in _PEXELS_VIDEO_CDN.items(): html = re.sub( rf"https://[^\"'\s]*pexels-video-{pid}\.mp4", mapped, html, flags=re.I, ) html = re.sub( rf"https://videos\.pexels\.com/video-files/{pid}/{pid}-hd_1920_1080_\d+fps\.mp4", mapped, html, flags=re.I, ) return html def _inject_streamlit_paths(html: str, paths: dict[str, str]) -> str: """Rewrite relative hrefs to Streamlit multipage paths (/webapp, /order, …).""" if not paths: return html ordered = sorted(paths.items(), key=lambda item: len(item[0]), reverse=True) def repl(m: re.Match[str]) -> str: quote, href = m.group(1), m.group(2) if href.startswith(("http://", "https://", "mailto:", "tel:", "data:", "#", "?")): return m.group(0) for old, new in ordered: if href == old: return f'href={quote}{new}{quote} target="_top"' if old.endswith("/") and href.startswith(old): suffix = href[len(old) :] return f'href={quote}{new.rstrip("/")}/{suffix.lstrip("/")}{quote} target="_top"' if href.startswith(old + "?") or href.startswith(old + "#"): return f'href={quote}{new}{href[len(old) :]}{quote} target="_top"' return m.group(0) html = _HREF_ANY.sub(repl, html) # JS redirects inside inlined scripts (e.g. order flow fallback). for old, new in ordered: html = html.replace(f'"{old}"', f'"{new}"') html = html.replace(f"'{old}'", f"'{new}'") return html def build_standalone_html( html_path: Path, *, site_root: Path | None = None, page_routes: dict[str, str] | None = None, asset_cdn: str | None = None, streamlit_paths: dict[str, str] | None = None, ) -> str: """Inline local CSS/JS; load images/video from CDN; preserve Vite ES modules.""" base = html_path.parent root = (site_root or base).resolve() html = html_path.read_text(encoding="utf-8", errors="replace") html = _MODULE_PRELOAD.sub("", html) html = _MANIFEST_OR_SW.sub("", html) html = _speed_tune_html(html) def inject_css(match: re.Match[str]) -> str: href = match.group(1) path = _resolve(base, href, root) if not path: return match.group(0) css = path.read_text(encoding="utf-8", errors="replace") def css_url(m: re.Match[str]) -> str: u = m.group(1).strip(" \"'") p = _resolve(path.parent, u, root) if not p: return m.group(0) if p.stat().st_size <= _INLINE_MAX: return f"url({_data_uri(p)})" cdn = _cdn_url(p, root, asset_cdn) if cdn: return f"url({cdn})" return m.group(0) css = re.sub(r"url\(([^)]+)\)", css_url, css) return f"" html = _LINK_CSS.sub(inject_css, html) html = _build_importmap_and_entries(html, base, root) def inject_asset(match: re.Match[str]) -> str: pre, src, post = match.group(1), match.group(2), match.group(3) # Remote URLs already optimized above if src.startswith(("http://", "https://", "data:", "//")): return match.group(0) path = _resolve(base, src, root) if not path: return match.group(0) if pre.lstrip().lower().startswith("href") and path.suffix.lower() == ".html": return match.group(0) if path.suffix.lower() in {".css", ".js", ".mjs"}: return match.group(0) size = path.stat().st_size if size <= _INLINE_MAX: return f"{pre}{_data_uri(path)}{post}" cdn = _cdn_url(path, root, asset_cdn) if cdn: return f"{pre}{cdn}{post}" # Last resort: skip huge base64 (keeps first paint fast; asset may 404 in iframe) if size > _INLINE_MAX: return match.group(0) return f"{pre}{_data_uri(path)}{post}" html = _ATTR_URL.sub(inject_asset, html) if page_routes: html = _inject_page_nav(html, page_routes) if streamlit_paths: html = _inject_streamlit_paths(html, streamlit_paths) # Inject fast-media head once if "st-fast-media" not in html: if re.search(r"]*>", html, re.I): html = re.sub(r"]*)>", r"" + _FAST_HEAD, html, count=1, flags=re.I) else: html = _FAST_HEAD + html return html def hide_streamlit_chrome() -> None: import streamlit as st st.markdown( """ """, unsafe_allow_html=True, ) def _query_page(default: str) -> str: """Read ?page= across Streamlit query-param API variants.""" import streamlit as st raw = None try: qp = st.query_params raw = qp.get("page", default) if hasattr(qp, "get") else qp["page"] except Exception: try: raw = (st.experimental_get_query_params() or {}).get("page", [default]) except Exception: raw = default if isinstance(raw, (list, tuple)): raw = raw[0] if raw else default page = str(raw or default).strip().lower() return page or default def _embed_html(html: str, height: int) -> None: """Embed HTML in a way that works on both older and newer Streamlit Cloud.""" import streamlit as st # Streamlit ≥1.56 prefers st.iframe for HTML strings (no scrolling= kwarg). if hasattr(st, "iframe"): try: st.iframe(html, height=int(height), width="stretch") return except TypeError: try: st.iframe(html, height=int(height)) return except Exception: pass import streamlit.components.v1 as components try: components.html(html, height=int(height), scrolling=True) except TypeError: # Newer wrappers may reject scrolling= components.html(html, height=int(height)) def render_live_site( html_path: Path, *, height: int = 920, about_title: str = "About this app", about_md: str = "", site_root: Path | None = None, page_routes: dict[str, str] | None = None, asset_cdn: str | None = None, streamlit_paths: dict[str, str] | None = None, **_extra: object, ) -> None: import streamlit as st hide_streamlit_chrome() html = build_standalone_html( html_path, site_root=site_root, page_routes=page_routes, asset_cdn=asset_cdn, streamlit_paths=streamlit_paths, ) _embed_html(html, height) if about_md: with st.expander(about_title, expanded=False): st.markdown(about_md) def render_multipage_site( pages: dict[str, Path], *, default: str = "home", height: int = 1100, about_title: str = "About this app", about_md: str = "", site_root: Path | None = None, asset_cdn: str | None = None, **_extra: object, ) -> None: """pages: query key -> html path (home->index.html, app->app.html).""" page = _query_page(default) if page not in pages: page = default routes = {path.name: key for key, path in pages.items()} render_live_site( pages[page], height=height, about_title=about_title, about_md=about_md, site_root=site_root, page_routes=routes, asset_cdn=asset_cdn, )