Integrate AdSense ads into the photo gallery, introduce SSR meta tags for the gallery page, and improve layout stability by reserving space for ads and dynamic content. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 81f3d8bc-2328-4374-a06e-57257dbb713b Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/OPD8Ro3 Replit-Helium-Checkpoint-Created: true
200 lines
12 KiB
Markdown
200 lines
12 KiB
Markdown
# 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 `usePageMeta` hook (updates `<link rel="canonical">` per page)
|
||
- SSR article pages: server-side meta tags (OG, Twitter, description, keywords, canonical) in both `server/vite.ts` (dev) and `server/static.ts` (prod)
|
||
- SSR gallery page (`/gallery`): OG meta, Twitter card, description, canonical — emphasizes exclusive backstage/concert photos from recordings
|
||
- `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, publishedAt
|
||
- `article_views`: id (serial), articleId, ipHash (sha256 truncated to 16 chars), viewedAt — tracks unique IP views per article
|
||
|
||
## Seed Behavior
|
||
- `server/seed.ts` runs 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 articles
|
||
- `GET /api/articles/featured` - Featured articles
|
||
- `GET /api/articles/popular` - Popular articles by views
|
||
- `GET /api/articles/category/:category` - Filter by category
|
||
- `GET /api/articles/:slug` - Single article (increments views)
|
||
- `POST /api/articles` - Create article
|
||
- `PATCH /api/articles/:id` - Update article
|
||
- `DELETE /api/articles/:id` - Delete article
|
||
- `POST /api/upload` - Upload image file
|
||
- `GET /api/push/vapid-key` - Get VAPID public key for push subscriptions
|
||
- `POST /api/push/subscribe` - Subscribe to push notifications
|
||
- `POST /api/push/unsubscribe` - Unsubscribe from push notifications
|
||
- `GET /api/push/count` - Get push subscriber count
|
||
- `POST /api/admin/push/send` - Send push notification to all subscribers
|
||
- `GET /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 image
|
||
- `DELETE /api/gallery/focal-points/:fileName` - Reset focal point for gallery image
|
||
- `GET /api/gallery/artists` - Artist name overrides (JSON)
|
||
- `PUT /api/gallery/artists/:fileName` - Set/update artist name override
|
||
- `POST /api/gallery/migrate-to-cloudinary` - Migrate remaining Dropbox images to Cloudinary
|
||
- `GET /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 validation
|
||
- `server/db.ts` - Database connection
|
||
- `server/storage.ts` - Storage interface + DatabaseStorage
|
||
- `server/routes.ts` - API routes + gallery + news feed
|
||
- `server/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 images
|
||
- `server/gallery-focal-points.json` - Manual focal point overrides for gallery images
|
||
- `server/gallery-artist-overrides.json` - Manual artist name overrides for gallery images
|
||
- `server/cloudinary.ts` - Cloudinary upload, URL generation, compression logic
|
||
- `client/src/pages/home.tsx` - MSN-style bento grid homepage
|
||
- `client/src/pages/article.tsx` - Article detail page
|
||
- `client/src/pages/category.tsx` - Category listing page
|
||
- `client/src/pages/videos.tsx` - Videos page
|
||
- `client/src/pages/gallery.tsx` - Full gallery page
|
||
- `client/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 links
|
||
- `client/src/components/photo-gallery.tsx` - Gallery widget + lightbox carousel + paginated gallery page (24/batch infinite scroll) + artist name display
|
||
- `client/src/pages/admin-gallery.tsx` - Admin page for gallery management (focal points + artist names) at /admin/gallery
|
||
- `client/src/components/horoscope-widget.tsx` - Horoscope widget with element colors
|
||
- `client/src/components/recipe-widget.tsx` - Recipe widget with modal
|
||
- `client/src/components/news-widget.tsx` - Google News RSS widget
|
||
- `client/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
|
||
1. **Edit seed data**: Update `server/seed.ts` with new article data
|
||
- Add new articles to the `articlesData` array
|
||
- Update existing articles as needed
|
||
- Use unique slugs for SEO-friendly URLs
|
||
2. **Run seed locally** (for development testing):
|
||
- Execute: `npm run seed`
|
||
- This populates the development database with article data
|
||
3. **Commit changes**: Push code changes to the repository
|
||
4. **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
|
||
|
||
### 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.ts` for reproducible deployments
|
||
- The `DATABASE_URL` environment variable automatically points to the correct database (local for dev, production for deploy)
|
||
|
||
### Deployment Checklist
|
||
- [ ] Update `server/seed.ts` with new article data
|
||
- [ ] Test locally: run `npm run seed` and verify articles appear in dev
|
||
- [ ] Run `npm run build` to 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:
|
||
1. **Always optimize images first**: Resize to max 1200px wide, quality 85%, target <300KB. Use `convert` command:
|
||
```
|
||
convert input.jpg -resize 1200x -quality 85 output.jpg
|
||
```
|
||
For OG/social sharing images, use 1200x630 crop:
|
||
```
|
||
convert input.jpg -resize 1200x630^ -gravity center -extent 1200x630 -quality 85 output.jpg
|
||
```
|
||
2. Copy optimized image to `client/public/uploads/`
|
||
3. Add article to `seedArticles` array in `server/seed.ts`
|
||
4. Add UPDATE migration at bottom of `seedDatabase()` function to update existing articles in production DB
|
||
5. If article already exists in DB, seed won't update it — must add explicit UPDATE SQL migration
|
||
6. Featured articles appear in homepage carousel; set `featured: false` for items that should not appear there
|
||
7. Videos use category "Video", news uses category "News"
|
||
8. Bunny CDN embeds: use `https://player.mediadelivery.net/embed/476412/{video-id}` — do NOT take text/title from Bunny, use user-provided text only
|
||
9. Facebook embeds: copy the iframe code as-is from user
|
||
10. 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-3xl` font-bold
|
||
- Meta (author/date/views): `text-xs` with `w-3.5 h-3.5` icons
|
||
- Body text: `prose-base` with `prose-p:text-[15px]` and `prose-headings:text-lg`
|
||
- In-article ad splits content at midpoint
|
||
|
||
## Important Notes
|
||
- Tailwind `object-[center_25%]` does NOT work — must use inline `style={{ 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
|