Present FOLX as a music news publisher and unify site SEO

This commit is contained in:
Codex
2026-10-07 13:42:48 +02:00
parent c5c3fe290e
commit cc306921d7
14 changed files with 173 additions and 115 deletions
+6 -6
View File
@@ -7,11 +7,11 @@
<meta charset="UTF-8" />
<meta name="google-site-verification" content="_rO4EdfjGp2lPdm3fpxkWv8k1id0wkthn9GxwsXLpeo" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
<title>Volksmusik & Schlager TV | FOLX Music Television – Nachrichten, Videos & mehr</title>
<meta name="description" content="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!" />
<title>Schlager &amp; Volksmusik News | FOLX TV</title>
<meta name="description" content="Aktuelle Schlager- und Volksmusik-News, Künstler-Interviews und Nachrichten aus der Musikszene. Jetzt Artikel und Videos auf FOLX TV entdecken." />
<meta name="keywords" content="Volksmusik, Schlager, Volksmusik TV, Schlager TV, Folx TV, FOLX Music Television, volkstümliche Musik, Musiksender, Alpenmusik, Volksmusik Nachrichten, Schlager News, Musikvideos, Live Shows, Volksmusik Künstler, Schlager Stars, Oberkrainer, Volksmusik Sender, Schlager Hits, volkstümliche Schlager" />
<meta property="og:title" content="Volksmusik & Schlager TV | FOLX Music Television" />
<meta property="og:description" content="FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos, Künstlerporträts und Live-Shows." />
<meta property="og:title" content="Schlager &amp; Volksmusik News | FOLX TV" />
<meta property="og:description" content="Aktuelle Schlager- und Volksmusik-News, Künstler-Interviews und Nachrichten aus der Musikszene. Jetzt Artikel und Videos auf FOLX TV entdecken." />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Folx Music Television" />
<meta property="og:url" content="https://folx.tv/" />
@@ -23,8 +23,8 @@
<meta property="og:image:alt" content="FOLX Network - Volksmusik & Schlager TV" />
<meta property="og:locale" content="de_DE" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Volksmusik & Schlager TV | FOLX Music Television" />
<meta name="twitter:description" content="FOLX TV – Ihr Fernsehsender Nr. 1 für Volksmusik und Schlager. Aktuelle Nachrichten, Musikvideos und Live-Shows." />
<meta name="twitter:title" content="Schlager &amp; Volksmusik News | FOLX TV" />
<meta name="twitter:description" content="Aktuelle Schlager- und Volksmusik-News, Künstler-Interviews und Nachrichten aus der Musikszene. Jetzt Artikel und Videos auf FOLX TV entdecken." />
<meta name="twitter:image" content="https://folx.tv/og-image.jpg" />
<meta property="fb:pages" content="281200958640258" />
<meta name="facebook-domain-verification" content="b4y482htm54uybm563tfr1ucb1n46w" />
+2 -2
View File
@@ -23,8 +23,8 @@ export default function Footer({ narrow }: { narrow?: boolean }) {
<div>
<h3 className="font-bold text-card-foreground text-lg mb-3">Folx Music Television</h3>
<p className="text-muted-foreground text-sm leading-relaxed mb-4">
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.
</p>
<div className="flex gap-3">
{SOCIAL_LINKS.map((s) => (
+1 -1
View File
@@ -43,7 +43,7 @@ export default function Header() {
<div className="flex items-center justify-between gap-4 h-16">
<Link href="/">
<div className="flex items-center gap-2 cursor-pointer" data-testid="link-logo">
<img src={folxLogo} alt="FOLX TV – Volksmusik & Schlager Fernsehsender" className="h-32 w-auto object-contain py-1" />
<img src={folxLogo} alt="FOLX TV – Schlager & Volksmusik News" className="h-32 w-auto object-contain py-1" />
</div>
</Link>
+25 -6
View File
@@ -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]);
}
+2 -4
View File
@@ -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 (
<div className="min-h-screen bg-background">
<Header />
+10 -14
View File
@@ -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",
+2 -1
View File
@@ -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 (
<div className="min-h-screen bg-background">
<Header />
+2 -4
View File
@@ -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 (
<div className="min-h-screen bg-background">
<Header />
+4 -31
View File
@@ -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() {
<Header />
<PageSideAds />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 space-y-4">
<h1 className="sr-only">FOLX TV – Volksmusik & Schlager Fernsehsender: Nachrichten, Videos und Live-Shows</h1>
<h1 className="sr-only">FOLX TV – Schlager & Volksmusik News</h1>
{/* Prej lastna kopija pasu s trdo visino 90 px in responsive=false:
Google je vanjo postavil 320x50 in okrog je ostal siv rob.
+2 -1
View File
@@ -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 (
<div className="min-h-screen bg-background">
<Header />
+2 -1
View File
@@ -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");
+20
View File
@@ -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("<!--folx-updated:invalid-->", pub), pub);
assert.equal(articleModifiedTime("<!--folx-updated:2025-01-01-->", pub), pub);
assert.equal(articleModifiedTime("<!--folx-updated:2099-01-01-->", pub), pub);
assert.equal(articleModifiedTime("<!--folx-updated:2026-02-01-->", pub), "2026-02-01T00:00:00.000Z");
assert.equal(articleGenre("<!--folx-badge:STAR NEWS-->").section, "Star News");
assert.ok(!articleGenre("<!--folx-badge:STAR NEWS-->").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");
+42 -44
View File
@@ -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 = /<!--\s*folx-badge:\s*([^>]*?)\s*-->/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<string, string> = {
// 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(
"</head>",
`<script type="application/ld+json">${JSON.stringify(homeLd).replace(/</g, "\\u003c")}</script>\n</head>`);
`<script id="home-jsonld" type="application/ld+json">${JSON.stringify(homeLd).replace(/</g, "\\u003c")}</script>\n</head>`);
// 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 <!--folx-updated:ISO-->
const modDate = (() => {
const mm = /<!--folx-updated:([^>]+?)-->/.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 = `<script type="application/ld+json">${JSON.stringify(jsonLd).replace(/</g, "\\u003c")}</script>`;
const jsonLdTag = `<script id="article-jsonld" type="application/ld+json">${JSON.stringify(jsonLd).replace(/</g, "\\u003c")}</script>`;
const videoLd = videoId ? {
"@context": "https://schema.org",
@@ -557,6 +525,8 @@ export function serveStatic(app: Express) {
`<meta property="og:title" content="${escapeHtml(article.title)}" />`,
`<meta property="og:description" content="${escapeHtml(article.excerpt)}" />`,
`<meta property="og:type" content="article" />`,
`<meta property="article:published_time" content="${pubDate}" />`,
`<meta property="article:modified_time" content="${modDate}" />`,
`<meta property="og:url" content="${escapeHtml(articleUrl)}" />`,
`<meta property="og:image" content="${escapeHtml(finalImage)}" />`,
`<meta property="og:image:secure_url" content="${escapeHtml(finalImage)}" />`,
@@ -575,7 +545,7 @@ export function serveStatic(app: Express) {
`<meta name="description" content="${escapeHtml(article.excerpt)}" />`,
`<meta name="keywords" content="${escapeHtml(folxGenre(article).words.join(", "))}, ${escapeHtml(article.title)}, ${escapeHtml(article.category || 'News')}, FOLX TV" />`,
`<link rel="canonical" href="${escapeHtml(articleUrl)}" />`,
`<title>${escapeHtml(article.title)} - Volksmusik & Schlager | Folx Music Television</title>`,
`<title>${escapeHtml(article.title)} | FOLX TV</title>`,
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>/, [
`<title>${escapeHtml(title)}</title>`,
`<meta name="description" content="${escapeHtml(info.description)}" />`,
`<link rel="canonical" href="${pageUrl}" />`,
`<meta property="og:title" content="${escapeHtml(title)}" />`,
`<meta property="og:description" content="${escapeHtml(info.description)}" />`,
`<meta property="og:type" content="website" />`,
`<meta property="og:url" content="${pageUrl}" />`,
`<meta property="og:image" content="${canonicalBase}/og-image.jpg" />`,
`<meta name="twitter:card" content="summary_large_image" />`,
`<meta name="twitter:title" content="${escapeHtml(title)}" />`,
`<meta name="twitter:description" content="${escapeHtml(info.description)}" />`,
`<meta name="twitter:image" content="${canonicalBase}/og-image.jpg" />`,
`<script id="info-jsonld" type="application/ld+json">${JSON.stringify(ld).replace(/</g, "\\u003c")}</script>`,
].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 = [
+53
View File
@@ -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 = /<!--folx-updated:([^>]+?)-->/.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 = /<!--\s*folx-badge:\s*([^>]*?)\s*-->/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<string, { title: string; description: string; type: string }> = {
"/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" }
};