From cc306921d725625c3cde510fec3abbb00f1c7ab3 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 7 Oct 2026 13:42:48 +0200 Subject: [PATCH] Present FOLX as a music news publisher and unify site SEO --- client/index.html | 12 ++--- client/src/components/footer.tsx | 4 +- client/src/components/header.tsx | 2 +- client/src/hooks/use-page-meta.ts | 31 ++++++++--- client/src/pages/about.tsx | 6 +-- client/src/pages/article.tsx | 24 ++++----- client/src/pages/datenschutz.tsx | 3 +- client/src/pages/empfang.tsx | 6 +-- client/src/pages/home.tsx | 35 ++----------- client/src/pages/impressum.tsx | 3 +- client/src/pages/kontakt.tsx | 3 +- script/seo-check.ts | 20 +++++++ server/static.ts | 86 +++++++++++++++---------------- shared/seo.ts | 53 +++++++++++++++++++ 14 files changed, 173 insertions(+), 115 deletions(-) create mode 100644 script/seo-check.ts create mode 100644 shared/seo.ts diff --git a/client/index.html b/client/index.html index f022450..098b326 100644 --- a/client/index.html +++ b/client/index.html @@ -7,11 +7,11 @@ - Volksmusik & Schlager TV | FOLX Music Television – Nachrichten, Videos & mehr - + Schlager & Volksmusik News | FOLX TV + - - + + @@ -23,8 +23,8 @@ - - + + diff --git a/client/src/components/footer.tsx b/client/src/components/footer.tsx index 68a017b..1b1b322 100644 --- a/client/src/components/footer.tsx +++ b/client/src/components/footer.tsx @@ -23,8 +23,8 @@ export default function Footer({ narrow }: { narrow?: boolean }) {

Folx Music Television

- FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, - Musikvideos, Künstlerporträts, Live-Shows und Interviews aus der Welt der volkstümlichen Musik. + FOLX TV – Aktuelle Schlager- und Volksmusik-News, Künstlerporträts und Interviews. + Entdecken Sie Nachrichten, Artikel und Videos aus der Musikszene sowie Informationen zu unserem TV-Angebot.

{SOCIAL_LINKS.map((s) => ( diff --git a/client/src/components/header.tsx b/client/src/components/header.tsx index 0a7ee08..79e8560 100644 --- a/client/src/components/header.tsx +++ b/client/src/components/header.tsx @@ -43,7 +43,7 @@ export default function Header() {
- FOLX TV – Volksmusik & Schlager Fernsehsender + FOLX TV – Schlager & Volksmusik News
diff --git a/client/src/hooks/use-page-meta.ts b/client/src/hooks/use-page-meta.ts index 562b653..78f1ad5 100644 --- a/client/src/hooks/use-page-meta.ts +++ b/client/src/hooks/use-page-meta.ts @@ -1,3 +1,4 @@ +import { HOME_TITLE, HOME_DESCRIPTION, INFO_SEO, publisherSchema } from "@shared/seo"; import { useEffect } from "react"; interface OgMeta { @@ -8,6 +9,7 @@ interface OgMeta { ogType?: string; ogUrl?: string; articlePublishedTime?: string; + articleModifiedTime?: string; articleAuthor?: string; articleSection?: string; } @@ -32,10 +34,10 @@ function removeMetaTag(selector: string) { if (el) el.remove(); } -const DEFAULT_TITLE = "Volksmusik & Schlager TV | FOLX Music Television – Nachrichten, Videos & mehr"; -const DEFAULT_DESC = "FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos, Künstlerporträts, Live-Shows und Interviews aus der Welt der volkstümlichen Musik. Jetzt einschalten!"; -const DEFAULT_OG_TITLE = "Volksmusik & Schlager TV | FOLX Music Television"; -const DEFAULT_OG_DESC = "FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos, Künstlerporträts und Live-Shows."; +const DEFAULT_TITLE = `${HOME_TITLE} | FOLX TV`; +const DEFAULT_DESC = HOME_DESCRIPTION; +const DEFAULT_OG_TITLE = `${HOME_TITLE} | FOLX TV`; +const DEFAULT_OG_DESC = HOME_DESCRIPTION; const DEFAULT_OG_IMAGE = "https://folx.tv/og-image.jpg"; const CANONICAL_BASE = "https://folx.tv"; @@ -62,8 +64,24 @@ export function usePageMeta(title: string, description?: string, og?: OgMeta) { const canonicalPath = og?.ogUrl || `${CANONICAL_BASE}${window.location.pathname}`; const canonicalUrl = canonicalPath.startsWith("http") ? canonicalPath : `${CANONICAL_BASE}${canonicalPath}`; setCanonical(canonicalUrl); + const info = INFO_SEO[window.location.pathname.replace(/\/+$/, "")]; + document.getElementById("info-jsonld")?.remove(); + if (info) { + const script = document.createElement("script"); + script.id = "info-jsonld"; + script.type = "application/ld+json"; + script.textContent = JSON.stringify({ "@context": "https://schema.org", "@type": info.type, + "@id": `${canonicalUrl}#webpage`, url: canonicalUrl, name: title + suffix, + description: info.description, inLanguage: "de-DE", publisher: publisherSchema() }); + document.head.appendChild(script); + } setMetaTag('meta[property="og:url"]', canonicalUrl); + setMetaTag('meta[property="og:title"]', og?.ogTitle || title + suffix); + setMetaTag('meta[property="og:description"]', og?.ogDescription || description || DEFAULT_DESC); + setMetaTag('meta[property="og:type"]', og?.ogType || "website"); + setMetaTag('meta[name="twitter:title"]', og?.ogTitle || title + suffix); + setMetaTag('meta[name="twitter:description"]', og?.ogDescription || description || DEFAULT_DESC); if (og) { if (og.ogTitle) setMetaTag('meta[property="og:title"]', og.ogTitle); if (og.ogDescription) setMetaTag('meta[property="og:description"]', og.ogDescription); @@ -78,7 +96,7 @@ export function usePageMeta(title: string, description?: string, og?: OgMeta) { if (og.articlePublishedTime) { setMetaTag('meta[property="article:published_time"]', og.articlePublishedTime); - setMetaTag('meta[property="article:modified_time"]', og.articlePublishedTime); + setMetaTag('meta[property="article:modified_time"]', og.articleModifiedTime || og.articlePublishedTime); } if (og.articleAuthor) { setMetaTag('meta[property="article:author"]', og.articleAuthor); @@ -88,6 +106,7 @@ export function usePageMeta(title: string, description?: string, og?: OgMeta) { } } return () => { + document.getElementById("info-jsonld")?.remove(); document.title = DEFAULT_TITLE; setMetaTag('meta[name="description"]', DEFAULT_DESC); setMetaTag('meta[property="og:title"]', DEFAULT_OG_TITLE); @@ -105,5 +124,5 @@ export function usePageMeta(title: string, description?: string, og?: OgMeta) { removeMetaTag('meta[property="article:author"]'); removeMetaTag('meta[property="article:section"]'); }; - }, [title, description, og?.siteName, og?.ogTitle, og?.ogDescription, og?.ogImage, og?.ogType, og?.ogUrl, og?.articlePublishedTime, og?.articleAuthor, og?.articleSection]); + }, [title, description, og?.siteName, og?.ogTitle, og?.ogDescription, og?.ogImage, og?.ogType, og?.ogUrl, og?.articlePublishedTime, og?.articleModifiedTime, og?.articleAuthor, og?.articleSection]); } diff --git a/client/src/pages/about.tsx b/client/src/pages/about.tsx index 22f09ec..4aecfef 100644 --- a/client/src/pages/about.tsx +++ b/client/src/pages/about.tsx @@ -1,3 +1,4 @@ +import { INFO_SEO } from "@shared/seo"; import { Tv, Youtube, Facebook, Instagram, PlayCircle, Radio } from "lucide-react"; import Header from "@/components/header"; import Footer from "@/components/footer"; @@ -43,10 +44,7 @@ const KANAELE = [ ]; export default function AboutPage() { - usePageMeta( - "Über FOLX TV - Volksmusik & Schlager Fernsehsender", - "Alles über FOLX TV – Ihren Volksmusik & Schlager Fernsehsender seit 2013. Die Künstler, der FOLX Stadl und alle Kanäle: Livestream, FOLX Plus, YouTube, Facebook und Instagram.", - ); + usePageMeta(INFO_SEO["/ueber-uns"].title, INFO_SEO["/ueber-uns"].description, { siteName: "FOLX TV" }); return (
diff --git a/client/src/pages/article.tsx b/client/src/pages/article.tsx index 58b41ee..a181ad1 100644 --- a/client/src/pages/article.tsx +++ b/client/src/pages/article.tsx @@ -1,3 +1,4 @@ +import { publisherSchema, articleModifiedTime, articleGenre } from "@shared/seo"; import { useQuery } from "@tanstack/react-query"; import { useParams, Link, useLocation } from "wouter"; import { type Article } from "@shared/schema"; @@ -160,17 +161,19 @@ export default function ArticlePage() { : ""; usePageMeta( - article ? `${article.title} - Volksmusik & Schlager` : "Volksmusik & Schlager Artikel", + article ? article.title : "Nachrichten", article?.excerpt || "Aktuelle Nachrichten aus der Volksmusik- und Schlagerszene bei FOLX TV.", article ? { + siteName: "FOLX TV", ogTitle: article.title, ogDescription: article.excerpt, ogImage: articleImage || undefined, ogType: "article", ogUrl: articleUrl, articlePublishedTime: new Date(article.publishedAt).toISOString(), + articleModifiedTime: articleModifiedTime(article.content || "", new Date(article.publishedAt).toISOString()), articleAuthor: article.author || "Folx Music Television", - articleSection: article.category || "News", + articleSection: articleGenre(article.content || "").section, } : undefined ); @@ -179,32 +182,25 @@ export default function ArticlePage() { const jsonLd = { "@context": "https://schema.org", "@type": "NewsArticle", + "@id": `${articleUrl}#article`, "headline": article.title, "description": article.excerpt, "image": articleImage ? [articleImage] : [], "datePublished": new Date(article.publishedAt).toISOString(), - "dateModified": new Date(article.publishedAt).toISOString(), + "dateModified": articleModifiedTime(article.content || "", new Date(article.publishedAt).toISOString()), "author": { "@type": "Organization", "name": article.author || "Folx Music Television", "url": "https://folx.tv" }, - "publisher": { - "@type": "Organization", - "name": "Folx Music Television", - "url": "https://folx.tv", - "logo": { - "@type": "ImageObject", - "url": `${window.location.origin}/favicon.png` - } - }, + "publisher": publisherSchema(), "mainEntityOfPage": { "@type": "WebPage", "@id": articleUrl }, - "articleSection": article.category || "News", + "articleSection": articleGenre(article.content || "").section, "inLanguage": "de", - "keywords": `Volksmusik, Schlager, ${article.category || "News"}, ${article.title}` + "keywords": articleGenre(article.content || "").words.join(", ") }; const breadcrumbLd = { "@context": "https://schema.org", diff --git a/client/src/pages/datenschutz.tsx b/client/src/pages/datenschutz.tsx index 1b8fed2..38aa32b 100644 --- a/client/src/pages/datenschutz.tsx +++ b/client/src/pages/datenschutz.tsx @@ -1,3 +1,4 @@ +import { INFO_SEO } from "@shared/seo"; import { Shield } from "lucide-react"; import Header from "@/components/header"; import Footer from "@/components/footer"; @@ -5,7 +6,7 @@ import { usePageMeta } from "@/hooks/use-page-meta"; import { PageSideAds } from "@/components/adsense"; export default function DatenschutzPage() { - usePageMeta("Datenschutz - FOLX TV Volksmusik Sender", "Datenschutzerklärung von FOLX TV – Ihrem Volksmusik & Schlager Fernsehsender."); + usePageMeta(INFO_SEO["/datenschutz"].title, INFO_SEO["/datenschutz"].description, { siteName: "FOLX TV" }); return (
diff --git a/client/src/pages/empfang.tsx b/client/src/pages/empfang.tsx index 62265ec..cce37bb 100644 --- a/client/src/pages/empfang.tsx +++ b/client/src/pages/empfang.tsx @@ -1,3 +1,4 @@ +import { INFO_SEO } from "@shared/seo"; import { Tv, MapPin, PlayCircle, Youtube, Smartphone, Radio, ExternalLink } from "lucide-react"; import Header from "@/components/header"; import Footer from "@/components/footer"; @@ -107,10 +108,7 @@ const APPS = [ ]; export default function EmpfangPage() { - usePageMeta( - "Empfang FOLX TV - Volksmusik & Schlager Sender empfangen", - "So empfangen Sie FOLX TV – den Volksmusik & Schlager Sender: kostenloser Livestream auf folx.live, die FOLX TV App für Apple, Android, Samsung und Fire TV sowie Kabel und IPTV in Deutschland, Österreich und der Schweiz.", - ); + usePageMeta(INFO_SEO["/empfang-folx-tv"].title, INFO_SEO["/empfang-folx-tv"].description, { siteName: "FOLX TV" }); return (
diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index 8e2b3cc..704e52c 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -1,3 +1,4 @@ +import { HOME_TITLE, HOME_DESCRIPTION, homeSchema } from "@shared/seo"; import { useQuery } from "@tanstack/react-query"; import { artistGroup } from "@/lib/artists"; import { objectPositionFor } from "@/lib/focal"; @@ -756,38 +757,10 @@ function BentoSkeleton() { } export default function Home() { - usePageMeta("Volksmusik & Schlager TV", "FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos, Künstlerporträts, Live-Shows und Interviews aus der Welt der volkstümlichen Musik."); + usePageMeta(HOME_TITLE, HOME_DESCRIPTION, { siteName: "FOLX TV" }); useEffect(() => { - const jsonLd = { - "@context": "https://schema.org", - "@type": "WebSite", - "name": "Folx Music Television", - "alternateName": ["FOLX TV", "Volksmusik TV"], - "url": "https://folx.tv", - "description": "FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos, Künstlerporträts, Live-Shows und Interviews aus der Welt der volkstümlichen Musik.", - "publisher": { - "@type": "Organization", - "name": "Folx Music Television", - "url": "https://folx.tv", - "logo": { - "@type": "ImageObject", - "url": `${window.location.origin}/favicon.png` - }, - "sameAs": [ - "https://www.facebook.com/folxtvmusic", - "https://www.instagram.com/folxtv", - "https://www.youtube.com/@FolxTV", - "https://www.tiktok.com/@folx.tv" - ] - }, - "potentialAction": { - "@type": "SearchAction", - "target": "https://folx.tv/search?q={search_term_string}", - "query-input": "required name=search_term_string" - }, - "inLanguage": "de" - }; + const jsonLd = homeSchema(); const script = document.createElement("script"); script.type = "application/ld+json"; script.id = "home-jsonld"; @@ -1033,7 +1006,7 @@ export default function Home() {
-

FOLX TV – Volksmusik & Schlager Fernsehsender: Nachrichten, Videos und Live-Shows

+

FOLX TV – Schlager & Volksmusik News

{/* Prej lastna kopija pasu s trdo visino 90 px in responsive=false: Google je vanjo postavil 320x50 in okrog je ostal siv rob. diff --git a/client/src/pages/impressum.tsx b/client/src/pages/impressum.tsx index 27c7184..581d838 100644 --- a/client/src/pages/impressum.tsx +++ b/client/src/pages/impressum.tsx @@ -1,3 +1,4 @@ +import { INFO_SEO } from "@shared/seo"; import { Scale } from "lucide-react"; import Header from "@/components/header"; import Footer from "@/components/footer"; @@ -5,7 +6,7 @@ import { usePageMeta } from "@/hooks/use-page-meta"; import { PageSideAds } from "@/components/adsense"; export default function ImpressumPage() { - usePageMeta("Impressum - FOLX TV Volksmusik Sender", "Impressum und rechtliche Informationen zu FOLX TV – Ihrem Volksmusik & Schlager Fernsehsender."); + usePageMeta(INFO_SEO["/impressum"].title, INFO_SEO["/impressum"].description, { siteName: "FOLX TV" }); return (
diff --git a/client/src/pages/kontakt.tsx b/client/src/pages/kontakt.tsx index 0a85b21..d307903 100644 --- a/client/src/pages/kontakt.tsx +++ b/client/src/pages/kontakt.tsx @@ -1,3 +1,4 @@ +import { INFO_SEO } from "@shared/seo"; import { useState } from "react"; import { Mail, Phone, Send, CheckCircle, AlertCircle, Smartphone, Printer } from "lucide-react"; import { SiFacebook, SiInstagram, SiYoutube, SiTiktok } from "react-icons/si"; @@ -8,7 +9,7 @@ import { PageSideAds, TopBannerAd } from "@/components/adsense"; import { apiRequest } from "@/lib/queryClient"; export default function KontaktPage() { - usePageMeta("Kontakt - FOLX TV Volksmusik Sender", "Kontaktieren Sie FOLX TV – Ihren Fernsehsender Nr. 1 für Volksmusik und Schlager. Wir freuen uns auf Ihre Nachricht."); + usePageMeta(INFO_SEO["/kontakt"].title, INFO_SEO["/kontakt"].description, { siteName: "FOLX TV" }); const [formData, setFormData] = useState({ name: "", email: "", subject: "", message: "" }); const [status, setStatus] = useState<"idle" | "sending" | "success" | "error">("idle"); diff --git a/script/seo-check.ts b/script/seo-check.ts new file mode 100644 index 0000000..0c937c7 --- /dev/null +++ b/script/seo-check.ts @@ -0,0 +1,20 @@ +import assert from "node:assert/strict"; +import { homeSchema, publisherSchema, articleModifiedTime, articleGenre, INFO_SEO } from "../shared/seo"; +const home = homeSchema(); +assert.equal(home["@graph"].filter(x => x["@type"] === "WebSite").length, 1); +assert.equal(publisherSchema()["@type"], "NewsMediaOrganization"); +assert.ok(JSON.stringify(home).includes("/search?q=")); +assert.ok(!JSON.stringify(home).includes("/suche")); +assert.ok(publisherSchema().sameAs.includes("https://www.youtube.com/@folxmtv")); +const pub = "2026-01-01T12:00:00.000Z"; +assert.equal(articleModifiedTime("", pub), pub); +assert.equal(articleModifiedTime("", pub), pub); +assert.equal(articleModifiedTime("", pub), pub); +assert.equal(articleModifiedTime("", pub), pub); +assert.equal(articleModifiedTime("", pub), "2026-02-01T00:00:00.000Z"); +assert.equal(articleGenre("").section, "Star News"); +assert.ok(!articleGenre("").words.includes("Schlager")); +assert.equal(articleGenre("").section, "Musik News"); +assert.equal(INFO_SEO["/ueber-uns"].type, "AboutPage"); +assert.equal(INFO_SEO["/kontakt"].type, "ContactPage"); +console.log("SEO metadata checks passed"); diff --git a/server/static.ts b/server/static.ts index 69917de..60f24a1 100644 --- a/server/static.ts +++ b/server/static.ts @@ -1,3 +1,4 @@ +import { publisherSchema, homeSchema, INFO_SEO, articleModifiedTime, articleGenre } from "../shared/seo"; import express, { type Express } from "express"; import fs from "fs"; import path from "path"; @@ -11,13 +12,7 @@ import sharp from "sharp"; // Brez markerja ostane vse kot prej (glasbeno), da starejsi clanki ne nazadujejo. // Besedilne zaznave namenoma NI: preizkus na 30 clankih jo je zgresil pri // Fernsehgartnu in Carpendaleju. -function folxGenre(article: any): { words: string[]; section: string } { - const m = //i.exec(String(article?.content || "")); - const badge = (m ? m[1] : "MUSIC NEWS").toUpperCase(); - return badge === "MUSIC NEWS" - ? { words: ["Volksmusik", "Schlager", "Musik News"], section: "Musik News" } - : { words: ["Star News", "Promi News", "Promi"], section: "Star News" }; -} +const folxGenre = (article: any) => articleGenre(String(article?.content || "")); function escapeHtml(str: string): string { @@ -88,15 +83,7 @@ const ARTICLE_SLUG_REDIRECTS: Record = { // v bazi in v /api/articles ostanejo, ker jih bere sidebar na TV kanalih. // Izdajatelj: doslej samo ime in logo. Brez registrirane druzbe, naslova in // profilov Google znamke nima s cim povezati. -const PUBLISHER = (base: string) => ({ - // 4. 9. 2026: izdajatelj spet samo "Folx Music Television" z logom, kot do - // 26. 8. - brez registrirane druzbe, naslova in kontaktov. - "@type": "Organization", - "@id": `${base}/#organization`, - "name": "Folx Music Television", - "url": base, - "logo": { "@type": "ImageObject", "url": `${base}/og-image.jpg` }, -}); +const PUBLISHER = publisherSchema; const ONE_RUBRIKEN = new Set(["Politik", "Promis", "Sport", "Musik & Film", "Wetter", "Finanzen", "Aktuelles"]); @@ -294,28 +281,10 @@ export function serveStatic(app: Express) { if (url === "/" || url.startsWith("/?")) { try { let template = await fs.promises.readFile(indexPath, "utf-8"); - const homeLd = { - "@context": "https://schema.org", - "@graph": [ - PUBLISHER(canonicalBase), - { - "@type": "WebSite", - "@id": `${canonicalBase}/#website`, - "url": `${canonicalBase}/`, - "name": "FOLX Music Television", - "inLanguage": "de-DE", - "publisher": { "@id": `${canonicalBase}/#organization` }, - "potentialAction": { - "@type": "SearchAction", - "target": { "@type": "EntryPoint", "urlTemplate": `${canonicalBase}/suche?q={search_term_string}` }, - "query-input": "required name=search_term_string", - }, - }, - ], - }; + const homeLd = homeSchema(canonicalBase); template = template.replace( "", - `\n`); + `\n`); // 31. 8. 2026: naslovnica je React aplikacija, zato je robot dobil samo // lupino brez ene same povezave na clanek. Clanki se do Googla pridejo @@ -325,7 +294,8 @@ export function serveStatic(app: Express) { try { const vsi = await storage.getArticles(); const zadnji = [...vsi] - .sort((a: any, b: any) => String(b.publishedAt).localeCompare(String(a.publishedAt))) + .filter((a: any) => !a.targetUrl && !ONE_RUBRIKEN.has(a.category || "")) + .sort((a: any, b: any) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime()) .slice(0, 40); if (zadnji.length) { const seznam = zadnji @@ -503,11 +473,7 @@ export function serveStatic(app: Express) { : new Date().toISOString(); // dateModified: ce je clanek dopolnjen (enrich), preberi marker - const modDate = (() => { - const mm = //.exec(article.content || ""); - if (mm) { try { return new Date(mm[1]).toISOString(); } catch (e) { /* ignore */ } } - return pubDate; - })(); + const modDate = articleModifiedTime(article.content || "", pubDate); // VideoObject: ce je v clanku vgrajen YouTube video const videoId = (() => { @@ -518,6 +484,8 @@ export function serveStatic(app: Express) { const jsonLd = { "@context": "https://schema.org", "@type": "NewsArticle", + "@id": `${articleUrl}#article`, + "inLanguage": "de-DE", "headline": article.title, "description": article.excerpt, "image": [finalImage], @@ -536,7 +504,7 @@ export function serveStatic(app: Express) { "@id": articleUrl, }, }; - const jsonLdTag = ``; + const jsonLdTag = ``; const videoLd = videoId ? { "@context": "https://schema.org", @@ -557,6 +525,8 @@ export function serveStatic(app: Express) { ``, ``, ``, + ``, + ``, ``, ``, ``, @@ -575,7 +545,7 @@ export function serveStatic(app: Express) { ``, ``, ``, - `${escapeHtml(article.title)} - Volksmusik & Schlager | Folx Music Television`, + `${escapeHtml(article.title)} | FOLX TV`, jsonLdTag, videoLdTag, ].join("\n "); @@ -765,6 +735,34 @@ export function serveStatic(app: Express) { return; } + const infoPath = pathOnly.replace(/\/+$/, ""); + const info = INFO_SEO[infoPath]; + if (info) { + let template = stripExistingMeta(await fs.promises.readFile(indexPath, "utf-8")); + const pageUrl = `${canonicalBase}${infoPath}`; + const title = `${info.title} | FOLX TV`; + const ld = { "@context": "https://schema.org", "@type": info.type, + "@id": `${pageUrl}#webpage`, url: pageUrl, name: title, description: info.description, + inLanguage: "de-DE", isPartOf: { "@id": `${canonicalBase}/#website` }, publisher: PUBLISHER(canonicalBase) }; + template = template.replace(/[^<]*<\/title>/, [ + `<title>${escapeHtml(title)}`, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ].join("\n")); + res.status(200).type("html").send(template); + return; + } + // Neznana pot mora vrniti PRAVI 404, sicer Google dobi soft 404 (HTTP 200 // z naslovnico). Seznam mora ustrezati potem v client/src/App.tsx. const KNOWN = [ diff --git a/shared/seo.ts b/shared/seo.ts new file mode 100644 index 0000000..6346dfa --- /dev/null +++ b/shared/seo.ts @@ -0,0 +1,53 @@ +export const SITE_URL = "https://folx.tv"; +export const HOME_TITLE = "Schlager & Volksmusik News"; +export const HOME_DESCRIPTION = "Aktuelle Schlager- und Volksmusik-News, Künstler-Interviews und Nachrichten aus der Musikszene. Jetzt Artikel und Videos auf FOLX TV entdecken."; + +export function publisherSchema(base = SITE_URL) { + return { + "@type": "NewsMediaOrganization", + "@id": `${base}/#organization`, + name: "Folx Music Television", + alternateName: "FOLX TV", + url: base, + description: HOME_DESCRIPTION, + logo: { "@type": "ImageObject", url: `${base}/og-image.jpg` }, + sameAs: ["https://www.facebook.com/folxtv", "https://www.instagram.com/folxtv", "https://www.youtube.com/@folxmtv"], + }; +} + +export function homeSchema(base = SITE_URL) { + return { "@context": "https://schema.org", "@graph": [ + publisherSchema(base), + { "@type": "WebSite", "@id": `${base}/#website`, url: `${base}/`, + name: "FOLX TV", alternateName: "Folx Music Television", description: HOME_DESCRIPTION, + inLanguage: "de-DE", publisher: { "@id": `${base}/#organization` }, + potentialAction: { "@type": "SearchAction", target: { "@type": "EntryPoint", urlTemplate: `${base}/search?q={search_term_string}` }, "query-input": "required name=search_term_string" } }, + { "@type": "CollectionPage", "@id": `${base}/#webpage`, url: `${base}/`, + name: `${HOME_TITLE} | FOLX TV`, description: HOME_DESCRIPTION, inLanguage: "de-DE", + isPartOf: { "@id": `${base}/#website` }, publisher: { "@id": `${base}/#organization` }, + hasPart: { "@type": "CollectionPage", "@id": `${base}/category/News#webpage`, url: `${base}/category/News`, name: "News" } } + ] }; +} + +export function articleModifiedTime(content: string, published: string): string { + const marker = //.exec(content || ""); + const time = marker ? new Date(marker[1]).getTime() : NaN; + return Number.isFinite(time) && time >= new Date(published).getTime() && time <= Date.now() + ? new Date(time).toISOString() : published; +} + +export function articleGenre(content: string) { + const marker = //i.exec(content || ""); + const badge = (marker ? marker[1] : "MUSIC NEWS").toUpperCase(); + return badge === "MUSIC NEWS" + ? { words: ["Volksmusik", "Schlager", "Musik News"], section: "Musik News" } + : { words: ["Star News", "Promi News", "Promi"], section: "Star News" }; +} + +export const INFO_SEO: Record = { + "/ueber-uns": { title: "Über FOLX TV – Musik-News, Interviews und Fernsehen", description: "FOLX TV verbindet aktuelle Schlager- und Volksmusik-News mit Künstler-Interviews, Videos und eigenen TV-Produktionen. Mehr über unser Angebot.", type: "AboutPage" }, + "/kontakt": { title: "Kontakt", description: "Kontakt zu FOLX TV: Fragen zu Artikeln, Musik-News, Interviews und unserem Programm. Schreiben Sie uns über das Kontaktformular.", type: "ContactPage" }, + "/impressum": { title: "Impressum", description: "Anbieterangaben und rechtliche Informationen zu FOLX TV und dem Nachrichtenangebot auf folx.tv.", type: "WebPage" }, + "/datenschutz": { title: "Datenschutz", description: "Informationen zum Datenschutz und zur Verarbeitung personenbezogener Daten auf folx.tv.", type: "WebPage" }, + "/empfang-folx-tv": { title: "FOLX TV empfangen – Livestream, App, Kabel und IPTV", description: "Informationen zum Empfang des FOLX TV Fernsehprogramms: Livestream, Apps sowie Kabel und IPTV in Deutschland, Österreich und der Schweiz.", type: "WebPage" } +};