RadioPlayer

RadioPlayer is a Vite + React web app for browsing, playing, and casting radio stations. It uses a managed catalog from public/stations.json, exposes a same-origin managed endpoint at /api/managed-stations.json, supports user stations with local persistence, and includes scripts to refresh station data from Radio.si.

Features

  • Station library with search, category tabs, favorites, recents, sorting, and pagination.
  • Country filtering plus a country picker to choose which Radio Browser countries are synced and shown.
  • Audio playback with previous/next controls, volume/mute, and coverflow quick picks.
  • Google Cast and AirPlay output support.
  • User station management with local import/export/reset tooling.
  • Managed catalog fallback chain: remote endpoint -> cached remote -> bundled stations.json.
  • Production service worker with app shell caching, station sync cache, and periodic refresh registration.
  • PWA install prompt and offline-friendly launch behavior.

Requirements

  • Node.js 18+
  • npm
  • For production managed endpoint: PHP-enabled web server with rewrite support (Apache + .htaccess in this repo)

Getting Started

Install dependencies:

npm install

Run development server:

npm run dev

Build for production:

npm run build

Preview production build locally:

npm run preview

Deploy built assets with the included script:

bash sync.sh

Managed Catalog Endpoint

Runtime managed endpoint path:

/api/managed-stations.json

In production this is served by:

  • public/.htaccess rewrite rule
  • public/api/managed-stations.php

Response shape:

{
  "schemaVersion": 1,
  "updatedAt": "2026-04-29T07:39:58.374Z",
  "stations": []
}

In dev/preview, Vite middleware in vite.config.js serves the same envelope from stations.json.

If the endpoint is unavailable, frontend and service worker fall back to bundled public/stations.json.

Station Data Refresh

Refresh managed stations from Radio.si:

npm run update:stations

Refresh and rebuild in one command:

npm run update:stations:build

Custom source and output:

node scripts/update-stations.mjs <source-url> <output-path>

Cron Automation (Server)

Helper script:

bash scripts/cron-refresh-stations.sh

Update-only helper script:

bash scripts/cron-update-stations.sh

The script:

  • acquires a lock file to prevent overlapping runs,
  • runs station refresh and build,
  • optionally runs DEPLOY_CMD,
  • writes logs to /tmp/radioplayer-refresh-stations.log by default,
  • rotates logs automatically when they exceed 1 MB, keeping 5 archives by default.

The update-only script:

  • acquires its own lock file,
  • runs only npm run update:stations,
  • optionally runs POST_UPDATE_CMD,
  • writes logs to /tmp/radioplayer-update-stations.log,
  • uses the same log rotation behavior.

Example crontab (every 6 hours):

0 */6 * * * REPO_DIR=/opt/www/virtual/RadioPlayer DEPLOY_CMD='rsync -av --delete /opt/www/virtual/RadioPlayer/dist/ /opt/www/virtual/RadioPlayer/' /opt/www/virtual/RadioPlayer/scripts/cron-refresh-stations.sh

Example update-only crontab (every 2 hours):

0 */2 * * * REPO_DIR=/opt/www/virtual/RadioPlayer /opt/www/virtual/RadioPlayer/scripts/cron-update-stations.sh

Project Structure

index.html
privacy.html
package.json
public/
  .htaccess
  api/
    managed-stations.php
  data/
    radio-stations.json
    radio-stations-sync.json
  manifest.json
  stations.json
  sw.js
scripts/
  bump-sw-cache-version.mjs
  cron-refresh-stations.sh
  cron-update-stations.sh
  import-radio-stations.ts
  update-stations.mjs
src/
  App.jsx
  main.jsx
  player.js
  styles.css
  radio/
  storage/

Notes

  • Service worker is only active in production builds. In dev, SW registrations and caches are cleared automatically.
  • src/main.jsx registers background sync / periodic sync where supported.
  • src/player.js also refreshes managed catalog on app focus after a timeout (fallback for browsers without periodic sync).
  • sync.sh deploys dist/ only.
  • If you edit stations manually, rerun npm run update:stations to resync from upstream when needed.
Description
No description provided
Readme 5.6 MiB
Languages
JavaScript 61.8%
CSS 19.3%
TypeScript 11.9%
HTML 5.3%
Shell 1.4%
Other 0.3%