Digiwaxx Radio — A Custom Urban Hip-Hop Streaming Platform
Digiwaxx Radio is an internet radio station for urban hip-hop. The brief was simple to say and hard to execute: build a station website that feels like an app, not a blog. Live audio that survives navigation. Real listener interaction. A back office a non-developer can actually run. And the freedom for the owner to push code changes without ever opening a code editor.
I built the entire site as a constellation of ~65 custom-built modules deployed through Code Snippets Pro, on top of a thin Twenty Twenty-Five child theme. Roughly 13,000 lines of custom PHP, JavaScript, and CSS — no page builders, no premium-plugin sprawl, no theme lock-in. Every piece was designed to be portable, version-controlled in the studio’s GitHub, and deployable in seconds via WP-CLI.
What was built
🎧 Live audio & player system
- Sticky bottom player — persistent across every page, mobile-responsive, with artwork, scrubber, volume, and one-tap play
- Hero player variant — large branded version for the homepage
- Persistent audio across navigation — uses Hotwire Turbo so clicking a link doesn’t interrupt the stream
- Pop-out mini player window — a separate browser window, synced live with the main tab via BroadcastChannel
- Cover art with Apple Music link-through — current track’s artwork is a one-click jump to buy on Apple
- Mobile-aware sticky positioning — uses safe-area-inset for iPhone notches and home indicators
- Media Session API integration — track title and artist appear in OS-level media controls (lock screen, Bluetooth car audio)
📊 Now-playing & charts
- Shoutcast stats proxy — custom REST endpoint with 15-second cache for now-playing data
- Auto chart logger — cron job logs every song played into a custom DB table
- iTunes Search API integration — 5-stage matcher with album-walking fallback and diacritics handling
- Auto-enrichment on play — first time a song plays, the Artist + Track WordPress posts are created automatically with full metadata, artwork, and 30-second preview
- Top Tracks / Top Artists — last-7-day charts surfaced on the dashboard and homepage
- Spin history — every track page shows the last 20 times it aired
📝 Content system
- 6 Custom Post Types — DJ, Show, Mix, Artist, Track, News
- 4 Custom Taxonomies — genre, show day, news category, news artist mentions
- Branded single-page templates for every content type, all hooked through clean
do_action()extension points - Quick-Add admin forms — one-page editors for Shows, DJs, Mixes, and News (no Gutenberg complexity for station staff)
- Searchable genre tag-picker — unified spelling across the site
- Featured + Exclusive flag system — gold, magenta, and cyan badge variants in corner/pill/hero sizes
- Featured Hero rotating banner — auto-cycling slideshow with dots, arrows, and touch-swipe support
🔎 Site features
- Live site search — Cmd/Ctrl+K overlay panel querying artists, tracks, shows, and mixes in one REST call
- Branded /shows page — live show in a hero card with next-up grid
- News hub — featured story + grid + category filters
- Studio dashboard — live now-playing, stats grid, quick-action buttons, top lists, recent activity
👥 Member system
- Custom auth pages — branded
/signin,/signup,/forgot,/account(no wp-login.php anywhere) - Marketing data capture on signup — DOB, location, favorite genres, listening devices, preferred listen times, newsletter topics, promo code
- Subscriber dashboard — Overview, Favorites, History, Settings tabs
- Custom Studio profile editor — full-page branded profile management replacing wp-admin/profile.php for staff
🎛️ Admin Studio (branded back office)
- Dark/magenta admin shell — hides all WordPress chrome from station managers
- Clean URL aliases —
/studio/*,/login,/account/*— wp-admin is invisible to non-developers - Station Manager custom role — granular capabilities for content management without WP admin power
- User Switching — admin can log in as any user (signed cookie, switch-back link)
- Card-based admin list views — every content type browses as a visual grid instead of a default WP table, with hover actions (View / Edit / Feature / Exclusive / Delete)
- Per-track Apple Music URL override — manual matcher for tracks iTunes Search misses
🎵 Exclusive tracks
- MP3 upload pipeline — reads ID3 tags from uploaded files and auto-creates Artist + Track posts as Digiwaxx Exclusives
- Custom full-track audio player — dedicated player with scrubber on exclusive tracks, distinct from the 30-second preview on regular tracks
- EXCLUSIVE corner/pill/hero badges — auto-applied across grids, hero, and single pages
🤖 AI-powered news importer
- 4 AI provider support — Google Gemini, Groq (Llama), Anthropic Claude, OpenAI
- RSS feed monitoring — pulls fresh stories from configured feeds on a cron
- AI rewriter — each story is rewritten in Digiwaxx’s voice with custom prompts
- Auto-extracts tags + artist mentions — flags which Artist posts each article mentions, surfaces “In the News” on artist pages
- 6-stage JSON extraction — battle-tested fallback chain for malformed AI responses (truncation, missing quotes, raw HTML in JSON strings)
- Rate-limit-aware — stops cleanly on 429s, paces calls on free tiers
- Retag batch tool — backfills tags/mentions across the entire news archive
- Featured image sideloading — pulls hero images from source articles
💬 Live chat
- Custom DB-backed chat — public read, member-only post
- REST polling endpoint with no-cache headers (solved a multi-browser sync bug)
- DJ badge — gold pill + magenta-glow message bubble for staff
- Adaptive polling — 4 s when open, 30 s when minimized, paused when tab hidden
- Embedded in pop-out player — full chat available in the mini window
- Turbo Drive double-init guard — solves a subtle duplicate-handler bug that affects every SPA-style WP site
🎤 Song requests (custom EverestCast integration)
- EverestCast API proxy — WordPress controls every request, the browser never talks to the broadcast server directly
- Local catalog mirror — refreshes every 5 minutes (EverestCast’s filter params silently ignore inputs, so server-side filtering wasn’t an option)
- Members-only with per-user cooldown — works around EverestCast’s IP-based dedup that would otherwise block everyone on shared hosting
- Full request log — every attempt stored with status, response code, body, user, message — full audit trail
- 4 surfaces — dedicated
/requestpage, music-note button on the sticky player, tab in the chat popup, and per-track widget on every Track page - Auto-link on track creation — when a new song plays on the stream and a Track post is auto-created, the system matches it against the EverestCast catalog by normalized title + artist (handles diacritics, featuring suffixes, HTML entities, paren version info)
- One-time backfill — matched 56 of 57 existing tracks to EverestCast in a single dry-run + commit pass
- Chat bridge — every successful request drops a styled card into live chat with cover art, track info, and the listener’s shoutout
- Admin Manage Requests page — branded dashboard with stats (today/week/month/all-time), top-requested tracks, top requesters, full activity log with status badges
- EverestCast Link meta box — search-as-you-type picker on Track edit screens for manual matching
- Settings tab with 6 anti-abuse controls — cooldown, daily cap, account age, quiet hours, block-list, profanity filter (mask or reject), role gating, chat-bridge toggle
🔌 Developer plumbing
- WP-CLI SSH deploy pipeline — Python scripts wrap paramiko for SFTP and remote
wpcommands - Code Snippets Pro batched import flow — every snippet round-trips through JSON for Git history
- Custom hook system —
do_action('digiwaxx_render_single_*')+do_action('digiwaxx_track_upserted')extension points keep snippets loosely coupled
Notable engineering challenges solved
Turbo Drive double-init. Hotwire Turbo’s turbo:load fires alongside DOMContentLoaded on initial page render — without an init guard, every event handler binds twice. Result: every chat message sent twice, every button click double-fires. Solved with a __bound flag pattern + data-turbo-permanent for widgets that survive navigation.
WP_Query meta-key INNER JOIN trap. WordPress silently turns meta_key + orderby = meta_value_num into an INNER JOIN that excludes posts missing that meta. We had 12 artists invisible from the admin list because of this. Fixed by switching to meta_query with EXISTS / NOT EXISTS branches and named-clause sorting.
AI JSON extraction. Llama on Groq routinely produces JSON with unescaped quotes, missing opening quotes on long strings, and mid-stream truncation. Built a 6-stage extractor: direct parse → strip fences → walk for balanced braces → escape-repair → truncation-repair → regex-by-key-marker last resort. It handles responses that aren’t even valid JSON.
EverestCast’s broken DRF filters. The broadcast server’s REST surface looks Django-REST-Framework-shaped, but ?search=, ?ordering=, and ?limit= are silent no-ops. Mirror the catalog locally and filter client-side — only path forward.
Per-user vs per-IP rate limiting. EverestCast dedups song requests by source IP, but every WordPress request comes from SiteGround’s shared NAT. One listener’s request would block everyone on the shared host. Solved by moving rate-limiting to per-WP-user account, then mapping EverestCast’s “blocked” response into a friendly UX message.
CSS [hidden] specificity. The browser UA stylesheet’s [hidden] { display: none } has near-zero specificity. Any .foo { display: flex } rule wins, so toggling the hidden attribute via JS silently fails. Pair every display: flex/grid/block rule with a matching [hidden] override.
Outcomes
- One developer, ~6 weeks, fully shipped to production — every feature listed above is live and serving real listeners
- Zero recurring plugin license costs — every paid-plugin equivalent (chat, song requests, AI news, member dashboard, custom admin) was built from scratch
- Station staff manage everything themselves — quick-add forms, drag-and-drop card grids, branded admin shell mean no developer in the loop for daily operations
- Sub-second TTFB on cached pages with full audio + chat + live updates running in the background
- Owner can deploy code changes from a snippet manager — no FTP, no theme files to edit, no CI pipeline to manage
Tech stack at a glance
Backend: PHP 8.2 · WordPress 7.0 · Custom REST API · WP-Cron · MySQL custom tables · WP-CLI
Frontend: Vanilla JS · Hotwire Turbo · CSS custom properties · Media Session API · BroadcastChannel · Page Visibility API
Integrations: Shoutcast 2 stats · iTunes Search API · EverestCast API · Google Gemini / Groq / Anthropic / OpenAI · SimplePie RSS
Infrastructure: SiteGround managed hosting · SSH/SFTP deploy via Python + paramiko · Code Snippets Pro for distribution