Files
iptvnator/apps/website
4grayandClaude Fable 5.1 e9326c9427 feat(website): add a Docker page for the self-hosted browser version
Publish /download/docker/ so "Self-host it with Docker" no longer sends
visitors off-site to the developer README. The page covers what the
image contains, a four-step quick start with the repository compose
command and an image-only compose snippet, the environment variables
and port that matter, the published tag patterns with the update
command, what the browser version leaves out, and a seven-question FAQ.
It links to docker/README.md for the full reference, to the desktop vs
browser comparison and to the setup guides; the README links back.

The download hub gains a fourth card, the homepage and hub links point
at the page, the platform switcher shows a Docker card on the OS pages,
and the comparison page links both the page and the README. The page
emits SoftwareApplication / FAQPage / BreadcrumbList JSON-LD and is
covered by website-download-pages.test.mjs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:53:45 +02:00
..

IPTVnator Website

The website is an Astro static site deployed to GitHub Pages at https://4gray.github.io/iptvnator/.

Blog Comments

Blog posts render Giscus comments from apps/website/src/components/GiscusComments.astro. Giscus stores comments in GitHub Discussions for 4gray/iptvnator and maps each page to a discussion by pathname, including the GitHub Pages base path such as /iptvnator/blog/why-external-players-help/.

The embed is wired to the dedicated Blog comments discussion category:

  • Repository id: MDEwOlJlcG9zaXRvcnkyMTMxOTQ3Mzg=
  • Category id: DIC_kwDODLUX8s4C9eBJ
  • Mapping: pathname
  • Theme: transparent_dark

If the category is recreated, query the new category id:

gh api graphql \
  -f owner=4gray \
  -f name=iptvnator \
  -f query='query($owner:String!, $name:String!) { repository(owner:$owner, name:$name) { discussionCategories(first:25) { nodes { id name slug isAnswerable } } } }'

Then update data-category-id in GiscusComments.astro.

Moderation happens in GitHub Discussions. Maintainers can hide, delete, lock, or move discussions and comments from the repository Discussions UI.

Download Pages

/download/ plus /download/windows/, /download/macos/, /download/linux/ and /download/docker/ (the self-hosted browser version: quick start, variables, tags, FAQ, with docker/README.md as the reference behind it) are landing pages (apps/website/src/pages/download/). They exist for search visibility on "IPTVnator download" style queries and to spare users the 27-asset GitHub release page; each carries OS-specific install steps, requirements, an FAQ and SoftwareApplication / FAQPage / BreadcrumbList structured data. Shared pieces live in src/components/download/ and reuse the blog components (StepRail, Alert, FaqAccordion, CopyCommand).

Latest-release resolution

Direct asset links need the release version, so src/lib/downloads.ts resolves it at build time:

  1. GET https://api.github.com/repos/4gray/iptvnator/releases/latest (8 s timeout). The asset list from the published release is authoritative: options whose file is missing are dropped, sizes and the publish date come from the API. deploy-website.yml passes GITHUB_TOKEN to the build so the call is authenticated.
  2. Fallback: the root package.json version with the asset naming pattern from electron-builder.json. This is deterministic but cannot prove the files exist yet (a version bump lands on master before the release is published), so a warning is printed. Set WEBSITE_SKIP_RELEASE_FETCH=1 to force it for offline or reproducible builds.

Both paths produce the same page structure. Adding an artifact means adding a DownloadOption (matcher + fallback name) in downloads.ts; the pages and the hub pick it up. The homepage SoftwareApplication schema reads the same resolved version.

pnpm nx test website builds the site and runs tools/testing/website-download-pages.test.mjs, which checks titles, canonicals, direct asset links, JSON-LD, cross-links and sitemap entries without depending on a specific version.

Guides

Evergreen how-to posts live in the blog collection next to release notes (xtream-codes-setup-guide.mdx, stalker-portal-setup-guide.mdx and m3u-playlist-epg-setup-guide.mdx in apps/website/src/content/blog/). Two conventions set them apart:

  • faq frontmatter. An optional list of { q, a } entries. BlogPost.astro renders it as an accordion after the body and emits a FAQPage JSON-LD block next to the BlogPosting one, so the answers can surface as rich results.
  • Screenshots from the capture script. Guide frames are captured by pnpm release:screenshots --group guides into apps/website/public/blog/guides/screenshots/<slug>-<theme>.png; the shots are declared in tools/release/screenshots.manifest.json with "group": "guides" and never appear in a release run.

tools/testing/website-guides.test.mjs (part of pnpm nx test website) checks each guide for the FAQPage schema, a link to the download hub and the presence of every referenced screenshot in the build output.

Feature Pages

/features/ plus one page per feature (m3u-player, xtream-codes-player, stalker-portal-player, epg, remote-control) live in apps/website/src/pages/features/. They target " player" style searches, reuse the download-page sections, and each carries SoftwareApplication (with featureList) / FAQPage / BreadcrumbList structured data. The registry in src/lib/features.ts drives the hub, the per-page switcher, the homepage feature cards and tools/testing/website-feature-pages.test.mjs; adding a page means adding one registry entry and one .astro file. Screenshots come only from the mock-backed guide and release captures, never from the older homepage screenshots that show real channel names.

Comparison Pages

/compare/ plus one page per decision the app asks users to make (m3u-vs-xtream-vs-stalker, playback-engines, desktop-vs-browser) live in apps/website/src/pages/compare/. They compare IPTVnator's own options against each other, never other products, so every claim is checkable against this repository; the registry is src/lib/comparisons.ts.

Each page opens with a one-paragraph verdict (CompareHero), carries at least one ComparisonTable (cells are true, false or a qualifying string) and emits WebPage / FAQPage / BreadcrumbList JSON-LD from src/lib/comparison-schema.ts — deliberately not SoftwareApplication, since these pages are guidance rather than a product listing, and tools/testing/website-compare-pages.test.mjs asserts that.

Naming a competitor on these pages is a product decision, not a technical one. Phase 3 of .plans/2026-09-03-marketing-landing-pages.md covers that and is still open.