Commit Graph

263 Commits

Author SHA1 Message Date
Glenn Thompson 61d3e490da Add Credits section with icon attribution
Attribution for sync.png icon from Flaticon (meaicon)
2025-12-12 08:57:07 -05:00
Glenn Thompson 86536a2f22 Replace reconnect button emoji with sync icon
- Add static/icons/sync.png (24x24px)
- Use CSS filter to make icon green to match player theme
2025-12-12 08:57:07 -05:00
Glenn Thompson 34a6d94324 Refactor geo stats to use real IPs from web requests
Instead of relying on Icecast's listener IPs (which show proxy IPs
behind HAProxy), capture real client IPs from X-Forwarded-For header
when users visit the front page or audio player frame.

Radiance automatically extracts X-Forwarded-For into (remote *request*).

Changes:
- Add *web-listeners* hash table to track visitors with real IPs
- Add register-web-listener to capture IPs during page requests
- Add collect-geo-stats-from-web-listeners for polling
- Call register-web-listener from front-page and audio-player-frame
- Filter out private/internal IPs (172.x, 192.168.x, 10.x, 127.x)
- Remove session requirement - use IP hash as key for anonymous visitors
2025-12-12 08:57:07 -05:00
Brian O'Reilly ff17490b35 Don't track site specific configuration in the repository. 2025-12-10 15:06:50 -05:00
Brian O'Reilly afa9f2e172 increase the polling interval for #'update-stream-information 2025-12-10 14:57:54 -05:00
Glenn Thompson 0748466811 Fix GeoIP lookup: convert drakma byte response to string
Drakma returns a byte vector, not a string. The code was passing
this directly to cl-json:decode-json-from-string which expects
a string, causing the lookup to silently fail.
2025-12-10 12:38:21 -05:00
Glenn Thompson f73d0ef007 Remove duplicate asteroid/recently-played API definition 2025-12-10 11:11:32 -05:00
Glenn Thompson 6fac97b6e1 Fix ParenScript constructor syntax: use ps:new for class instantiation
Change (new -ClassName) to (ps:new (-ClassName)) for proper ParenScript
macro expansion. Fixes Event and FormData constructors.

Co-authored-by: @easilok
2025-12-10 11:11:32 -05:00
Glenn Thompson 68a83390c9 Use local-time:now for last-login update (database agnostic)
Replace raw PostgreSQL SQL with data-model approach using local-time:now
as suggested by easilok. This keeps the code database backend agnostic
instead of being tied to PostgreSQL-specific syntax.
2025-12-10 11:11:32 -05:00
Glenn Thompson c1d71800ab Disable auto-generation of stream-queue.m3u from all tracks
- Comment out fallback that dumped entire library to stream-queue.m3u
- Add stream-queue.m3u to .gitignore (generated file with local paths)
- Delete orphaned stream-queue.m3u from project root

The curated playlists/stream-queue.m3u for Liquidsoap should be
manually managed, not auto-generated by the application.
2025-12-10 11:11:32 -05:00
Glenn Thompson b6c1baa473 Implement playlist management MVP for player page
Features:
- Create, view, load, and delete playlists
- Add tracks to playlists via dropdown menu (📋 button)
- View playlist contents (👁️ button)
- Load playlist to queue with auto-play (📂 button)
- Delete playlist with confirmation (🗑️ button)

Backend changes:
- Move get-db-connection-params to database.lisp for proper load order
- Update playlist functions to use playlist_tracks junction table
- Add get-playlist-tracks and get-playlist-track-count functions
- Add delete and remove-track API endpoints
- Fix stream-track endpoint to not wrap errors in JSON

Frontend changes (ParenScript):
- Add playlist display with action buttons
- Add showAddToPlaylistMenu dropdown
- Add deletePlaylist and viewPlaylist functions
- Fix forEach chaining with progn
- Fix let* scoping for sequential bindings
- Fix ps:regex syntax for string escaping
- Add console logging for playlist debugging
2025-12-10 11:11:32 -05:00
Glenn Thompson 135a6a8dee Apply easilok's ParenScript fixes and fix playlist creation
ParenScript player.lisp fixes (from easilok):
- Replace == with = for JavaScript equality
- Replace inner-html with inner-h-t-m-l
- Replace parseInt with parse-int
- Replace let with let* where needed for sequential bindings
- Replace new "FormData" with new -Form-data
- Fix audio event listener chaining
- Replace Promise chains with setTimeout callbacks

playlist-management.lisp:
- Remove track-ids field (column doesn't exist, using junction table)
2025-12-10 11:11:32 -05:00
Glenn Thompson 46d57e2775 Fix timestamp consistency across all tables
- Update USERS table schema to use TIMESTAMP instead of integer
- Add migration 003 to convert existing integer timestamps to TIMESTAMP
- Remove timestamp-to-unix calls in playlist-management.lisp (use DB default)
- Update user-management.lisp to use postmodern:execute for last-login update
- All timestamp columns now consistently use PostgreSQL TIMESTAMP type
2025-12-10 11:11:32 -05:00
Glenn Thompson 22b2a2d87e Add Liquidsoap/Icecast controls, fix library scan
- Add Liquidsoap control panel: status display, skip track, reload playlist, restart container
- Add Icecast restart button to System Status section
- Remove redundant Web Player Control section from admin
- Fix music library scan to follow symlinks (truename resolution)
- Fix database timestamp error (let PostgreSQL default handle added-date)
- Update docker-compose mount for stream-queue.m3u
- Clean up playlist path handling
2025-12-10 11:11:32 -05:00
Glenn Thompson 74a9448e9a Fix player.js bugs and update admin panel
- Remove Live Stream Monitor section (redundant with frame player)
- Fix player.lisp pagination start-index calculation
- Fix track property access (remove erroneous index 0)
- Fix if/else paren structure in play-next function
- Fix indentation in play-track function
- Update Music Library Management section to reflect Docker setup
- Add geostationary playlist file
2025-12-10 11:11:32 -05:00
Glenn Thompson c89e31b998 Add geo stats collection and improve admin dashboard UI
- Add geo IP lookup for listener locations (ip-api.com)
- Add geo stats table with country flags to admin dashboard
- Fix listener stats table alignment with proper centering
- Fix Now Playing display to update without requiring audio playback
- Add caching for geo lookups to reduce API calls
2025-12-10 11:11:32 -05:00
Glenn Thompson 4be3b83da1 Add listener statistics feature
- Add database schema for listener snapshots, sessions, and aggregates
- Implement background polling of Icecast admin XML stats
- Add API endpoints for current, daily, and geo stats
- Add listener stats section to admin dashboard with auto-refresh
- GDPR compliant: IP hashing, data retention cleanup
2025-12-10 11:11:32 -05:00
Glenn Thompson 63c32c25f3 Add listener statistics feature design document 2025-12-10 11:11:32 -05:00
Glenn Thompson 51b40fe8df Add status page for frameset mode and fix navigation 2025-12-10 11:11:32 -05:00
Glenn Thompson 8c19e0fbde Fix wedged player with reconnect button and volume preservation
- Add reconnect button (🔄) to frameset player bar
- Recreate audio element on reconnect to fix wedged MediaElementSource
- Properly close and reinitialize AudioContext for spectrum analyzer
- Preserve volume and muted state when reconnecting
- Show status messages for connection issues
- Reduce Now Playing update interval to 5 seconds
- Add front-page.js to player-content.ctml for Now Playing updates
- Create About pages (about.ctml and about-content.ctml)
- Add About link to navigation in both modes
2025-12-10 11:11:32 -05:00
Brian O'Reilly 8fd0b06b69 dump users in db to csv, and restore them from same
this is (hopefully) a write once run once utility, but we're moving
from sqlite to postgres, and I don't want to lose the users that have
already signed up for the site.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 4ca6570d5e correct the database creation script for the expected fields. 2025-12-07 19:44:04 -05:00
Brian O'Reilly c9f6cb2aa7 ... And then we ask: How did this *ever* work??
(It did. On my machine.)
2025-12-07 19:44:04 -05:00
Brian O'Reilly 6c6732d1e0 as far as I'm concerned, this is version 1.
Bump the major version number in the asdf system definition file, for
great ceremony.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 8754e7261f pull creds from the enclosing environment
When we stand up, we establish the configuration for database
authentication in the postgres docker container. Put these credentials
into environment variables for production, and default to known values
for development.
2025-12-07 19:44:04 -05:00
Brian O'Reilly e55210c332 Source database auth creds from the environment
Defaults to known values for development.
source the environment credentials so they're available at container build.
2025-12-07 19:44:04 -05:00
Brian O'Reilly d66b7b8053 this effectively reverts the previous case change of the USERS table name
I guess for now we'll leave this quirk in
2025-12-07 19:44:04 -05:00
Brian O'Reilly 44096dfb4b move m3u files into dedicated directory.
These playlist files are useful only to the installation at
https://asteroid.radio, because they reference tracks that likely do
not exist in any other library where this code might be run.
Regardless, it's useful to me, and saving this work somewhere durable
has intrinsic value to me, operationally. If nothing else, this shows
the structure of a plalist for use with this code.
2025-12-07 19:44:04 -05:00
Brian O'Reilly 9cbc0a7780 the db is connected, so init the users. 2025-12-07 19:44:04 -05:00
Brian O'Reilly 7a4e9c208a creating the users table all caps style gives us duplicates in postgres. 2025-12-07 19:44:04 -05:00
Luis Pereira aa84ff4470 fix: improve live stream text proportions
Conflicts:
	static/asteroid.css
2025-12-07 19:44:04 -05:00
Brian O'Reilly 9bb31ec88c many things, almost working, but not quite. 2025-12-07 19:44:04 -05:00
Brian O'Reilly 6c2ed75b15 include i-postgmodern interface for postgres.
Conflicts:
	asteroid.asd
2025-12-07 19:44:04 -05:00
Brian O'Reilly d2508451d0 Delete the music/library directory which held an erroneous path to a softlink that doesn't exist in prod. 2025-12-07 19:44:04 -05:00
Glenn Thompson efe993e0c1 Fix HTML entity decoding in now playing titles
- Use plump:decode-entities to decode HTML entities (&, ", etc.) in track titles from Icecast XML
- Fixes display of artist names and track titles containing ampersands and other special characters
- One-line fix using existing plump library dependency
2025-12-06 11:55:24 -05:00
Glenn Thompson 2fa03e447a Update dropdown colors to match spectrum analyzer theme
- Dropdown boxes now dynamically update their text and border colors to match selected theme
- Prevents color clashing between UI elements and spectrum analyzer
- Colors update both on theme change and on page load
2025-12-06 11:55:24 -05:00
Glenn Thompson c08b5f73de Add monotone theme and dynamic border color
- Add monotone theme with deep blue to cobalt gradient
- Update canvas border color dynamically to match selected theme
- Set initial border color on page load based on saved preference
2025-12-06 11:55:24 -05:00
Glenn Thompson 16a69dc6e9 Rename recently-played.js to recently-played.js.original
Match naming convention of other original JS files now replaced by parenscript
2025-12-06 11:55:24 -05:00
Glenn Thompson 51387bddba Add spectrum analyzer theming and visualization styles
- Add 6 color themes: green, blue, purple, red, amber, rainbow
- Add 3 visualization styles: bars, wave, dots
- Add UI controls (dropdowns) to change theme and style
- Persist user preferences in localStorage
- Remove debug logging from parenscript serving and Icecast stats
- Fix dots visualization with proper Math.PI handling
2025-12-06 11:55:24 -05:00
Glenn Thompson b6be0ebab1 feat: Convert JavaScript to Parenscript with stream fixes and UX improvements
Major Changes:
- Convert all JavaScript files to Parenscript for better maintainability
- Move spectrum-analyzer to parenscript/ directory structure
- Add parenscript-utils.lisp for shared utilities
- Convert admin.js, player.js, front-page.js, auth-ui.js to Parenscript
- Convert profile.js, users.js, recently-played.js to Parenscript

Stream Reconnect Fixes (from merged PR):
- Add reset-spectrum-analyzer function to properly clean up Web Audio API
- Implement reconnect logic for pauses longer than 10 seconds
- Detect stale audio in 'playing' event and force stream reconnection
- Prevent 'Now Playing' updates while stream is paused
- Reduce reconnect delay to 200ms for faster response
- Add proper spectrum analyzer reset/reinit during reconnection

UX Improvements:
- Change live indicator from blink to smooth pulse (2s ease-in-out)
- Pulse animation like old PowerBook/MacBook sleep indicator
- Add MUTED indicator to spectrum analyzer when audio is muted
- Spectrum continues to flow even when muted (data still streaming)
- Red 'MUTED' text displayed in top-right corner of canvas

Technical Details:
- Parenscript files generate JavaScript via API endpoints
- All player modes updated: main player, front page, popout, frame player
- Improved audio context handling to only create once per element
- Added comprehensive error handling and logging
- Updated asteroid.asd to include parenscript module structure

Documentation:
- Updated all documentation dates to 2025-12-06
- Added PARENSCRIPT-EXPERIMENT.org documenting the conversion
- Updated PROJECT-HISTORY.org with Phase 9 (Visual Audio Features)
- Added comprehensive project statistics (408 commits, 9,300 LOC)

This conversion improves code maintainability by using Lisp throughout
the stack and makes it easier to share code between frontend and backend.
2025-12-06 11:55:24 -05:00
Brian O'Reilly f68c85f0cd asteroid.css is generated on every start. delete it from repository. 2025-12-06 10:25:26 -05:00
Glenn Thompson 4ec90c0f27 perf: Reduce reconnect delay from 500ms to 200ms for faster response
Optimized the timeout after stream reconnection to make the pause/unpause
experience more responsive. The 200ms delay is sufficient for the browser
to clear buffers and start the fresh stream while minimizing perceived lag.
2025-12-06 08:36:30 -05:00
Glenn Thompson 7c7b2c921e fix: Prevent stale audio playback after long pause and reorganize spectrum analyzer
- Detect pauses longer than 10 seconds across all player modes
- Intercept 'playing' event to stop stale buffered audio
- Force stream reconnect to get live audio after long pause
- Reset and reinitialize spectrum analyzer during reconnect
- Prevent 'Now Playing' updates while stream is paused
- Move spectrum-analyzer.lisp to parenscript/ directory
- Update all documentation dates to 2025-12-06
- Add comprehensive project statistics (408 commits, 9,300 LOC)
- Add Phase 9 (Visual Audio Features) to project history

Fixes issue where resuming playback after a long pause would play
old buffered audio instead of the current live stream. The fix uses
the 'playing' event to detect when stale audio starts and immediately
stops it, then reconnects to get fresh stream data.

All player modes updated: main player, front page, popout, and frame player.
2025-12-06 08:36:30 -05:00
Glenn Thompson 6e8260172f fix: Reduce Icecast burst size and prevent now-playing updates during pause
- Reduced Icecast burst-size from 64KB to 8KB to minimize buffer accumulation
- Fixed spectrum analyzer to only create MediaElementSource once
- Added resetSpectrumAnalyzer() function to allow reconnection
- Prevent now-playing info updates when stream is paused across all players:
  * Player page
  * Front page
  * Pop-out player
  * Frame player
  * Admin page
- After pause >10s, reconnect stream and reinitialize spectrum analyzer
- Preserves spectrum analyzer functionality after pause/unpause
- Eliminates stuttering and buffer accumulation issues
2025-12-06 08:36:30 -05:00
Glenn Thompson 924f6498de Add real-time spectrum analyzer using Parenscript
- Implement Web Audio API-based spectrum analyzer with green gradient bars
- Add spectrum analyzer to front page and persistent player content frames
- Support cross-frame audio element access for frameset mode
- Add CORS headers to Icecast configuration for Web Audio API
- Serve Parenscript-generated JavaScript dynamically via API endpoint
- Canvas visualization appears below page title, above navigation
2025-12-03 19:00:13 -05:00
Glenn Thompson 090e8e9898 fix: Serve spectrum analyzer JavaScript dynamically via API
Changed from static file generation to Radiance API endpoint.
Parenscript now generates JavaScript in-memory at runtime when
the /api/asteroid/spectrum-analyzer.js endpoint is requested.

This is the correct approach for Parenscript integration with Radiance.
2025-12-03 19:00:13 -05:00
Glenn Thompson a1257af16f feat: Add spectrum analyzer using Parenscript
Implements real-time audio visualization for the main page:
- Added Parenscript dependency to asteroid.asd
- Created spectrum-analyzer.lisp with Parenscript code
- Added canvas element to front-page.ctml above nav buttons
- Auto-generates JavaScript at compile time
- Green gradient bars matching Asteroid aesthetic
- Uses Web Audio API for FFT analysis

This is the first Parenscript component in Asteroid Radio,
demonstrating the approach for future JavaScript conversions.
2025-12-03 19:00:13 -05:00
Glenn Thompson 280b8f0690 fix: Use 'normal' mode instead of 'sequential' for playlist playback
The 'sequential' mode in Liquidsoap starts playback at a random position
in the playlist, causing tracks to play out of order. Switching to 'normal'
mode ensures the playlist starts from the beginning and plays sequentially
through all tracks in order.
2025-12-03 19:00:13 -05:00
Glenn Thompson c668a5f40f Add phase metadata to stream-queue.m3u 2025-12-03 19:00:13 -05:00
Luis Pereira 4c534a0a4f feat: add blinking live cursor 2025-11-23 18:43:45 -05:00