Introduces a new `MobileStickyAd` component that displays a 320x50px ad banner at the bottom of the screen on mobile devices only. The ad appears after a 2-second delay and can be dismissed by the user for the session. The footer dynamically adjusts its padding to prevent content overlap with the sticky ad. The `replit.md` file is updated to reflect this new feature. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: db8604a4-d491-44c8-b0ac-b67d779b436a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/tdiozLO Replit-Helium-Checkpoint-Created: true
12 KiB
12 KiB
Folx Music Television - folx.tv
Overview
The official website for Folx Music Television (folx.tv). Dark-themed bento grid layout with content for folk music (Volksmusik/Schlager) fans. Features articles, videos, photo gallery, horoscope widget + subpage, recipe widget + subpage, Google News feed, and integrated AdSense ads. All content is hardcoded in seed for production deployments.
SEO
- Primary keyword: "Volksmusik" — used across all page titles, meta descriptions, OG tags, and structured data
- Dynamic canonical URLs via
usePageMetahook (updates<link rel="canonical">per page) - SSR article pages: server-side meta tags (OG, Twitter, description, keywords, canonical) in both
server/vite.ts(dev) andserver/static.ts(prod) stripExistingMeta()removes duplicate meta/canonical from base HTML before injecting article-specific ones- JSON-LD structured data: WebSite (home) with SearchAction, NewsArticle + BreadcrumbList (articles)
- Sitemap at
/sitemap.xml— includes all static pages, categories, horoscope signs, and articles - robots.txt disallows
/api/,/search,/admin/ - H1 tag on home page (sr-only): "FOLX TV – Volksmusik & Schlager Fernsehsender"
- Logo alt text includes "Volksmusik & Schlager Fernsehsender"
Architecture
- Frontend: React + Vite + TailwindCSS + shadcn/ui (dark mode)
- Backend: Express.js + Node.js
- Database: PostgreSQL with Drizzle ORM
- Routing: wouter (frontend), Express (backend API)
Key Features
- MSN-style bento grid homepage with mixed article/widget layout
- FeaturedCarousel with 5 article pages (page 2 = wide layout) + gallery page
- Photo gallery widget (547 Dropbox images) with fullscreen lightbox carousel
- Horoscope widget with element colors, star ratings, full /horoskop subpage
- Recipe widget + full /rezepte subpage (21 recipes across 5 regions: Österreich, Bayern, Schwaben/Baden, Südtirol/Alpen, Norddeutschland) with AI-generated images
- Google News RSS widget (Volksmusik/Schlager news, 5 items, auto-rotate)
- Google AdSense integration (ca-pub-4465464714854276)
- Interstitial overlay ad on article pages (3s delay, shows every other article visit, sessionStorage counter)
- Parallax/reveal ad below article content (sticky background ad revealed on scroll)
- Mobile sticky banner ad at bottom of screen (2s delay, session-dismissible, mobile-only via useIsMobile hook)
- Web Push Notifications (bell icon in header, auto-send on new articles, admin panel at /admin/push)
- Article listing with featured carousel and category filtering
- HTML content supports embedded iframes (bunny.net, YouTube, Facebook, Instagram, TikTok)
- DOMPurify sanitization for safe HTML rendering
- Responsive design with mobile navigation
Data Model
articles: id (serial), title, slug (unique), excerpt, content (HTML), coverImage, category, author, featured, views, publishedAtarticle_views: id (serial), articleId, ipHash (sha256 truncated to 16 chars), viewedAt — tracks unique IP views per article
Seed Behavior
server/seed.tsruns on startup and always syncs article content from seedArticles array to the database- Existing articles (matched by slug) are fully updated with latest title, excerpt, content, coverImage, category, author, featured, publishedAt
- New articles are inserted; articles not in seed list are deleted
- HTML entity „/“ in title/excerpt are auto-replaced with „/" after seeding
- Any changes to article content in seed.ts will be reflected on next deployment
- Currently all articles are under "News" category (+ "Video" for video-only articles)
- Future subcategory plan: add subcategory field for "Porträt" label on portrait articles
- Portrait articles (for future subcategory): die-edlseer, die-pagger-buam, anita-hofmann, oeschs-die-dritten
API Endpoints
GET /api/articles- All articlesGET /api/articles/featured- Featured articlesGET /api/articles/popular- Popular articles by viewsGET /api/articles/category/:category- Filter by categoryGET /api/articles/:slug- Single article (increments views)POST /api/articles- Create articlePATCH /api/articles/:id- Update articleDELETE /api/articles/:id- Delete articlePOST /api/upload- Upload image fileGET /api/push/vapid-key- Get VAPID public key for push subscriptionsPOST /api/push/subscribe- Subscribe to push notificationsPOST /api/push/unsubscribe- Unsubscribe from push notificationsGET /api/push/count- Get push subscriber countPOST /api/admin/push/send- Send push notification to all subscribersGET /api/gallery- Shuffled gallery images from Cloudinary (with artist names from filenames + overrides)GET /api/gallery/focal-points- Gallery image focal points (JSON)PUT /api/gallery/focal-points/:fileName- Set focal point for gallery imageDELETE /api/gallery/focal-points/:fileName- Reset focal point for gallery imageGET /api/gallery/artists- Artist name overrides (JSON)PUT /api/gallery/artists/:fileName- Set/update artist name overridePOST /api/gallery/migrate-to-cloudinary- Migrate remaining Dropbox images to CloudinaryGET /api/gallery/thumb- Proxy endpoint for Dropbox thumbnail resizing (sharp, 400x400, 30-min cache)GET /api/news-feed- Google News RSS feed for Volksmusik/Schlager (15-min cache, stale-while-error)GET /api/breaking-news- Google News RSS feed for general news (15-min cache, stale-while-error)GET /api/videos- BunnyCDN video list (30-min cache, stale-while-error)GET /api/videos/:guid- BunnyCDN video details
File Structure
shared/schema.ts- Drizzle schema + Zod validationserver/db.ts- Database connectionserver/storage.ts- Storage interface + DatabaseStorageserver/routes.ts- API routes + gallery + news feedserver/seed.ts- Hardcoded seed data (articles)server/gallery-data.json- Fallback gallery data (used when Dropbox/Cloudinary unavailable)server/cloudinary-gallery-map.json- Cloudinary public ID map for 176 migrated imagesserver/gallery-focal-points.json- Manual focal point overrides for gallery imagesserver/gallery-artist-overrides.json- Manual artist name overrides for gallery imagesserver/cloudinary.ts- Cloudinary upload, URL generation, compression logicclient/src/pages/home.tsx- MSN-style bento grid homepageclient/src/pages/article.tsx- Article detail pageclient/src/pages/category.tsx- Category listing pageclient/src/pages/videos.tsx- Videos pageclient/src/pages/gallery.tsx- Full gallery pageclient/src/pages/horoscope.tsx- Full horoscope page (12 signs, love/career/health, weekly/monthly)client/src/pages/recipes.tsx- Full recipes page (21 recipes, 5 regions, AI-generated images)client/src/lib/horoscope-data.ts- Shared horoscope data (signs, texts, helpers)client/src/components/header.tsx- Header with nav (Start, News, Video, Galerie, Horoskop, Rezepte)client/src/components/footer.tsx- Footer with linksclient/src/components/photo-gallery.tsx- Gallery widget + lightbox carousel + paginated gallery page (24/batch infinite scroll) + artist name displayclient/src/pages/admin-gallery.tsx- Admin page for gallery management (focal points + artist names) at /admin/galleryclient/src/components/horoscope-widget.tsx- Horoscope widget with element colorsclient/src/components/recipe-widget.tsx- Recipe widget with modalclient/src/components/news-widget.tsx- Google News RSS widgetclient/src/components/adsense.tsx- AdSense ad components
Homepage Layout (MSN Bento Grid)
- Row 1: FeaturedCarousel (hero + side articles + TopStorys, page 2 = wide hero)
- Row 2: 2 articles + PhotoGalleryWidget + RecipeWidget (mixed)
- Row 3: HoroscopeWidget + 2 articles + NewsWidget (mixed)
- Row 4: 3 articles + NativeAdCard
- Row 5: 3 articles + NativeAdCard
Branding
- Dark theme (class="dark" on html)
- Primary/brand color: crimson/red (RGB 218,35,77)
- Background: 0 0% 5%, Card: 0 0% 9%
- Font: Poppins
- Logo: folx_MT_poz_b_1772296729169.png
External Services
- Bunny.net: Library 476412, CDN vz-7982dfc4-cc8.b-cdn.net (NO autoplay)
- Google AdSense: ca-pub-4465464714854276
- Cloudinary: Gallery images (cloud_name: djqxt0pf3, 176 images migrated from Dropbox, face-aware cropping)
- Dropbox: Original gallery source (fallback if Cloudinary map empty)
- Google News RSS: Volksmusik/Schlager news feed
Publishing Workflow
Adding or Updating Articles
- Edit seed data: Update
server/seed.tswith new article data- Add new articles to the
articlesDataarray - Update existing articles as needed
- Use unique slugs for SEO-friendly URLs
- Add new articles to the
- Run seed locally (for development testing):
- Execute:
npm run seed - This populates the development database with article data
- Execute:
- Commit changes: Push code changes to the repository
- Deploy to production:
- Deployment is configured as "autoscale" type in
.replit - Build command:
npm run build - Run command:
node ./dist/index.cjs - The production database is separate from the development database
- Important: Seed must run on every deploy to ensure the production database is updated with new/modified articles
- To run seed on deploy, add seed execution to the deployment process or manually trigger seed after deployment
- Deployment is configured as "autoscale" type in
Database Management
- Development: Local PostgreSQL database (populated by
npm run seed) - Production: Separate PostgreSQL database on Replit deployment
- All article content is hardcoded in
server/seed.tsfor reproducible deployments - The
DATABASE_URLenvironment variable automatically points to the correct database (local for dev, production for deploy)
Deployment Checklist
- Update
server/seed.tswith new article data - Test locally: run
npm run seedand verify articles appear in dev - Run
npm run buildto ensure no build errors - Commit all changes
- Deploy via Replit deployment interface
- After deployment, ensure seed runs on the production database (may require manual trigger or additional setup)
- Verify live site at https://www.folx.tv shows updated content
Adding Articles Workflow
When adding new articles:
- Always optimize images first: Resize to max 1200px wide, quality 85%, target <300KB. Use
convertcommand:
For OG/social sharing images, use 1200x630 crop:convert input.jpg -resize 1200x -quality 85 output.jpgconvert input.jpg -resize 1200x630^ -gravity center -extent 1200x630 -quality 85 output.jpg - Copy optimized image to
client/public/uploads/ - Add article to
seedArticlesarray inserver/seed.ts - Add UPDATE migration at bottom of
seedDatabase()function to update existing articles in production DB - If article already exists in DB, seed won't update it — must add explicit UPDATE SQL migration
- Featured articles appear in homepage carousel; set
featured: falsefor items that should not appear there - Videos use category "Video", news uses category "News"
- Bunny CDN embeds: use
https://player.mediadelivery.net/embed/476412/{video-id}— do NOT take text/title from Bunny, use user-provided text only - Facebook embeds: copy the iframe code as-is from user
- After deploy, go to Facebook Sharing Debugger and click "Scrape Again" to refresh cache
Article Page Layout
- Outer wrapper:
max-w-7xl(for PageSideAds), inner content:max-w-4xl mx-auto - Cover image:
max-h-[420px]with object-cover - Title:
text-2xl md:text-3xlfont-bold - Meta (author/date/views):
text-xswithw-3.5 h-3.5icons - Body text:
prose-basewithprose-p:text-[15px]andprose-headings:text-lg - In-article ad splits content at midpoint
Important Notes
- Tailwind
object-[center_25%]does NOT work — must use inlinestyle={{ objectPosition: "center 25%" }} - Horoscope widget navigates to /horoskop on click (no modal)
- News widget external links open in new tab (target="_blank")
- FeaturedCarousel: page 1 = wide (5 cols, no side cards), last page = gallery
- All images use
objectPosition: "center 25%"for better face cropping