mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
chore(website): enhance SEO with structured data and meta tags in various components
This commit is contained in:
1 parent
bdf0e3265a
commit
7931127aa6
9 files changed
+172
-12
No files matched your search
@@ -9,7 +9,7 @@
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/4gray/iptvnator/build-and-test.yaml?style=for-the-badge&logo=github"> <a href="https://github.com/4gray/iptvnator/releases"><img src="https://img.shields.io/github/downloads/4gray/iptvnator/total?style=for-the-badge&logo=github" alt="Releases"></a> <a href="https://codecov.io/gh/4gray/iptvnator"><img alt="Codecov" src="https://img.shields.io/codecov/c/github/4gray/iptvnator?style=for-the-badge"></a> <a href="https://t.me/iptvnator"><img src="https://img.shields.io/badge/telegram-iptvnator-blue?logo=telegram&style=for-the-badge" alt="Telegram"></a> <a href="https://bsky.app/profile/iptvnator.bsky.social"><img src="https://img.shields.io/badge/bluesky-iptvnator-darkblue?logo=bluesky&style=for-the-badge" alt="Bluesky"></a>
|
||||
</p>
|
||||
|
||||
<a href="https://t.me/iptvnator">Telegram channel for discussions</a> | <a href="https://ko-fi.com/4gray" target="_blank">Buy me a coffee</a> | <a href="https://github.com/sponsors/4gray">GitHub Sponsors</a>
|
||||
🌐 **[Website](https://4gray.github.io/iptvnator/)** | <a href="https://t.me/iptvnator">Telegram channel for discussions</a> | <a href="https://ko-fi.com/4gray" target="_blank">Buy me a coffee</a> | <a href="https://github.com/sponsors/4gray">GitHub Sponsors</a>
|
||||
|
||||
**IPTVnator** is a video player application that provides support for IPTV playlist playback (m3u, m3u8). The application allows users to import playlists using remote URLs or by uploading files from the local file system. Additionally, it supports EPG information in XMLTV format which can be provided via URL.
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# IPTVnator
|
||||
|
||||
> IPTVnator is an open-source cross-platform IPTV player (desktop + web) that supports M3U/M3U8 playlists, Xtream Codes API, Stalker portals, and XMLTV EPG.
|
||||
|
||||
## Canonical URLs
|
||||
|
||||
- Website: https://4gray.github.io/iptvnator/
|
||||
- Blog: https://4gray.github.io/iptvnator/blog/
|
||||
- Latest Release: https://github.com/4gray/iptvnator/releases/latest
|
||||
- Release Notes (v0.18): https://4gray.github.io/iptvnator/blog/v0-18-release-notes/
|
||||
- Troubleshooting (macOS app damaged fix): https://4gray.github.io/iptvnator/blog/macos-arm-app-damaged-fix/
|
||||
- Source Code: https://github.com/4gray/iptvnator
|
||||
- Telegram: https://t.me/iptvnator
|
||||
- GitHub Sponsors: https://github.com/sponsors/4gray
|
||||
- Ko-fi: https://ko-fi.com/4gray
|
||||
|
||||
## Product Scope
|
||||
|
||||
- IPTVnator is a media player application.
|
||||
- It does not provide, host, or sell playlists, channels, or media content.
|
||||
- Any channel names or metadata shown in screenshots/posts are demo examples only.
|
||||
|
||||
## Machine-readable Discovery
|
||||
|
||||
- Sitemap: https://4gray.github.io/iptvnator/sitemap-index.xml
|
||||
- Robots: https://4gray.github.io/iptvnator/robots.txt
|
||||
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://4gray.github.io/iptvnator/sitemap-index.xml
|
||||
@@ -3,16 +3,46 @@ interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
type?: 'website' | 'article';
|
||||
noindex?: boolean;
|
||||
keywords?: string[];
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
jsonLd?: Record<string, unknown> | Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
const { title, description, image = '/iptvnator/og-image.png' } = Astro.props;
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
image = '/iptvnator/og-image.png',
|
||||
type = 'website',
|
||||
noindex = false,
|
||||
keywords = [],
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
jsonLd,
|
||||
} = Astro.props;
|
||||
|
||||
const absoluteImageURL = new URL(image, Astro.site);
|
||||
const robotsContent = noindex
|
||||
? 'noindex, nofollow, noarchive'
|
||||
: 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1';
|
||||
const schemaPayload = jsonLd
|
||||
? JSON.stringify(Array.isArray(jsonLd) ? jsonLd : [jsonLd])
|
||||
: null;
|
||||
---
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="/iptvnator/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="robots" content={robotsContent} />
|
||||
<meta name="referrer" content="strict-origin-when-cross-origin" />
|
||||
<meta name="author" content="4gray" />
|
||||
<meta name="theme-color" content="#0a0a08" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
{keywords.length > 0 && <meta name="keywords" content={keywords.join(', ')} />}
|
||||
|
||||
<!-- Fonts: Playfair Display (display), DM Sans (body), IBM Plex Mono (code) -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
@@ -23,7 +53,7 @@ const { title, description, image = '/iptvnator/og-image.png' } = Astro.props;
|
||||
/>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
<link rel="canonical" href={canonicalURL.toString()} />
|
||||
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>{title}</title>
|
||||
@@ -31,15 +61,24 @@ const { title, description, image = '/iptvnator/og-image.png' } = Astro.props;
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
<meta property="og:type" content={type} />
|
||||
<meta property="og:site_name" content="IPTVnator" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:url" content={canonicalURL.toString()} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={new URL(image, Astro.url)} />
|
||||
<meta property="og:image" content={absoluteImageURL.toString()} />
|
||||
<meta property="og:image:secure_url" content={absoluteImageURL.toString()} />
|
||||
{type === 'article' && publishedTime && <meta property="article:published_time" content={publishedTime} />}
|
||||
{type === 'article' && modifiedTime && <meta property="article:modified_time" content={modifiedTime} />}
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={Astro.url} />
|
||||
<meta property="twitter:url" content={canonicalURL.toString()} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={new URL(image, Astro.url)} />
|
||||
<meta property="twitter:image" content={absoluteImageURL.toString()} />
|
||||
|
||||
{schemaPayload && (
|
||||
<script type="application/ld+json" set:html={schemaPayload} />
|
||||
)}
|
||||
@@ -8,15 +8,31 @@ interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
type?: 'website' | 'article';
|
||||
noindex?: boolean;
|
||||
keywords?: string[];
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
jsonLd?: Record<string, unknown> | Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
const { title, description, image } = Astro.props;
|
||||
const { title, description, image, type, noindex, keywords, publishedTime, modifiedTime, jsonLd } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} image={image} />
|
||||
<BaseHead
|
||||
title={title}
|
||||
description={description}
|
||||
image={image}
|
||||
type={type}
|
||||
noindex={noindex}
|
||||
keywords={keywords}
|
||||
publishedTime={publishedTime}
|
||||
modifiedTime={modifiedTime}
|
||||
jsonLd={jsonLd}
|
||||
/>
|
||||
</head>
|
||||
<body class="min-h-screen bg-surface-950 font-body text-surface-200">
|
||||
<Header />
|
||||
|
||||
@@ -26,9 +26,43 @@ const formattedUpdatedDate = updatedDate?.toLocaleDateString('en-US', {
|
||||
});
|
||||
|
||||
const AUTHOR_AVATAR_URL = 'https://avatars.githubusercontent.com/u/1503032';
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site).toString();
|
||||
const blogPostSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: title,
|
||||
description,
|
||||
datePublished: pubDate.toISOString(),
|
||||
dateModified: (updatedDate ?? pubDate).toISOString(),
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: author,
|
||||
url: 'https://github.com/4gray',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'IPTVnator',
|
||||
url: 'https://4gray.github.io/iptvnator/',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://4gray.github.io/iptvnator/favicon.ico',
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: canonicalURL,
|
||||
image: heroImage ? new URL(heroImage, Astro.site).toString() : undefined,
|
||||
};
|
||||
---
|
||||
|
||||
<BaseLayout title={`${title} | IPTVnator Blog`} description={description}>
|
||||
<BaseLayout
|
||||
title={`${title} | IPTVnator Blog`}
|
||||
description={description}
|
||||
image={heroImage}
|
||||
type="article"
|
||||
publishedTime={pubDate.toISOString()}
|
||||
modifiedTime={(updatedDate ?? pubDate).toISOString()}
|
||||
keywords={tags}
|
||||
jsonLd={blogPostSchema}
|
||||
>
|
||||
<article class="pt-36 pb-24">
|
||||
<div class="relative mx-auto max-w-3xl px-6">
|
||||
<div class="pointer-events-none absolute -top-16 -right-14 hidden w-44 rotate-[-8deg] opacity-30 mix-blend-screen md:block lg:w-52">
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<BaseLayout title="Page Not Found | IPTVnator" description="The page you're looking for doesn't exist.">
|
||||
<BaseLayout
|
||||
title="Page Not Found | IPTVnator"
|
||||
description="The page you're looking for doesn't exist."
|
||||
noindex={true}
|
||||
>
|
||||
<section class="flex min-h-[80vh] items-center justify-center pt-20">
|
||||
<div class="mx-auto max-w-md px-6 text-center">
|
||||
<img
|
||||
|
||||
@@ -17,6 +17,7 @@ const remainingPosts = featuredPost ? posts.filter((post) => post.id !== feature
|
||||
<BaseLayout
|
||||
title="Blog | IPTVnator"
|
||||
description="Release notes, tutorials, and project updates from the IPTVnator team."
|
||||
keywords={['IPTVnator blog', 'IPTV release notes', 'EPG, Archive setup', 'Xtream Codes Player (XC)', 'Stalker Player Support (STB)']}
|
||||
>
|
||||
<section class="pt-36 pb-24">
|
||||
<div class="mx-auto max-w-6xl px-6">
|
||||
|
||||
@@ -18,11 +18,47 @@ const allPosts = (await getCollection('blog'))
|
||||
const featuredPost = allPosts.find((post) => post.data.featured) ?? allPosts[0];
|
||||
const otherPosts = featuredPost ? allPosts.filter((post) => post.id !== featuredPost.id).slice(0, 2) : [];
|
||||
const posts = featuredPost ? [featuredPost, ...otherPosts] : allPosts.slice(0, 3);
|
||||
|
||||
const homeSchema = [
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: 'IPTVnator',
|
||||
url: 'https://4gray.github.io/iptvnator/',
|
||||
description:
|
||||
'A cross-platform IPTV player supporting M3U/M3U8 playlists, Xtream Codes API, and Stalker portals.',
|
||||
inLanguage: 'en',
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'SoftwareApplication',
|
||||
name: 'IPTVnator',
|
||||
applicationCategory: 'MultimediaApplication',
|
||||
operatingSystem: 'Windows, macOS, Linux, Web',
|
||||
softwareVersion: '0.19.0',
|
||||
url: 'https://4gray.github.io/iptvnator/',
|
||||
downloadUrl: 'https://github.com/4gray/iptvnator/releases/latest',
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: '4gray',
|
||||
url: 'https://github.com/4gray',
|
||||
},
|
||||
sameAs: [
|
||||
'https://github.com/4gray/iptvnator',
|
||||
'https://t.me/iptvnator',
|
||||
'https://ko-fi.com/4gray',
|
||||
],
|
||||
description:
|
||||
'Open-source IPTV player with M3U/M3U8, Xtream Codes API, and Stalker portal support.',
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="IPTVnator - Open Source IPTV Player"
|
||||
description="A cross-platform IPTV player supporting M3U/M3U8 playlists, Xtream Codes API, and Stalker portals. Available as a desktop app and PWA."
|
||||
keywords={['IPTV player', 'M3U', 'M3U8', 'Xtream Codes', 'Stalker portal', 'EPG', 'XMLTV', 'open-source']}
|
||||
jsonLd={homeSchema}
|
||||
>
|
||||
<Hero />
|
||||
<FeatureGrid />
|
||||
|
||||
Reference in new issue
Block a user