ITDV-Site is a Next.js web application that serves as a platform for showcasing music content from various artists, with a focus on The Doerfels and related artists.
- Next.js
- TypeScript
- Tailwind CSS
- Service Workers for PWA functionality
- RSS Feed parsing
- CDN integration (Bunny.net)
- BoostBox integration (tardbox.com) for boost metadata logging
/app- Next.js pages and routes/components- Reusable React components/lib- Core utilities and services/public- Static assets/scripts- Utility scripts for deployment and maintenance
- Use TypeScript for type safety
- Follow Next.js best practices for routing and data fetching
- Use Tailwind CSS for styling
- Implement proper error handling and loading states
- Optimize for mobile-first design
- Ensure PWA compatibility
- Adding new artist content
- Managing RSS feeds
- Optimizing image and audio delivery
- Maintaining service worker functionality
- Managing CDN integration
- BoostBox integration (boost/stream metadata posted to tardbox.com via
lib/boostbox-service.ts)
Three JSON files in public/ serve the album list and must stay consistent:
static-albums.json— authoritative list. Read byapp/album/[id]/page.tsx(SSR) andapp/api/albums-static-cached/route.ts(homepage).albums-static-cached.json— secondary cache, written alongsidestatic-albums.jsonby admin endpoints. Drift here is the bug source for duplicate-album incidents.album-index.json— slug → array-position lookup built fromstatic-albums.json. Rebuild after any edit withnode scripts/build-album-index.js.
Writers: app/api/admin/manage-feeds/route.ts (POST/PUT/DELETE), scripts/regenerate-static-cache-direct.ts (full rebuild from data/feeds.json), scripts/reparse-affected.ts (single- or multi-feed reparse with assertions).
On Vercel the project filesystem is read-only, so any admin handler that mutates data/*.json or public/*.json must commit through GitHub — a raw fs.writeFileSync either throws EROFS or lands in per-instance tmpfs and silently disappears. Pattern (see app/api/admin/manage-feeds/route.ts:11-107 and app/api/admin/pinned-albums/route.ts):
- Detect with
const IS_VERCEL = !!(process.env.VERCEL || process.env.VERCEL_URL); - On Vercel:
await commitFiles([{ path, content }, ...], '<msg>')fromlib/github.ts(auto-deploy ships the change). - Locally:
fs.writeFileSyncdirectly. - Return
{ success, error?, deployed? }so the client can show a "redeploying…" toast on Vercel vs. instant-save locally.
The pinned-albums route used raw fs.writeFileSync and silently failed in prod for ~5 months before this was fixed. Don't add a third writer that bypasses the pattern.
A feed entry can include "trackFilter": "<term>". The parser at lib/rss-parser.ts keeps tracks whose title contains that term (case-insensitive) plus any chapter tracks (videoUrl + startTime + endTime). Used for compilation feeds — Satellite Spotlight filtered to CityBeach, Autumn Rust filtered to The Doerfels. Configure at the data layer; do not add new title-string heuristics to the parser.
Feeds not indexed on Podcast Index get "originalUrl": "PRIVATE_FEED_URL" redacted into committed JSON. The real URL only lives in local working copies and the production env. To re-parse such a feed locally, use scripts/reparse-them.ts or follow the redactInCache pattern in scripts/reparse-affected.ts to keep the redacted URL in the cache files while still calling the public source URL for parsing.
createSlug(title) and buildAlbumIndex(albums) are the single source of truth for slug generation and the lookup index. Import from here rather than reimplementing inline. scripts/build-album-index.js keeps a parallel JS copy for plain-node invocation — keep both in lockstep.
contexts/VideoContext.tsxowns video play state, separate fromAudioContext. The global now-playing bar (components/GlobalNowPlayingBar.tsx) reads from whichever has a current item.components/VideoPlayer.tsxacceptsexternalIsPlayingto sync the DOM<video>element withVideoContext.isPlaying(so the bottom-bar play/pause actually drives the video).- A track is "video" if it has
videoUrl. A "chapter" track hasvideoUrl + startTime + endTimeand represents a segment of a longer video. - Global shuffle (
app/page.tsx:289-293) skips video-only tracks (!track.url); hybrid audio+video tracks are shuffled and play as audio.
Import Task Master's development workflow commands and guidelines, treat as if import is in the main CLAUDE.md file. @./.taskmaster/CLAUDE.md
- Test on both desktop and mobile devices
- Verify PWA functionality
- Check CDN integration
- Validate RSS feed parsing
- Test audio playback across different scenarios
- Fast initial page load
- Optimized image loading
- Efficient audio streaming
- Smooth transitions between pages
- Reliable offline functionality
- Secure API endpoints
- Safe CDN usage
- Protected admin routes
- Proper environment variable handling
- Regular security audits
- BoostBox API key is client-side (
NEXT_PUBLIC_prefix) by design — BoostBox expects this