Major changes:
- streaming-drain: Custom drain that captures PCM from Harmony's pack
buffer (raw IEEE 754 floats in unsigned-byte-8 array), converts to
signed-16 PCM via CFFI, encodes to MP3 via LAME, and writes to
stream server's ring buffer
- Fixed ring buffer deadlock: buffer-read/buffer-write held lock then
called buffer-available which tried to acquire same lock. Created
internal %buffer-available/%buffer-free-space without locking.
- Fixed ring buffer zero-length guard for unbound variable in finally
- Fixed sleep duration in drain: was dividing raw byte count by
samplerate, now correctly converts to frames first
- Added flexi-streams wrapper for bivalent HTTP socket I/O
- Exported all public API symbols from cl-streamer package
- Added test-stream.lisp end-to-end test script
Verified: Amon Tobin FLAC -> 128kbps MP3 stream at localhost:8000
file reports: MPEG ADTS, layer III, v1, 128kbps, 44.1kHz, JntStereo
- harmony-backend.lisp: Audio pipeline connecting Harmony to encoders
- Uses :dummy drain to capture PCM without audio output
- Reads from packer bip-buffer via request-read/finish-read API
- Encodes PCM to MP3 via LAME and feeds to stream server
All three systems compile cleanly:
cl-streamer, cl-streamer/encoder, cl-streamer/harmony
- Architecture document outlining Icecast/Liquidsoap replacement
- Core streaming server with ICY metadata protocol support
- Thread-safe ring buffer for audio data
- Mount point abstraction with metadata updates
- Multi-client connection handling
This is experimental groundwork for integrating with Harmony/cl-mixed
and playlisp/parsector for a pure CL streaming solution.
API-ENDPOINTS.org:
- Add documentation for all 90+ API endpoints
- Stream Queue, Scheduler, Track Requests, User Playlists
- Favorites, History, Profile, Account endpoints
- Frontend Partials, Utility endpoints
- Now covers 100% of define-api endpoints in codebase
PROJECT-OVERVIEW.org:
- Tidy up tables and formatting
The r-simple-rate library has a bug where rate limit counters can go
negative and never reset. This happens because the reset condition
only triggers when amount >= 0, so negative amounts are permanently
stuck.
This fix adds:
- cleanup-corrupted-rate-limits function to delete corrupted entries
- db:connected trigger to run cleanup automatically on startup
This prevents the 429 error loops that occurred when counters became
corrupted with large negative values.
Fix 429 errors caused by aggressive rate limiting on now-playing APIs.
Changed from 2-3 req/sec to 60 req/min for:
- asteroid/partial/now-playing
- asteroid/partial/now-playing-inline
- asteroid/partial/now-playing-json
- asteroid/channel-name
This fixes notifications not working and may resolve auto-reconnect issues.
- Implement Web Notifications API in ParenScript
- Add notification toggle button (🔔/🔕) to player frame
- Show 'Artist - Track' notification when track changes
- Store notification preference in localStorage
- Auto-close notifications after 5 seconds
- Click notification to focus browser window
- Add dropdown to admin template to choose sort order
- Update get-geo-stats to accept order-by parameter
- Update API endpoint to pass sort-by parameter
- Update ParenScript to read dropdown and pass to API
- Default sort is by minutes (engagement time)
- Add register-web-listener to now-playing-json API endpoint
This keeps listeners registered during continuous playback instead of
timing out after 5 minutes of inactivity
- Fix listen_minutes calculation to increment by listener_count per poll
Previously incremented by 1 regardless of how many listeners, now properly
tracks listener-minutes (1 minute per listener per 60s poll interval)
- Add migration 009 documenting the calculation fix
- Remove cross-frame audio access that was breaking audio on navigation
- Add mini spectrum visualizer to persistent player frame
- Mini analyzer syncs theme and style with main analyzer via localStorage
- Add MusicBrainz search link to player frame (updates with track changes)
- Reduce quality selector width from 140px to 55px
- Add search_url to now-playing-json API response
The main analyzer is disabled in frameset mode due to browser restrictions
on cross-frame MediaElementSource, but the mini analyzer in the player
frame provides visualization that persists across content navigation.
- Reset *is-reconnecting* flag before play attempt so error handler can
catch failures and trigger retry with exponential backoff
- Always reconnect on stall event - ready-state check was unreliable
- Add retry logic with backoff when reconnect fails (up to 5 attempts)
- Store stalled timeout reference for proper cancellation
Fixes issue where stream would show 'Stream stalled - reconnecting...'
but never actually reconnect, requiring manual intervention.
- Rename icecast.xml.base -> icecast-base.xml for editor highlighting
- Rename icecast-yp.xml.snippet -> icecast-yp-snippet.xml for editor highlighting
- Add STATION_URL env var to Liquidsoap for YP directory station link
- Production sets STATION_URL=https://asteroid.radio
- Update docker-compose.yml to pass both ICECAST_HOSTNAME and STATION_URL
- Update environment.sh.template with STATION_URL documentation
- Add hostname substitution in icecast-entrypoint.sh
- Update environment.sh.template with ICECAST_HOSTNAME option
- Defaults to localhost for dev, production sets ice.asteroid.radio
- Fixes YP directories showing localhost:8000 as stream URL
- Rename format-timestamp-for-postgres to format-timestamp-iso8601 (database-agnostic)
- Reuse format-timestamp-iso8601 in authenticate-user instead of inline formatting
- Change listened_at field type from integer to timestamp for consistency
- Update all references from listening_history to user_listening_history
- Use correct column names: user_id, listened_at, duration_seconds, track_artist
- Update database.lisp collection definition to match PostgreSQL schema