mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 19:46:16 -08:00
Publish /features/ with one landing page per feature people search for: M3U playlist player, Xtream Codes player, Stalker portal player, TV guide (EPG) and phone remote control. Each page composes a feature hero (download and setup-guide calls to action) with the download-page sections, carries SoftwareApplication (featureList) / FAQPage / BreadcrumbList structured data, links to the other feature pages and the matching guides, and uses only mock-backed screenshots. A registry in src/lib/features.ts drives the hub, the per-page switcher, the homepage feature cards (now links) and the header Features entry, so a new page is one registry entry and one .astro file. tools/testing/website-feature-pages.test.mjs checks canonical URLs, schema, cross-links, hub coverage and sitemap entries. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import type { FaqEntry } from './download-schema';
|
|
import { REPO_URL } from './downloads';
|
|
import type { FeatureEntry } from './features';
|
|
import { FEATURES_HUB_HREF } from './features';
|
|
import { absoluteUrl } from './site';
|
|
|
|
interface FeaturePageSchemaInput {
|
|
feature: FeatureEntry;
|
|
pageUrl: string;
|
|
description: string;
|
|
/** Plain-language feature statements for schema.org `featureList`. */
|
|
featureList: string[];
|
|
faq: FaqEntry[];
|
|
}
|
|
|
|
/** SoftwareApplication (with featureList) + FAQPage + BreadcrumbList for one feature page. */
|
|
export function buildFeaturePageSchema(input: FeaturePageSchemaInput): Array<Record<string, unknown>> {
|
|
const { feature, pageUrl, description, featureList, faq } = input;
|
|
|
|
return [
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'SoftwareApplication',
|
|
name: 'IPTVnator',
|
|
applicationCategory: 'MultimediaApplication',
|
|
applicationSubCategory: 'IPTV player',
|
|
operatingSystem: 'Windows, macOS, Linux',
|
|
url: pageUrl,
|
|
downloadUrl: absoluteUrl('/download/'),
|
|
featureList,
|
|
isAccessibleForFree: true,
|
|
license: `${REPO_URL}/blob/master/LICENSE`,
|
|
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
author: { '@type': 'Person', name: '4gray', url: 'https://github.com/4gray' },
|
|
sameAs: [REPO_URL, 'https://t.me/iptvnator'],
|
|
description,
|
|
},
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'FAQPage',
|
|
mainEntity: faq.map((entry) => ({
|
|
'@type': 'Question',
|
|
name: entry.q,
|
|
acceptedAnswer: { '@type': 'Answer', text: entry.a },
|
|
})),
|
|
},
|
|
{
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BreadcrumbList',
|
|
itemListElement: [
|
|
{ '@type': 'ListItem', position: 1, name: 'IPTVnator', item: absoluteUrl('/') },
|
|
{ '@type': 'ListItem', position: 2, name: 'Features', item: absoluteUrl(FEATURES_HUB_HREF.replace('/iptvnator', '')) },
|
|
{ '@type': 'ListItem', position: 3, name: feature.label, item: pageUrl },
|
|
],
|
|
},
|
|
];
|
|
}
|