screentinker/scripts/indexnow-submit.sh
screentinker 2dc1d1279a
feat(seo): IndexNow + landing-page optimization (schema, FAQ, CWV, content) (#177)
* feat(seo): enable IndexNow (key file + submission script)

Instant re-crawl pings to Bing/Yandex/Seznam/Naver on content changes (Google ignores IndexNow
but uses the same sitemap). Hosts the ownership key at frontend/<key>.txt (served at
https://screentinker.com/<key>.txt) and adds scripts/indexnow-submit.sh which POSTs the sitemap
URLs to api.indexnow.org (DRY_RUN=1 to preview). Run after a content deploy / from CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(seo): landing-page optimization pass (schema fixes, FAQ, content depth, CWV)

From a 3-way SEO audit (technical / structured-data / content). Highest-value fixes:

Structured data (penalty risk + rich results):
- REMOVE the fabricated aggregateRating (4.8/50) from SoftwareApplication — no visible reviews
  on the page = a "spammy structured markup" risk. Replace loose Offers with a proper AggregateOffer
  + publisher + image/screenshot.
- Add a VISIBLE FAQ section (10 Q&As) so the FAQPage schema finally has on-page content (it shipped
  4 Q&As with no visible counterpart — a mismatch); expand the FAQPage JSON-LD to mirror all 10.
- Add a WebSite entity block; add YouTube to Organization sameAs + a description.
- Fix the guides/compare BreadcrumbList position-2 target (dead /#features -> /).

Content / keywords / IA:
- Hero + Features subtitle rewritten to surface "digital signage software" / "digital signage CMS"
  / "self-host" / "free" above the fold.
- New "How It Works" (3-step) and "Use Cases / Industries" (8 verticals) sections for snippet +
  long-tail intent. Platform tiles (Android TV / Fire TV / Raspberry Pi) now link to their guides.
- FAQ answers add keyword-rich internal links to the guides + compare pages.

Technical / Core Web Vitals:
- Lazy-load the YouTube iframe (loading=lazy + youtube-nocookie + explicit width/height) — the top
  LCP/TBT win on mobile.
- Title 84->~60 chars (keyword-front), meta description ~178->~156 + CTA.
- <div> footer -> <footer> landmark; favicon sizes (192+512).
- sitemap.xml: add <lastmod> to all 10 URLs.

All JSON-LD validated (4 blocks parse; single H1; no fabricated data).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(seo): open-source/what-is pillars, Xibo+Anthias compares, integrations hub [#177]

Competitor-SEO gap-fill from the Yodeck/ScreenCloud/OptiSigns/Xibo teardowns.
11 new static pages + sitemap/landing/README wiring. All match the existing
seo-page.css template; BreadcrumbList on every page, FAQPage (visible-backed)
on guides + integration spokes. No fabricated ratings.

Pillar guides:
- guides/open-source-digital-signage.html  (head term "open source digital signage")
- guides/what-is-digital-signage.html       (TOFU definitional pillar + FAQ)

Comparisons (the open-source SERP Xibo/Anthias own):
- compare/xibo-alternative.html    (wedge: every ScreenTinker player free vs Xibo's paid Android/Tizen/webOS licences; no free plan)
- compare/anthias-alternative.html (wedge: fleet + video walls + multi-platform vs one-Pi-one-screen)

Integrations (OptiSigns' top tactic — one page per app):
- integrations/index.html hub
- google-slides / canva / power-bi  (honestly framed as the universal Webpage widget, with the X-Frame-Options / publish-vs-edit-URL caveat + Power BI public-data warning)
- youtube / rss / weather           (native widgets)

Wiring:
- sitemap.xml +11 URLs (lastmod 2026-07-13)
- landing.html Resources grid: 6 new cards (open-source, what-is, vs Xibo, vs Anthias, integrations hub)
- README.md: keyword-rich open-source/self-hosted intro + platform list + guide links (GitHub-SERP asset)
- docs/seo-directory-listings.md: off-repo G2/Capterra/AlternativeTo/awesome-selfhosted/fingoweb submission checklist + reusable kit

Validated: all JSON-LD parses, canonicals match paths, 0 broken internal links, sitemap well-formed (21 URLs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:22:37 -05:00

32 lines
1.6 KiB
Bash
Executable file

#!/bin/bash
# Submit the sitemap URLs to IndexNow — instant "this changed, re-crawl it" pings to Bing,
# Yandex, Seznam, Naver (and anyone else on the shared IndexNow endpoint). Google doesn't use
# IndexNow but honors the same sitemap. Run after a content deploy, or from CI/cron.
#
# Prereq: the IndexNow key file (frontend/<KEY>.txt) must be DEPLOYED and reachable at
# https://<host>/<KEY>.txt — that's how IndexNow verifies ownership.
#
# scripts/indexnow-submit.sh [host] (default host: screentinker.com)
# DRY_RUN=1 scripts/indexnow-submit.sh (print the payload, don't POST)
set -euo pipefail
cd "$(dirname "$0")/.."
HOST="${1:-screentinker.com}"
KEY_FILE="$(find frontend -maxdepth 1 -type f -name '*.txt' | grep -E '/[0-9a-f]{16,}\.txt$' | head -1 || true)"
[ -n "$KEY_FILE" ] || { echo "ERROR: no IndexNow key file (frontend/<hex>.txt) found." >&2; exit 1; }
KEY="$(basename "$KEY_FILE" .txt)"
mapfile -t URLS < <(grep -oE '<loc>[^<]+</loc>' frontend/sitemap.xml | sed -E 's#</?loc>##g')
[ "${#URLS[@]}" -gt 0 ] || { echo "ERROR: no <loc> URLs in frontend/sitemap.xml." >&2; exit 1; }
PAYLOAD="$(node -e '
const [host, key] = [process.argv[1], process.argv[2]];
const urlList = process.argv.slice(3);
process.stdout.write(JSON.stringify({ host, key, keyLocation: `https://${host}/${key}.txt`, urlList }));
' "$HOST" "$KEY" "${URLS[@]}")"
echo "IndexNow: ${#URLS[@]} URLs, host=$HOST, key=$KEY"
if [ "${DRY_RUN:-0}" = "1" ]; then echo "$PAYLOAD"; echo "(DRY_RUN — not submitted)"; exit 0; fi
curl -sS -w '\nHTTP %{http_code}\n' -X POST 'https://api.indexnow.org/indexnow' \
-H 'Content-Type: application/json; charset=utf-8' --data "$PAYLOAD"