- app/api/seo.py (+ app/templates/seo/*): publiczny HTML SEO router (programmatic entity long-tail: performer/studio/scene/landing/2257), bez api-key. Importowany przez main.py — wymagany do uruchomienia, dotąd untracked. Opsec-clean (brak VPS IP/sekretów). - CLAUDE.md: instrukcje projektu (dotąd untracked). - .gitignore: .nimbalyst/ (lokalne tracker-tooling, nie dla OSS repo). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.6 KiB
HTML
54 lines
2.6 KiB
HTML
{% extends "seo/base.html" %}
|
||
{% set perf_names = performers|map(attribute="canonical_name")|list %}
|
||
{% block title %}{{ scene.title }}{% if studio %} – {{ studio.name }}{% endif %} | Goon{% endblock %}
|
||
{% block description %}{% if scene.description %}{{ scene.description|truncate(155) }}{% else %}Watch {{ scene.title }}{% if perf_names %} with {{ perf_names|join(", ") }}{% endif %}{% if studio %} ({{ studio.name }}){% endif %}. Available on {{ sources|length }} source{{ 's' if sources|length != 1 else '' }} — indexed by Goon.{% endif %}{% endblock %}
|
||
{% block og_type %}video.other{% endblock %}
|
||
{% block og_image %}{% if thumb %}<meta property="og:image" content="{{ thumb if thumb.startswith('http') else base_url ~ thumb }}">{% endif %}{% endblock %}
|
||
|
||
{% block jsonld %}
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context":"https://schema.org",
|
||
"@type":"VideoObject",
|
||
"name": {{ scene.title|tojson }},
|
||
"description": {{ (scene.description or scene.title)|tojson }},
|
||
{% if thumb %}"thumbnailUrl": {{ (thumb if thumb.startswith('http') else base_url ~ thumb)|tojson }},{% endif %}
|
||
{% if scene.release_date %}"uploadDate": {{ scene.release_date.isoformat()|tojson }},{% endif %}
|
||
{% if iso_duration %}"duration": {{ iso_duration|tojson }},{% endif %}
|
||
{% if perf_names %}"actor": {{ perf_names|map("string")|list|tojson }},{% endif %}
|
||
{% if studio %}"productionCompany": {{ {"@type":"Organization","name": studio.name}|tojson }},{% endif %}
|
||
"url": {{ canonical|tojson }}
|
||
}
|
||
</script>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<h1>{{ scene.title }}</h1>
|
||
<p class="meta">
|
||
{% if studio %}<a href="{{ base_url }}/studio/{{ studio.slug }}">{{ studio.name }}</a>{% endif %}
|
||
{% if scene.release_date %} · {{ scene.release_date }}{% endif %}
|
||
{% if scene.duration_sec %} · {{ (scene.duration_sec // 60) }} min{% endif %}
|
||
</p>
|
||
|
||
{% if performers %}
|
||
<p>Featuring:
|
||
{% for p in performers %}<a href="{{ base_url }}/p/{{ p.slug }}">{{ p.canonical_name }}</a>{% if not loop.last %}, {% endif %}{% endfor %}
|
||
</p>
|
||
{% endif %}
|
||
|
||
{% if scene.description %}<p>{{ scene.description }}</p>{% endif %}
|
||
|
||
<h2>Watch on {{ sources|length }} source{{ 's' if sources|length != 1 else '' }}</h2>
|
||
<div class="sources">
|
||
{% for src in sources %}
|
||
<a href="{{ src.page_url }}" rel="nofollow sponsored noopener" target="_blank">
|
||
{{ src.label }}{% if src.quality %} · {{ src.quality }}{% endif %} ↗</a>
|
||
{% endfor %}
|
||
</div>
|
||
|
||
{% if tags %}
|
||
<p class="tags" style="margin-top:20px">
|
||
{% for t in tags %}<span>#{{ t.name }}</span>{% if not loop.last %} {% endif %}{% endfor %}
|
||
</p>
|
||
{% endif %}
|
||
{% endblock %}
|