# 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: ```bash npm install ``` Run development server: ```bash npm run dev ``` Build for production: ```bash npm run build ``` Preview production build locally: ```bash npm run preview ``` Deploy built assets with the included script: ```bash bash sync.sh ``` ## Managed Catalog Endpoint Runtime managed endpoint path: ```text /api/managed-stations.json ``` In production this is served by: - `public/.htaccess` rewrite rule - `public/api/managed-stations.php` Response shape: ```json { "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: ```bash npm run update:stations ``` Refresh and rebuild in one command: ```bash npm run update:stations:build ``` Custom source and output: ```bash node scripts/update-stations.mjs ``` ## Cron Automation (Server) Helper script: ```bash bash scripts/cron-refresh-stations.sh ``` Update-only helper script: ```bash 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): ```cron 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): ```cron 0 */2 * * * REPO_DIR=/opt/www/virtual/RadioPlayer /opt/www/virtual/RadioPlayer/scripts/cron-update-stations.sh ``` ## Project Structure ```text 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.