Commit Graph

36 Commits

Author SHA1 Message Date
Glenn Thompson 4b49f63991 fix: Normalize USERS table timestamps before dm:save to prevent PostgreSQL type errors
- Add format-timestamp-for-postgres and normalize-user-timestamps functions in database.lisp
- data-model-save now normalizes timestamp fields for USERS table before saving
- Fix authenticate-user to use data-model-save instead of dm:save directly
- Add global auth state in auth-ui.lisp for other scripts to check login status
- Add auth checks in front-page.lisp and stream-player.lisp to skip API calls when not logged in
- Fix ParenScript parentheses structure in stream-player.lisp DOMContentLoaded handler

Fixes password reset, role update, and last-login update failures caused by
integer timestamps being sent to PostgreSQL TIMESTAMP columns.
2025-12-29 09:39:51 -05:00
glenneth ccce10db50 feat: Sync favorites between front page and frame player via postMessage 2025-12-28 09:58:33 -05:00
glenneth 116d9ceebf fix: Favorites star UI and track-id lookup
- Fix find-track-by-title to parse 'Artist - Title' format from Icecast
  and search both artist and title columns in tracks table
- Fix favorites API alist key mismatch (TRACK-TITLE not TRACK_TITLE)
- Fix favorites cache to update UI after loading
- Fix race condition where star reverted after clicking
- Add aget-profile helper for Postmodern uppercase key lookup
2025-12-27 20:06:37 +03:00
glenneth 02e585bd5b feat: Custom user playlists with submission and admin review
- Add user playlist creation, editing, and track management
- Add library browser for adding tracks to playlists
- Add playlist submission workflow for station airing
- Add admin review interface with preview, approve, reject
- Generate M3U files on approval in playlists/user-submissions/
- Include user-submissions in playlist scheduler dropdown
- Use playlist description as PHASE tag in M3U
- Add database migration for user_playlists table
- Update TODO-next-features.org to mark feature complete
2025-12-21 18:45:35 +03:00
glenneth 2c49092c01 refactor: Remove Recently Played section from profile page
Removed the Recently Played UI section from profile as redundant.
The listening history backend and APIs remain intact for future use.
Previous commit (0359e59) preserves the full implementation.
2025-12-21 12:53:10 +03:00
glenneth 0359e5909a feat: Track requests, listening history, and profile enhancements
Track Requests:
- Database table for user track requests (migration 007)
- API endpoints for submit, approve, reject, play
- Front page UI for submitting requests
- Shows recently played requests section

Listening History:
- Auto-records tracks when playing (with 60s deduplication)
- Recently Played section on profile (has date formatting issues)
- Activity chart showing listening patterns by day
- Load More Tracks pagination

Profile Improvements:
- Fixed 401 errors returning proper JSON
- Fixed PostgreSQL boolean type for completed column
- Added offset parameter to recent-tracks API

Note: Recently Played section has date formatting issues showing
'20397 days ago' - may be removed in future commit if not needed.
The listening history backend works correctly.

For production: run migrations/007-track-requests.sql
2025-12-21 12:45:49 +03:00
glenneth 8f5fe7534d feat: Add avatar upload and fix authentication errors
Avatars:
- Add avatar_path column to USERS table (migration 006)
- Upload API endpoint /api/asteroid/user/avatar/upload
- Profile page shows avatar with hover-to-change overlay
- Default SVG avatar for users without uploaded image
- Avatars stored in static/avatars/ directory

Fixes:
- 401 errors now return proper JSON instead of 500
- SQL escaping for history recording (single quotes)
- Added debug logging for history/record API
- Avatar container has background color for visibility

For production: run migrations/006-user-avatars.sql
2025-12-21 09:07:56 +03:00
glenneth a2ebc415f2 feat: Add listening activity chart to profile page
- New API endpoint /api/asteroid/user/activity for daily aggregation
- Bar chart showing tracks played per day (last 30 days)
- Hover tooltips show exact date and count
- Total tracks summary below chart
- Green gradient bars matching site theme
2025-12-21 08:43:36 +03:00
glenneth 7600ea6bed feat: Add listening history tracking and fix favorites
Listening History:
- Auto-record tracks when they change (logged-in users only)
- Track stored by title (no tracks table dependency)
- Profile page shows real recent tracks, top artists, listening stats
- APIs: /api/asteroid/user/history, /user/listening-stats, /user/recent-tracks, /user/top-artists

Favorites Fixes:
- Remove favorite now uses title instead of track-id
- Fixed response parsing to show green success message
- Profile page remove button works correctly

Migration Script Updated:
- track_title column added to both tables
- track-id now optional (nullable)
- Unique index on (user-id, track_title)
- No foreign key to tracks table (title-based storage)

For production: run migrations/005-user-favorites-history.sql
2025-12-21 08:35:35 +03:00
glenneth 5225a07b8b feat: Add track favorites feature with star button
- Add user_favorites and listening_history database tables
- Add migration 005-user-favorites-history.sql
- Create user-profile.lisp with favorites/history API endpoints
- Add star button (☆/★) to Now Playing on main page
- Add star button to frame player bar
- Add Favorites section to profile page
- Show login prompt when unauthenticated user clicks star
- Use gold color (#ffcc00) for favorited state (space theme)
- Fix require-authentication to properly detect API routes
- Support title-based favorites (no track DB required)
2025-12-21 08:15:52 +03:00
glenneth 349fa31d8f Fix last-login timezone handling
- Add cl-time-to-unix helper to convert CL universal time to Unix epoch
- Store last-login as UTC time for correct timezone conversion
- Handle Unix epoch in JavaScript (detect seconds vs milliseconds)
2025-12-19 18:00:30 -05:00
glenneth bc7da82d84 Fix playlist display polling and add footer links
- Add /api/asteroid/channel-name endpoint for live channel name updates
- Update front-page.js and stream-player.js to poll server for channel name
  instead of relying on localStorage (fixes issue where listeners don't see
  playlist changes until page refresh)
- Add footer with Internet Radio directory link and craftering webring links
- Fix last-login display bug: use proper ISO 8601 timestamp format and
  parse as Date string instead of Unix epoch
2025-12-19 18:00:30 -05:00
glenneth b415ca9530 feat: Add database persistence and editable UI for playlist schedule
- Store schedule in PostgreSQL (playlist_schedule table)
- Load schedule from database on startup
- Admin UI: add/update schedule entries with hour and playlist dropdowns
- Admin UI: delete buttons for each schedule entry
- Available playlists populated from playlists directory
- Changes persist across server restarts
2025-12-17 20:48:07 -05:00
glenneth 6a80072075 feat: Add admin UI for playlist scheduler with server time display
- Add server time info (UTC) to scheduler status API
- Add scheduler section to admin.ctml with:
  - Server time display (UTC)
  - Current playlist indicator
  - Enable/Disable/Load Current buttons
  - Schedule table showing all time slots
- Add ParenScript functions for scheduler controls
- Auto-refresh scheduler status every 30 seconds
- Highlight active playlist in schedule table
2025-12-17 20:48:07 -05:00
Glenn Thompson cb76c02e63 Remove debug logging from channel name update code 2025-12-14 19:08:41 -05:00
Glenn Thompson 987d01beaa Dynamic channel name updates and playlist crossfade transition
- Fix channel name not updating in frame player when playlist changes
- Use localStorage polling (2s interval) for cross-frame communication
- Fix API response access: use bracket notation for 'channel-name' property
- Add skip command after playlist load to trigger crossfade to new playlist
- Add #PHASE metadata to Asteroid-Low-Orbit.m3u playlist
2025-12-14 19:08:41 -05:00
Glenn Thompson 18c251c8c4 Fix toggleCountryCities bug - use let* for sequential binding
The arrow variable was referencing country-row before it was defined
because let binds all variables simultaneously. Changed to let* for
sequential binding so country-row is available when binding arrow.
2025-12-14 19:08:41 -05:00
Glenn Thompson 93140f8f24 Add channel/quality selector separation with dynamic playlist phase names
- Separate Channel selector (Curated/Shuffle) from Quality selector (bitrate)
- Add channel selector to frame player, front page, and popout player
- Dynamic curated channel name from playlist #PHASE: metadata
- Channel selection syncs across all player contexts via localStorage
- Quality selector disabled when Shuffle channel selected (fixed bitrate)
- Fix reconnectStream to use channel-aware config
- Consistent CSS styling for selector heights
2025-12-14 19:08:41 -05:00
Glenn Thompson 55d63770d2 Add shuffle mount to admin dashboard listener stats
- Add Shuffle column to Current Listeners table
- Display listener count for /asteroid-shuffle.mp3 mount
- Include shuffle listeners in total count
2025-12-14 15:43:29 -05:00
Glenn Thompson 4f1a60328b Add shuffle stream mount with separate recently-played tracking
- Add shuffle source to Liquidsoap config (96kbps MP3)
- Add shuffle option to all UI quality selectors (frame, popout, front page)
- Make now-playing APIs mount-aware for correct metadata display
- Implement separate recently-played lists for curated vs shuffle streams
- Speed up now-playing and recently-played refresh on stream change
- Fix clean shutdown of stats polling thread (positional timeout arg)
- Widen quality selector dropdown for shuffle label
2025-12-14 14:02:01 -05:00
Glenn Thompson 1467df7d14 Fix geo stats peak tracking and migrate inline JS to ParenScript
- Fix listener_count accumulation bug: use GREATEST() instead of + to track
  peak concurrent listeners per day rather than cumulative count
- Migrate all inline JavaScript from templates to ParenScript:
  - admin.ctml: listener stats, geo stats, password reset -> admin.lisp
  - audio-player-frame.ctml: stream player logic -> stream-player.lisp (new)
  - popout-player.ctml: popout player logic -> stream-player.lisp (shared)
  - frameset-wrapper.ctml: frame-busting -> frameset-utils.lisp (new)
  - profile.ctml: removed redundant inline init (already in profile.lisp)
- Fix API route detection: handle URIs with or without leading slash
- Add routes to serve new ParenScript-generated JS files
- Update ASDF system definition with new ParenScript components

Tested locally for 8+ hours with no issues.
2025-12-14 10:20:32 -05:00
Glenn Thompson b29e504bb3 Add pause event handler for muted stream throttling
- Detect when browser pauses muted stream (throttling)
- Auto-reconnect after 3 seconds when paused while muted
- Apply to all three players for consistency
2025-12-12 13:55:55 -05:00
Glenn Thompson 2cd128260c Add robust auto-reconnect to all audio players
- Implement isReconnecting flag to prevent duplicate reconnect attempts
- Add exponential backoff for error retries (3s, 6s, 12s, max 30s)
- Retry indefinitely until stream returns
- Handle error, stalled, and ended events consistently
- Reset state on successful playback
- Apply same logic to frame player, popout player, and front-page player
2025-12-12 13:55:55 -05:00
Brian O'Reilly afa9f2e172 increase the polling interval for #'update-stream-information 2025-12-10 14:57:54 -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 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 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 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
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 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
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