4.2 KiB
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 +
.htaccessin 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/.htaccessrewrite rulepublic/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.logby 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.jsxregisters background sync / periodic sync where supported.src/player.jsalso refreshes managed catalog on app focus after a timeout (fallback for browsers without periodic sync).sync.shdeploysdist/only.- If you edit stations manually, rerun
npm run update:stationsto resync from upstream when needed.