Set page_url in views

This commit is contained in:
Joakim Hellsén 2026-03-17 04:51:43 +01:00
commit d05996fd3b
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
5 changed files with 40 additions and 8 deletions

View file

@ -5,6 +5,7 @@ from typing import TypedDict
class SeoMeta(TypedDict, total=False):
"""Shared typed optional SEO metadata for template context generation."""
page_url: str | None
page_image: str | None
page_image_width: int | None
page_image_height: int | None

View file

@ -73,6 +73,7 @@ DEFAULT_SITE_DESCRIPTION = "Archive of Twitch drops, campaigns, rewards, and mor
def _build_seo_context( # noqa: PLR0913, PLR0917
page_title: str = "ttvdrops",
page_description: str | None = None,
page_url: str | None = None,
page_image: str | None = None,
page_image_width: int | None = None,
page_image_height: int | None = None,
@ -89,6 +90,7 @@ def _build_seo_context( # noqa: PLR0913, PLR0917
Args:
page_title: Page title (shown in browser tab, og:title).
page_description: Page description (meta description, og:description).
page_url: Canonical absolute URL for the current page.
page_image: Image URL for og:image meta tag.
page_image_width: Width of the image in pixels.
page_image_height: Height of the image in pixels.
@ -115,6 +117,8 @@ def _build_seo_context( # noqa: PLR0913, PLR0917
"og_type": og_type,
"robots_directive": robots_directive,
}
if page_url:
context["page_url"] = page_url
if page_image:
context["page_image"] = page_image
if page_image_width and page_image_height:
@ -437,6 +441,7 @@ def docs_rss_view(request: HttpRequest) -> HttpResponse:
seo_context: dict[str, Any] = _build_seo_context(
page_title="Twitch RSS Feeds",
page_description="RSS feeds for Twitch drops.",
page_url=request.build_absolute_uri(reverse("core:docs_rss")),
)
return render(
request,
@ -836,6 +841,7 @@ def search_view(request: HttpRequest) -> HttpResponse:
seo_context: dict[str, Any] = _build_seo_context(
page_title=page_title,
page_description=page_description,
page_url=request.build_absolute_uri(reverse("core:search")),
)
return render(
request,

View file

@ -56,6 +56,8 @@ def _build_seo_context(
"robots_directive": "index, follow",
}
if seo_meta:
if seo_meta.get("page_url"):
context["page_url"] = seo_meta["page_url"]
if seo_meta.get("og_type"):
context["og_type"] = seo_meta["og_type"]
if seo_meta.get("robots_directive"):
@ -225,6 +227,7 @@ def campaign_list_view(request: HttpRequest) -> HttpResponse:
seo_context: dict[str, str] = _build_seo_context(
page_title=title,
page_description="Browse Kick drop campaigns.",
seo_meta={"page_url": request.build_absolute_uri(base_url)},
)
return render(
request,

View file

@ -16,9 +16,6 @@
{# - robots_directive: str - robots meta content (default: "index, follow") #}
{# #}
{% load static %}
{# Preconnect to external resources for performance #}
<link rel="preconnect" href="https://static-cdn.jtvnw.net" />
<link rel="dns-prefetch" href="https://static-cdn.jtvnw.net" />
{# Description meta tag #}
<meta name="description"
content="{% firstof page_description 'ttvdrops - Track Twitch drops.' %}" />
@ -26,7 +23,7 @@
<meta name="robots"
content="{% firstof robots_directive 'index, follow' %}" />
{# Author and Copyright #}
<meta name="author" content="TheLovinator1" />
<meta name="author" content="TheLovinator" />
<meta name="copyright"
content="This work is dedicated to the public domain under CC0 1.0 Universal." />
{# Open Graph tags for social sharing #}
@ -45,10 +42,14 @@
{% endif %}
{# Twitter Card tags for rich previews #}
<meta name="twitter:card"
content="{% if page_image %}summary_large_image{% else %}summary{% endif %}" />
content="{% if page_image %}
summary_large_image
{% else %}
summary
{% endif %}" />
<meta name="twitter:title" content="{% firstof page_title 'ttvdrops' %}" />
<meta name="twitter:description"
content="{% firstof page_description 'ttvdrops - Track Twitch drops.' %}" />
content="{% firstof page_description 'ttvdrops - Twitch and Kick drops.' %}" />
{% if page_image %}<meta name="twitter:image" content="{{ page_image }}" />{% endif %}
{# Article dates for content pages #}
{% if published_date %}<meta property="article:published_time" content="{{ published_date }}" />{% endif %}
@ -61,6 +62,22 @@
{% for link in pagination_info %}<link rel="{{ link.rel }}" href="{{ link.url }}" />{% endfor %}
{% endif %}
{# Schema.org JSON-LD structured data #}
{% if schema_data %}<script type="application/ld+json">{{ schema_data|safe }}</script>{% endif %}
{% if schema_data %}
<script type="application/ld+json">
{
{
schema_data | safe
}
}
</script>
{% endif %}
{# Breadcrumb schema #}
{% if breadcrumb_schema %}<script type="application/ld+json">{{ breadcrumb_schema|safe }}</script>{% endif %}
{% if breadcrumb_schema %}
<script type="application/ld+json">
{
{
breadcrumb_schema | safe
}
}
</script>
{% endif %}

View file

@ -116,6 +116,8 @@ def _build_seo_context(
"robots_directive": "index, follow",
}
if seo_meta:
if seo_meta.get("page_url"):
context["page_url"] = seo_meta["page_url"]
if seo_meta.get("og_type"):
context["og_type"] = seo_meta["og_type"]
if seo_meta.get("robots_directive"):
@ -483,6 +485,7 @@ def drop_campaign_list_view(request: HttpRequest) -> HttpResponse: # noqa: PLR0
page_title=title,
page_description=description,
seo_meta={
"page_url": request.build_absolute_uri(base_url),
"pagination_info": pagination_info,
"schema_data": collection_schema,
},
@ -1336,6 +1339,7 @@ def reward_campaign_list_view(request: HttpRequest) -> HttpResponse:
page_title=title,
page_description=description,
seo_meta={
"page_url": request.build_absolute_uri(base_url),
"pagination_info": pagination_info,
"schema_data": collection_schema,
},
@ -1557,6 +1561,7 @@ class ChannelListView(ListView):
page_title="Twitch Channels",
page_description="List of Twitch channels participating in drop campaigns.",
seo_meta={
"page_url": self.request.build_absolute_uri(base_url),
"pagination_info": pagination_info,
"schema_data": collection_schema,
},