Commit Graph

410 Commits

Author SHA1 Message Date
Brian O'Reilly 4f7b63e1b9 docker port maps leak to external interface...
Unless they are explicitly bound to loopback, which I thought was the
default, but it is not. likely related to the interface between
bridges and ip tables in the Linux kernel, but anyhow, get literal
about the portmap interface address to prevent exposing the database
to the entire internet. With thanks to the friendly heads up email
from the German Federal Republic via Hetzner.
2026-03-14 06:52:09 +00:00
Glenn Thompson 5d7606fb3b Bump now-playing-json rate limit 30→120/min + update cl-streamer submodule
- frontend-partials.lisp: increase rate limit on now-playing-json from
  30 to 120 requests/minute to prevent 429 errors from concurrent
  frontend pollers (player + now-playing + popout)
- cl-streamer: catch serious-condition in play-list for native code
  crash recovery (23b5ee1)
2026-03-09 09:25:20 +00:00
Glenn Thompson 2a713d7386 Fix SBCL bracket-escaping in shuffle library scanner
stream-harmony.lisp:
- scan-music-library-files: use native-namestring instead of namestring
  when collecting file paths, producing real OS paths without SBCL's
  backslash escaping of brackets (e.g. [FLAC] not \[FLAC])
- Use parse-native-namestring for root directory entry point so dirs
  with brackets are not treated as wildcard patterns

cl-streamer submodule updated to d57a268:
- play-file, read-audio-metadata, play-list retry: same native-namestring
  fix to prevent double-escaping through parse-native-namestring

Fixes FLAC FILE slot unbound errors and SIGSEGV memory fault that
crashed the shuffle pipeline after consecutive failures on bracket paths.
2026-03-08 22:02:46 +03:00
Glenn Thompson 04a874ceb5 Update CL-STREAMER-STANDALONE.org: mark all phases done, add DSL rationale
- Phases 1-4 marked DONE with commit refs
- Step 1 (eliminate *server* global) and Step 2 (shuffle pipeline) documented
- Checklist items marked [X] for completed work
- Added design rationale: why keyword args over quoted-plist DSL
  (idiomatic, compile-time checking, no parser, composability, extensibility)
- Updated architecture diagram to show current state
- Remaining: integration test, source protocol, VPS deployment
2026-03-08 13:41:35 +03:00
Glenn Thompson 5efa49321e Add shuffle stream as second pipeline
stream-harmony.lisp:
- scan-music-library-files: recursive directory scanner for supported formats
- Cached shuffle library pool (2797 tracks, 1hr TTL refresh)
- shuffle-random-batch: picks N random tracks from pool
- refill-shuffle-queue: track-change hook keeps queue topped up
- on-shuffle-track-change: updates recently-played, refills queue
- shuffle-now-playing: now-playing stats for shuffle mounts
- start-shuffle-streaming: creates pipeline sharing curated server,
  registers hooks, seeds queue, starts play-list with crossfade
- stop-shuffle-streaming: stops pipeline without stopping shared server

asteroid.lisp:
- Start shuffle pipeline after curated pipeline on startup
- Stop/restart shuffle in stream restart endpoint
- Fix stale cl-streamer:get-listener-count call (needs server arg)

frontend-partials.lisp:
- Route shuffle mounts to shuffle-now-playing
- Fix stale cl-streamer:get-listener-count (use pipeline-listener-count)

listener-stats.lisp:
- Poll shuffle mounts (/shuffle.mp3, /shuffle.aac) for listener stats

parenscript/stream-player.lisp:
- get-stream-config returns /shuffle.* mounts when channel is shuffle

parenscript/front-page.lisp, parenscript/player.lisp:
- Normalize now-playing polling to 15s (was 5s/10s, caused 429s)

Build verified, runtime tested: both pipelines play simultaneously,
channel selector switches streams correctly.
2026-03-08 13:32:51 +03:00
Glenn Thompson 3e6b496340 Eliminate *server* global: update dj-session + stream-harmony to protocol generics
dj-session.lisp:
- Replace pipeline-harmony-server + harmony:*server* binding with
  pipeline-play-voice / pipeline-stop-voice protocol generics
- Replace volume-ramp with pipeline-volume-ramp
- Replace read-audio-metadata / format-display-title with
  pipeline-read-metadata / pipeline-format-title
- Replace update-all-mounts-metadata with pipeline-update-metadata
- pipeline-stop-all-voices now a protocol generic (was defun)

stream-harmony.lisp:
- Replace cl-streamer:get-listener-count (global-dependent) with
  cl-streamer:pipeline-listener-count (pipeline-scoped)

Build verified.
2026-03-08 12:47:05 +03:00
Glenn Thompson 8d9d2b33b1 Phase 4: Extract cl-streamer to standalone repository
Replace in-tree cl-streamer/ with git submodule pointing to
glenneth1/cl-streamer (https://github.com/glenneth1/cl-streamer).

ASDF discovers cl-streamer via source-registry :tree scan — no
config changes needed. Submodule tracks master branch.

Build verified: all cl-streamer systems load from submodule.
2026-03-08 12:08:09 +03:00
Glenn Thompson 2e86dc4a88 Phase 3: Refactor stream-server.lisp to iolib
- Replace usocket with iolib for socket I/O
- iolib:make-socket with :connect :passive for listener
- iolib:accept-connection for client connections
- Add SO_KEEPALIVE for stale connection detection
- Add TCP_NODELAY for low-latency streaming
- Add SO_SNDTIMEO (30s) write timeout for stale client detection
- Handle iolib:socket-connection-reset-error and isys:ewouldblock
- Update cl-streamer.asd: usocket→iolib, drop chunga/trivial-gray-streams
- Fix now-playing poll interval 5s→15s to eliminate 429 rate limit errors

Runtime verified: audio streams, metadata displays, clients connect.
2026-03-08 11:33:55 +03:00
Glenn Thompson b7266e3ac2 Phase 2: Clean package boundaries — declarative make-pipeline DSL
- New make-pipeline function: single declarative call creates server,
  mounts, encoders, and pipeline wiring from an output spec
- Pipeline owns encoder lifecycle (pipeline-encoders slot, auto-cleanup)
- Pipeline owns server when it creates one (pipeline-owns-server-p)
- Hook system wired: pipeline-add-hook fires on track-change and
  playlist-change via pipeline-fire-hook
- stream-harmony.lisp slimmed: start is 1 make-pipeline + 2 hooks,
  stop is 1 pipeline-stop call (cleanup automatic)
- Removed global encoder variables from Asteroid glue layer
- Backward-compatible: dj-session.lisp unchanged, cl-streamer:*server*
  still set for legacy callers

Runtime verified: audio streams, metadata displays, crossfades work.
2026-03-08 11:23:40 +03:00
Glenn Thompson c79cebcfa7 Phase 1: Define CLOS protocol layer for cl-streamer
- New cl-streamer/protocol.lisp with generic functions for server,
  pipeline, encoder, and hook protocols
- harmony-backend.lisp: convert defuns to defmethod on protocol generics,
  import/re-export protocol symbols, add hook system, backward-compat aliases
- encoder.lisp, aac-encoder.lisp: add encoder-encode/encoder-close methods
- package.lisp: export all protocol symbols
- cl-streamer.asd: add protocol.lisp to components

Runtime verified: audio streams, metadata displays, crossfades work.
2026-03-08 11:09:50 +03:00
Glenn Thompson 37a3b761db Fix API rate limits causing 429 errors on polling endpoints
- asteroid/stats/current: add explicit :limit 120 :timeout 60 (was default 60/60s)
- now-playing, now-playing-inline, now-playing-json: change from :limit 10 :timeout 1
  to :limit 30 :timeout 60 — the 1-second window was too aggressive and likely
  triggering r-simple-rate's negative-amount corruption bug

These endpoints are polled every 5-30s by the player frame, admin dashboard, and
popout player. With multiple tabs/frames sharing a session, the old limits were
easily exceeded, producing 429 responses that cascaded into audio error events.
2026-03-08 10:37:44 +03:00
Glenn Thompson 2aab912b5d Add cl-streamer standalone library refactor plan
Org document detailing the phased approach to extracting cl-streamer
into a standalone, reusable CL audio streaming library:

- Phase 1: Define CLOS protocol (generic functions, protocol classes)
- Phase 2: Clean package boundaries, declarative pipeline DSL
- Phase 3: Refactor stream-server to iolib (per Fade's recommendation)
- Phase 4: Optional separate repository extraction

Aligns with Fade's architecture vision: logically separated stream
daemon with well-defined protocol between application and streamer.
2026-03-07 18:58:21 +03:00
Glenn Thompson f2e60b5648 Fix stream-player.js reconnect loop (play/pause race, stall backoff)
- Keep *is-reconnecting* true until 'playing' event fires, preventing
  pause/stalled handlers from triggering new reconnect cycles mid-flight
- Add exponential backoff for stall retries (5s, 10s, 20s... max 60s)
- Give up auto-reconnect after 10 stall attempts, show manual retry
- Add *stall-count* tracking, reset on successful playback
- Add *user-paused* guard to muted-tab pause handler
- Increase play() delay from 200ms to 500ms after load() for reliability

Fixes: AbortError from play()/pause() race, 429 Too Many Requests from
aggressive reconnect hammering, infinite reconnect loop on muted tabs.
2026-03-07 10:34:43 +03:00
Glenn Thompson bcfda2ebb6 int16 pack encoding + fix playlist resume across schedule changes
- Replace default float packer with int16 packer (mixed:make-packer :encoding :int16)
  cl-mixed now handles float→s16 conversion in optimized C code instead of
  per-sample Lisp loop. Halves pack buffer memory (2 vs 4 bytes/sample).
- Remove float-to-s16 helper (no longer needed)
- Fix resume-from-saved-state: when saved playlist differs from currently
  scheduled playlist, use the scheduled one from the beginning instead of
  continuing the old playlist. Prevents stale playlist playing after restart.
2026-03-06 18:56:10 +03:00
Glenn Thompson df9d939a2f Revert int16 pack encoding (caused static), keep float-to-s16 drain
Setting (mixed:encoding pack) :int16 after server creation did not
change the pack's internal buffer format — data was still written as
float but read as int16, producing garbage audio.

Added TODO comment to investigate correct API for setting pack encoding
at creation time. The float→s16 conversion in Lisp works correctly.
2026-03-06 11:30:53 +03:00
Glenn Thompson f594daabf8 Fix DJ Console: 400 POST error, library search SQL, auto-playlist pause, now-playing override
- Fix api-post: skip Content-Type header on empty-body POSTs (Hunchentoot 400)
- Fix api-post/api-get: unwrap Radiance data wrapper, add try/catch + console logging
- Fix search-library-tracks: use raw SQL with parameterized ILIKE (S-SQL :offset broken)
- Fix search-library-tracks: quote file-path column name for Radiance hyphenated columns
- Add pipeline-stop-all-voices: immediately silence all Harmony voices on mixer
- Fix pause-auto-playlist: clear queue + skip + stop all voices (no more overlap)
- Override get-now-playing-stats during DJ session to show active deck info
2026-03-06 08:06:02 +03:00
Glenn Thompson e712009d79 Implement DJ Console Phase 1 — dual-deck library mixing
New files:
- dj-session.lisp: DJ session state, deck management, crossfader
  (constant-power curve), auto-playlist pause/resume, ICY metadata
  auto-detect, library search, watchdog timer
- parenscript/dj-console.lisp: UI polling (500ms), deck controls,
  crossfader, library search with load-to-deck, session management
- template/dj-console.ctml: Dark hacker-themed dual-deck interface
  with progress bars, transport controls, crossfader slider, metadata
  override, and library search table

Modified files:
- asteroid.lisp: 14 DJ API endpoints (session, deck, crossfader,
  library search), define-page dj-console, dj-console.js serving
- asteroid.asd: Add dj-session and dj-console components
- cl-streamer/harmony-backend.lisp: Export update-all-mounts-metadata,
  volume-ramp, pipeline-harmony-server for DJ deck control
- navbar-admin.ctml: DJ Console link (role-gated to :dj/:admin)

API endpoints all require :dj role. Session lifecycle:
  GO LIVE -> pause auto-playlist -> mix -> END SESSION -> resume

External audio input stubbed for Phase 2.
2026-03-05 21:22:09 +03:00
Glenn Thompson 3ddd86f8ab Add DJ Console design document
Comprehensive design doc for the live DJ mixing feature:
- Dual-deck library mixing with crossfader (constant-power curve)
- External audio input: local sound card (ALSA/Pulse/JACK) and
  network audio (Icecast source protocol for remote DJs)
- Session lifecycle with auto-playlist pause/resume and watchdog
- API endpoints, backend classes, frontend layout
- Phased implementation plan
- Open questions for team discussion
2026-03-05 20:40:42 +03:00
Glenn Thompson ef3e1eab47 Rewrite README.org for current Radiance/Harmony/CL-Streamer architecture
- Document CL-Streamer as our own in-process streaming server
- Remove Icecast/Liquidsoap sections (no longer used)
- Add architecture diagram showing single-process audio pipeline
- Document playlist scheduling, playback state persistence, resume
- Add FDK-AAC shim build instructions
- Update dependencies (Harmony, cl-mixed, CFFI, LAME, FDK-AAC, etc.)
- Add resource usage stats
- Update troubleshooting for common SBCL pathname issues
- Docker now only used for PostgreSQL
2026-03-05 20:22:52 +03:00
Glenn Thompson 770e565027 Fix broken file paths in all 5 scheduler playlists
- underworld-and-friends.m3u: Fix Drexciya (Bubble Chamber mp3, Aqua Worm Hole mp3),
  Model 500 (correct filename 08-model_500-digital_solutions.flac)
- afternoon-orbit.m3u: Fix BoC (dash + mp3), The Orb (mp3 + track numbers),
  Drexciya, Model 500, Vector Lovers (track 11 mp3), Underworld STITI (subdir)
- midnight-ambient.m3u: Fix Bark Psychosis (mp3), Tape Loop Orchestra (NBSP in filename)
- morning-drift.m3u: Fix BoC Kid for Today (dash + mp3)
- evening-descent.m3u: Fix Johann Johannsson (capitalization),
  Tape Loop Orchestra (disc subdir), Brian Eno (curly quote in filename)
2026-03-05 20:13:35 +03:00
Glenn Thompson f39abeb8f8 Fix playlist resume and SIMPLE-ARRAY pathname errors
- Add *resumed-from-saved-state* flag to prevent scheduler's db:connected
  trigger from overwriting resumed playlist position with full playlist
- Use sb-ext:parse-native-namestring in play-file to prevent SBCL from
  interpreting brackets in directory names (e.g. [WEB FLAC]) as wildcard
  patterns, which caused non-simple-string pathname components that broke
  cl-flac's CFFI calls
2026-03-05 18:21:32 +03:00
Glenn Thompson 1807e58971 Remove Icecast/Liquidsoap, migrate fully to Harmony/CL-Streamer
- Delete all Icecast/Liquidsoap config files, Dockerfiles, and .liq scripts
- Remove icecast/liquidsoap services from docker-compose.yml (keep postgres)
- Remove liquidsoap-command, parse-liquidsoap-metadata, format-remaining-time
- Remove liquidsoap-command-succeeded-p, liquidsoap-reload-and-skip
- Remove icecast-now-playing, check-icecast-status, check-liquidsoap-status
- Remove icecast XML polling from listener-stats.lisp
- Replace asteroid/liquidsoap/* APIs with asteroid/stream/* APIs
- Simplify all if-harmony-else-liquidsoap branches to Harmony-only
- Update admin template: single Stream Status card, pipeline controls
- Update about.ctml credits: Harmony + CL-Mixed replace Icecast + Liquidsoap
- Update status.ctml server name to CL-Streamer / Harmony
- Update parenscript/admin.lisp: stream-* functions, new API endpoints
- Export pipeline-running-p from cl-streamer/harmony package
2026-03-05 17:24:12 +03:00
Glenn Thompson 47e6c5da46 Fix scheduler timezone and taglib type errors
- cl-cron uses local time, not UTC: add utc-hour-to-local-hour
  conversion so schedule hours fire at correct UTC times
  (e.g. 12:00 UTC now fires at 15:00 local on UTC+3)

- Wrap each taglib field read in safe-tag with per-field error
  handling so a type error in one field (e.g. album with non-simple
  string) doesn't crash play-file or skip the track

- Use (coerce ... 'simple-string) instead of copy-seq for
  guaranteed simple-string output from ensure-simple-string
2026-03-05 15:16:21 +03:00
Glenn Thompson 9ae7546466 Fix playlist stalls, FLAC/taglib errors, and playback state resume
- Prevent play-list thread death on scheduler playlist change:
  drain-queue-into-remaining drains full scheduler queue at once,
  updates loop-queue reference so repeat replays correct playlist,
  top-level handler-case prevents thread from dying silently

- Fix taglib SIMPLE-ARRAY CHARACTER type errors:
  ensure-simple-string coerces metadata strings and trims whitespace

- Fix FLAC::FILE slot unbound errors:
  retry once with 200ms delay for transient init failures

- Improve playback state persistence:
  save playlist path alongside track file so restart loads the
  correct playlist instead of always falling back to stream-queue.m3u

- Startup now uses resume-from-saved-state to resolve saved playlist
  and track position, falls back to stream-queue.m3u only if no state
2026-03-05 13:25:15 +03:00
Glenn Thompson 16da880822 Fix playlist repeat: loop playlist when tracks exhaust, scheduler takes priority
- Enable loop-queue in play-list: repeats current playlist when tracks run out
- next-entry checks queue first so scheduler-queued playlists override repeat cycle
- Prevents silent stream / client reconnect loops between scheduled playlist changes
2026-03-04 10:20:57 +03:00
Glenn Thompson d66d4fe46c Crossfade timing, scheduler fix, playback resume, auth noise cleanup
- Delay metadata/track-change notification by 1s after crossfade completes
- Log 'Loading next:' instead of 'Now playing:' during crossfade prep
- Add diagnostic logging: track duration check, crossfade trigger time
- harmony-load-playlist defaults to skip=nil: scheduler queues tracks
  without interrupting current playback
- Save current track to .playback-state.lisp on each track change,
  resume from saved position on restart
- Replace ~50 format-t debug statements in auth with log:info/log:warn
- Remove password hash logging for security
- Add .playback-state.lisp to .gitignore
2026-03-04 00:23:25 +03:00
Glenn Thompson 6e23efe1e4 Fix listener count and recently-played rendering
- Use total listener count across all mounts instead of per-mount
  (asteroid.lisp icecast-status API, stream-harmony.lisp now-playing)
- Fix recently-played.lisp: equal -> = for ParenScript string comparison
- Remove non-existent asteroid-shuffle.mp3 mount from recently-played JS
- Map all mount references to existing asteroid.mp3/asteroid.aac
2026-03-03 23:25:23 +03:00
Glenn Thompson fd1bc504a5 cl-streamer integration fixes: CORS, reconnect, stream config
Server-side fixes (stream-server.lisp):
- Add CORS preflight (OPTIONS) request handler for browser crossorigin audio
- AAC clients start from current buffer position instead of burst to avoid
  ADTS frame alignment issues that caused browser decode errors
- Upgrade client stream error logging from debug to warn for diagnostics
- Add send-cors-preflight function with proper Access-Control headers

Frontend fixes (stream-player.lisp):
- Rewrite reconnect-stream to reuse existing audio element instead of
  creating a new one, preserving browser user gesture context and preventing
  NotAllowedError on autoplay after reconnect
- Unify stream config: both curated and shuffle channels use same mount
  points (asteroid.mp3/asteroid.aac) since cl-streamer has a single pipeline
- Remove non-existent /asteroid-shuffle.mp3 mount reference that caused
  404s and broken pipe cascade when switching to shuffle channel
- Map :low quality to same MP3 mount (asteroid-low.mp3 not yet available)

Note: Channel selector preserved for future multi-stream support.
Recently-played API works correctly; frontend rendering to investigate separately.
2026-03-03 23:17:01 +03:00
Glenn Thompson 77458467c4 Fix integration: CORS, auto-start, mount names, Icecast bypass
CORS fix (icy-protocol.lisp):
- Add Access-Control-Allow-Origin: * to stream response headers
- Browser audio player can now connect cross-origin (port 8080 -> 8000)

Auto-start (asteroid.lisp -main):
- Start cl-streamer pipeline automatically on boot
- Load stream-queue.m3u and begin playback immediately
- Wrapped in handler-case so streaming failure doesn't block web server

Mount names (stream-harmony.lisp):
- Renamed /stream.mp3 -> /asteroid.mp3, /stream.aac -> /asteroid.aac
- Matches existing frontend URLs, zero template changes needed

Icecast bypass (asteroid.lisp, listener-stats.lisp):
- Front page uses get-now-playing-stats instead of icecast-now-playing
- check-icecast-status returns cl-streamer status when pipeline is active
- check-liquidsoap-status returns N/A when using cl-streamer
- asteroid/icecast-status API returns cl-streamer data directly
- poll-and-store-stats uses cl-streamer listener counts directly
- Eliminates hanging HTTP requests to port 8000 for Icecast XML

Tested: full browser streaming working end-to-end
2026-03-03 22:29:21 +03:00
Glenn Thompson dad1418bf8 Integrate cl-streamer into Asteroid Radio (replaces Icecast + Liquidsoap)
New files:
- stream-harmony.lisp: Bridge between cl-streamer pipeline and Asteroid app
  - start-harmony-streaming / stop-harmony-streaming lifecycle
  - on-harmony-track-change callback: feeds recently-played, DB track lookup
  - harmony-now-playing: returns same alist format as icecast-now-playing
  - harmony-load-playlist: loads M3U, converts Docker paths, feeds queue
  - harmony-skip-track / harmony-get-status

Pipeline control (harmony-backend.lisp):
- Add pipeline-current-track, pipeline-on-track-change callback
- Add pipeline-skip, pipeline-queue-files, pipeline-get-queue, pipeline-clear-queue
- play-list now supports skip flag, queue consumption, loop-queue mode
- notify-track-change fires callback after crossfade completes

Graceful fallback - all touch points check *harmony-pipeline*:
- frontend-partials.lisp: now-playing endpoints try Harmony first, fall back to Icecast
- asteroid.lisp: admin APIs (status/skip/reload/restart) try Harmony first
- playlist-scheduler.lisp: load-scheduled-playlist tries Harmony first
- asteroid.asd: added cl-streamer subsystem dependencies

Docker scripts updated:
- start.sh / stop.sh: only start/stop postgres (cl-streamer replaces streaming)
2026-03-03 21:27:29 +03:00
Glenn Thompson edf9326007 Taglib metadata reading, crossfade metadata timing fix
- Add taglib dependency to cl-streamer/harmony system
- Add read-audio-metadata: reads artist/title/album from FLAC/MP3 tags
- Add format-display-title: builds 'Artist - Title' from tags, falls back to filename
- Add update-all-mounts-metadata: updates ICY metadata on all mount points
- Defer metadata update during crossfade until fade completes (listeners hear correct track)
- Fix play-list wait loop: was nested inside crossfade conditional, first track never waited
- Remove filename-derived :title from test playlist (taglib reads real tags now)
2026-03-03 21:10:44 +03:00
Glenn Thompson 2649a8169a AAC encoding fixes, crossfade, buffer tuning, README rewrite
- Fix FDK-AAC C shim: use proper OUT_BITSTREAM_DATA=3 constant and INT types
- Add frame-aligned PCM accumulation buffer to AAC encoder for clean output
- Add fdkaac_encode and fdkaac_close to C shim (all FDK-AAC calls in C)
- Implement crossfade between tracks (3s overlap, 2s fade-in/out)
- Tune buffer: 90% drain sleep for encoding headroom, 64KB burst-on-connect
- Add FFI bindings for new shim functions
- Rewrite README.org with full architecture docs and integration plan
2026-03-03 20:20:43 +03:00
Glenn Thompson fcda723577 feat: Broadcast buffer, sequential playlist, ICY metadata
- Broadcast buffer: single-producer multi-consumer ring buffer with
  per-client read cursors. 32KB burst-on-connect for fast playback.
  Never blocks producer (overwrites old data for slow clients).
- Sequential playlist: play-list runs tracks one at a time using
  Harmony's on-end callback + condition variable for completion.
- ICY metadata: set-now-playing called on each track change.
- Fixed string vs pathname bug in harmony:play (etypecase mismatch).
- Debug logging for client disconnect diagnosis.

Verified: browser plays shuffled FLAC playlist via 128kbps MP3 stream.
2026-03-03 18:15:31 +03:00
Glenn Thompson a9e8276e9a feat: End-to-end streaming working! Custom streaming-drain + fixes
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
2026-03-03 17:46:55 +03:00
Glenn Thompson 9d5166c562 feat: Add Harmony backend with dummy drain for streaming pipeline
- 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
2026-03-03 16:56:24 +03:00
Glenn Thompson 6c15441a08 feat: Add MP3 and AAC encoder FFI bindings
- LAME FFI (lame-ffi.lisp) for MP3 encoding
- FDK-AAC FFI (fdkaac-ffi.lisp) for AAC encoding
- High-level encoder wrappers (encoder.lisp, aac-encoder.lisp)
- Fix compilation warnings in buffer.lisp and icy-protocol.lisp

Both encoders tested and loading successfully:
- LAME version: 3.101
- FDK-AAC: libfdk-aac.so.2

Next steps: Harmony integration for audio pipeline
2026-03-03 16:46:29 +03:00
Glenn Thompson e1be88a54a feat: Initial cl-streamer skeleton for CL-native streaming
- 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.
2026-03-03 16:30:49 +03:00
Luis Pereira 5f9dc80ac8 fix: add parenscript utils file 2026-03-02 17:51:26 -05:00
Luis Pereira 8700724f81 feat: add media session API on now-playing update 2026-03-02 17:51:26 -05:00
Luis Pereira 61266647a9 feat: frontpage now playing default fill 2026-03-02 17:51:26 -05:00
Luis Pereira 6fd8071a05 fix: add favourite count to icecast stats 2026-03-02 17:51:26 -05:00
glenneth 1e5a7e75f3 Replace format t logging with log4cl in playlist-scheduler
Converts all 22 format t print statements to 7 targeted log4cl calls:
- log:info for successful operations (playlist loaded, scheduler started)
- log:warn for recoverable failures (DB fallback, save/delete errors)
- log:error for actionable failures (liquidsoap unresponsive, missing playlist)

Removes redundant startup/shutdown chatter and per-retry debug noise.
2026-02-14 08:01:13 -05:00
glenneth 0da8101f63 fix: Scheduler retry logic + comprehensive documentation update
playlist-scheduler.lisp:
- Add liquidsoap-reload-and-skip with retry mechanism (3 attempts, 2s delay)
- Add liquidsoap-command-succeeded-p to validate telnet responses
- Sends reload before skip for reliability
- Root cause: nc -q1 silently failing, handler-case never triggered

Documentation updates:
- Fix API endpoint count: 15+ → 90+ across all docs
- Fix PROJECT-HISTORY.org Phase 10 heading level (was nested incorrectly)
- Add Phase 11: Scheduler Reliability Fix (February 2026)
- Update PROJECT-HISTORY.org dates and stats to February 2026
- Add Playlist Scheduler section to STREAM-CONTROL.org
- Update Future Enhancements checklist (mark implemented features)
- Fix TEMPLATING_SYSTEM.org missed date update
- New playlists: solar-flare.m3u, ceres-rising.m3u
2026-02-10 07:16:27 -05:00
glenneth 3d595df916 docs: Add complete API endpoint documentation and tidy PROJECT-OVERVIEW
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
2026-02-10 07:16:27 -05:00
glenneth 476ec64309 docs: Comprehensive documentation update January 2026
- Update all #+DATE: and Last Updated lines to 2026-01-26
- Bump documentation version to 3.1
- Standardize author metadata (Glenn → Glenn Thompson where applicable)
- Add Phase 9: Production Launch & Feature Expansion (Nov-Dec 2025)
- Add Phase 10: Documentation Overhaul (January 2026)
- Update PROJECT-HISTORY.org with accurate contributor stats:
  - 614 total commits, 88 PRs merged
  - Glenn Thompson: 354 commits, 59 PRs
  - Brian O'Reilly: 148 commits, 3 PRs
  - Luis Pereira: 109 commits, 26 PRs
- Standardize API endpoints to /api/asteroid/ base path
- Update UI routes to reflect root / mounting
- Correct template extensions from .chtml to .ctml
- Fix Docker music library paths (music/library/)
- Update stream queue location to playlists/stream-queue.m3u
- Expand API-ENDPOINTS.org with playlist, stream control, admin APIs
2026-02-10 07:16:27 -05:00
Luis Pereira d894964c20 fix: increase now-playing rate limit to 10 per second 2026-01-23 21:17:44 -05:00
Luis Pereira 6b56a17b4a fix: restore AUTHSTATE object when there is no login 2026-01-23 21:17:44 -05:00
Luis Pereira 0def454077 feat: use navbar partial on all page templates 2026-01-21 17:32:41 -05:00
Luis Pereira 7c6eaa1fe0 feat: adds templating system docs 2026-01-21 17:32:41 -05:00
Luis Pereira 2992822010 feat: replace global auth state variable with template injected object 2026-01-21 17:32:41 -05:00