feat(share): rich link previews (Open Graph) for /watch pages
A shared /watch/{token} link is a client-rendered SPA, so a social crawler only
saw the generic index.html — a blank link card. The server now injects per-video
Open Graph / Twitter tags (title, channel, thumbnail) into the served HTML for
that route, so links unfurl richly in Messenger and other chat apps; real
browsers ignore the extra tags and hydrate the page as usual. Password /
expired / invalid links fall back to the generic card with no metadata leak.
Also shortens the generic site description used for search engines and link
previews.
This commit is contained in:
parent
8c86c6b4a8
commit
8591e45747
3 changed files with 107 additions and 3 deletions
|
|
@ -20,7 +20,7 @@ if not _siftlode_logger.handlers:
|
|||
|
||||
log = logging.getLogger("siftlode.app")
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
|
||||
|
|
@ -178,6 +178,21 @@ async def index() -> FileResponse:
|
|||
return FileResponse(INDEX_HTML, headers=INDEX_HEADERS)
|
||||
|
||||
|
||||
@app.get("/watch/{token}")
|
||||
async def watch_page(token: str):
|
||||
"""Serve the SPA for a public share link, but with per-video Open Graph tags injected so the
|
||||
link unfurls richly (title/channel/thumbnail) in Messenger/social crawlers. A real browser
|
||||
ignores the extra tags and hydrates WatchPage as usual; invalid/expired/password links fall
|
||||
back to the generic card."""
|
||||
from app.downloads import og
|
||||
|
||||
with SessionLocal() as db:
|
||||
rendered = og.render_watch_html(token, INDEX_HTML, db)
|
||||
if rendered is None:
|
||||
return FileResponse(INDEX_HTML, headers=INDEX_HEADERS)
|
||||
return HTMLResponse(rendered, headers=INDEX_HEADERS)
|
||||
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def spa_fallback(full_path: str) -> FileResponse:
|
||||
# Client-side routes fall back to index.html; real API/asset paths are matched above.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue