Add an artist listing page and improve SEO for various pages

Introduces a new `/kuenstler` page, API endpoint `/api/artists`, and enhances meta tags on `index.html` and for crawler requests on `/kuenstler`. Adds `KuenstlerPage.tsx` and updates `routes.ts` and `index.html`.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 401e2ec0-e00d-4f10-9d0e-60f3d479f9a5
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 69ee4fdb-c617-4cdd-b9f6-d3fab268d533
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/401e2ec0-e00d-4f10-9d0e-60f3d479f9a5/qFrskyV
This commit is contained in:
sebastjanartic
2026-02-13 17:53:37 +00:00
parent 19acaec48c
commit 590584b893
5 changed files with 304 additions and 2 deletions
+1
View File
@@ -19,6 +19,7 @@
<script async src="https://fundingchoicesmessages.google.com/i/pub-4465464714854276?ers=1" nonce="temp-nonce"></script>
<script nonce="temp-nonce">(function() {function signalGooglefcPresent() {if (!window.frames['googlefcPresent']) {if (document.body) {const iframe = document.createElement('iframe'); iframe.style = 'width: 0; height: 0; border: none; z-index: -1000; left: -1000px; top: -1000px;'; iframe.style.display = 'none'; iframe.name = 'googlefcPresent'; document.body.appendChild(iframe);} else {setTimeout(signalGooglefcPresent, 0);}}}signalGooglefcPresent();})();</script>
<meta name="description" content="Folx TV - Nummer 1 in Europa für Volksmusik und Schlager. Video Streaming Plattform mit Musik, Unterhaltung und Live-Streaming." />
<meta name="keywords" content="Volksmusik, Schlager, Folx TV, Kastelruther Spatzen, Monika Martin, Oswald Sattler, Angela Wiedl, Die Grubertaler, Michael Hirte, Sašo Avsenik, Jonny Hill, Rosanna Rocci, Linda Feller, Julia Buchner, Melanie Payer, Marlena Martinelli, Musikvideo, Streaming, Volksmusik TV" />
<!-- Google Search Console verification -->
<meta name="google-site-verification" content="YcRvFNGnAbAnlhQvhODVJOaqHLVdOKnpCqk7oH-xeKE" />
+2
View File
@@ -11,6 +11,7 @@ import GeschichteLiedPage from "@/pages/GeschichteLiedPage";
import GipfelstammtischPage from "@/pages/GipfelstammtischPage";
import LivePage from "@/pages/LivePage";
import PlayerPage from "@/pages/PlayerPage";
import KuenstlerPage from "@/pages/KuenstlerPage";
import AdminPage from "@/pages/admin";
import PrivacyPolicy from "@/pages/PrivacyPolicy";
import TermsOfService from "@/pages/TermsOfService";
@@ -27,6 +28,7 @@ function Router() {
<Route path="/gipfelstammtisch" component={GipfelstammtischPage} />
<Route path="/live" component={LivePage} />
<Route path="/player" component={PlayerPage} />
<Route path="/kuenstler" component={KuenstlerPage} />
<Route path="/admin" component={AdminPage} />
<Route path="/privacy" component={PrivacyPolicy} />
<Route path="/terms" component={TermsOfService} />
+162
View File
@@ -0,0 +1,162 @@
import { useQuery } from '@tanstack/react-query';
import { Link, useLocation } from 'wouter';
import { ArrowLeft, Music, Users, Search, Menu, X } from 'lucide-react';
import { useState, useEffect } from 'react';
import HeaderAd from '@/components/HeaderAd';
interface Artist {
name: string;
videoCount: number;
}
export default function KuenstlerPage() {
const [searchQuery, setSearchQuery] = useState("");
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [, setLocation] = useLocation();
useEffect(() => {
document.title = 'Alle Künstler & Interpreten | Folx TV - Video';
const metaDescription = document.querySelector('meta[name="description"]');
if (metaDescription) {
metaDescription.setAttribute('content', 'Entdecken Sie alle Volksmusik- und Schlager-Künstler auf Folx TV. Nummer 1 in Europa für Volksmusik und Schlager.');
}
}, []);
const { data, isLoading } = useQuery<{ artists: Artist[], total: number }>({
queryKey: ['/api/artists'],
});
const artists = data?.artists || [];
const filteredArtists = searchQuery
? artists.filter(a => a.name.toLowerCase().includes(searchQuery.toLowerCase()))
: artists;
const letters = Array.from(new Set(filteredArtists.map(a => a.name.charAt(0).toUpperCase()))).sort((a, b) => a.localeCompare(b, 'de'));
if (isLoading) {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-500"></div>
</div>
);
}
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white">
<header className="bg-black/80 backdrop-blur-md border-b border-purple-500/30 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-4">
<Link href="/">
<span className="text-2xl font-bold bg-gradient-to-r from-purple-400 to-blue-400 bg-clip-text text-transparent cursor-pointer">
Folx TV
</span>
</Link>
</div>
<nav className="hidden md:flex items-center gap-6">
<Link href="/" className="text-gray-300 hover:text-white transition-colors">Startseite</Link>
<Link href="/folx-stadl" className="text-gray-300 hover:text-white transition-colors">FOLX STADL</Link>
<Link href="/geschichte-lied" className="text-gray-300 hover:text-white transition-colors">Geschichte des Liedes</Link>
<Link href="/gipfelstammtisch" className="text-gray-300 hover:text-white transition-colors">Gipfelstammtisch</Link>
<Link href="/kuenstler" className="text-purple-400 font-semibold">Künstler</Link>
<Link href="/live" className="text-red-400 hover:text-red-300 transition-colors flex items-center gap-1">
<span className="w-2 h-2 bg-red-500 rounded-full animate-pulse"></span> LIVE
</Link>
</nav>
<button className="md:hidden text-white" onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{isMobileMenuOpen && (
<nav className="md:hidden bg-black/95 border-t border-purple-500/20 py-4 px-4 space-y-3">
<Link href="/" className="block text-gray-300 hover:text-white py-2">Startseite</Link>
<Link href="/folx-stadl" className="block text-gray-300 hover:text-white py-2">FOLX STADL</Link>
<Link href="/geschichte-lied" className="block text-gray-300 hover:text-white py-2">Geschichte des Liedes</Link>
<Link href="/gipfelstammtisch" className="block text-gray-300 hover:text-white py-2">Gipfelstammtisch</Link>
<Link href="/kuenstler" className="block text-purple-400 font-semibold py-2">Künstler</Link>
<Link href="/live" className="block text-red-400 py-2">LIVE</Link>
</nav>
)}
</header>
<HeaderAd />
<main className="max-w-7xl mx-auto px-4 py-8">
<div className="flex items-center gap-3 mb-6">
<Link href="/">
<ArrowLeft className="text-gray-400 hover:text-white cursor-pointer" size={24} />
</Link>
<Users className="text-purple-400" size={28} />
<h1 className="text-3xl font-bold">Alle Künstler & Interpreten</h1>
<span className="bg-purple-500/20 text-purple-300 px-3 py-1 rounded-full text-sm">
{artists.length} Künstler
</span>
</div>
<div className="relative mb-8 max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={18} />
<input
type="text"
placeholder="Künstler suchen..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-purple-500"
/>
</div>
<div className="flex flex-wrap gap-2 mb-8">
{letters.map(letter => (
<a
key={letter}
href={`#letter-${letter}`}
className="w-9 h-9 flex items-center justify-center bg-gray-800 hover:bg-purple-600 rounded-lg text-sm font-bold transition-colors"
>
{letter}
</a>
))}
</div>
{letters.map(letter => {
const letterArtists = filteredArtists.filter(a => a.name.charAt(0).toUpperCase() === letter);
return (
<div key={letter} id={`letter-${letter}`} className="mb-8">
<h2 className="text-2xl font-bold text-purple-400 mb-4 border-b border-purple-500/30 pb-2">{letter}</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
{letterArtists.map(artist => (
<Link
key={artist.name}
href={`/?search=${encodeURIComponent(artist.name)}`}
className="flex items-center gap-3 p-3 bg-gray-800/40 hover:bg-gray-700/60 rounded-lg transition-colors group"
>
<div className="w-10 h-10 bg-purple-500/20 rounded-full flex items-center justify-center group-hover:bg-purple-500/40 transition-colors">
<Music size={18} className="text-purple-400" />
</div>
<div className="flex-1 min-w-0">
<p className="text-white font-medium truncate">{artist.name}</p>
<p className="text-gray-400 text-sm">{artist.videoCount} Video{artist.videoCount > 1 ? 's' : ''}</p>
</div>
</Link>
))}
</div>
</div>
);
})}
{filteredArtists.length === 0 && (
<div className="text-center py-16 text-gray-400">
<Users size={48} className="mx-auto mb-4 opacity-50" />
<p className="text-lg">Keine Künstler gefunden für "{searchQuery}"</p>
</div>
)}
</main>
<footer className="bg-black/80 border-t border-gray-800 py-6 mt-12">
<div className="max-w-7xl mx-auto px-4 text-center text-gray-500 text-sm">
<p>Folx TV - Nummer 1 in Europa für Volksmusik und Schlager</p>
</div>
</footer>
</div>
);
}
+7
View File
@@ -56,6 +56,13 @@ export default function Home() {
if (metaDescription) {
metaDescription.setAttribute('content', 'Folx TV - Nummer 1 in Europa für Volksmusik und Schlager. Video Streaming Plattform mit Musik, Unterhaltung und Live-Streaming.');
}
let metaKeywords = document.querySelector('meta[name="keywords"]');
if (!metaKeywords) {
metaKeywords = document.createElement('meta');
metaKeywords.setAttribute('name', 'keywords');
document.head.appendChild(metaKeywords);
}
metaKeywords.setAttribute('content', 'Volksmusik, Schlager, Folx TV, Kastelruther Spatzen, Monika Martin, Oswald Sattler, Angela Wiedl, Die Grubertaler, Michael Hirte, Sašo Avsenik, Jonny Hill, Rosanna Rocci, Linda Feller, Julia Buchner, Melanie Payer, Marlena Martinelli, Musikvideo, Streaming');
}, []);
// Update videos when new data comes in