/ docs / DEVELOPMENT.org
DEVELOPMENT.org
  1  #+TITLE: Asteroid Radio - Development Guide
  2  #+AUTHOR: Asteroid Radio Development Team
  3  #+DATE: 2025-10-26
  4  
  5  * Development Setup
  6  
  7  #+BEGIN_QUOTE
  8  *Note on Package Managers*: Examples use =apt= (Debian/Ubuntu). Replace with your distribution's package manager (=dnf=, =pacman=, =zypper=, =apk=, etc.).
  9  #+END_QUOTE
 10  
 11  ** Prerequisites
 12  
 13  *** System Dependencies
 14  - SBCL (Steel Bank Common Lisp)
 15  - Quicklisp package manager
 16  - Git version control
 17  - Docker and Docker Compose
 18  - PostgreSQL (for production database)
 19  - taglib for metadata extraction
 20  
 21  *** Lisp Dependencies (via Quicklisp)
 22  
 23  All Lisp dependencies are automatically installed via Quicklisp when you run =(ql:quickload :asteroid)=:
 24  
 25  **** Core Framework
 26  - =radiance= - Web framework and module system
 27  - =slynk= - SLIME/SLY development server
 28  - =i-log4cl= - Logging interface
 29  - =r-clip= - CLIP template processor (Radiance)
 30  - =r-simple-rate= - Rate limiting (Radiance)
 31  - =r-simple-profile= - User profiles (Radiance)
 32  - =r-data-model= - Data modeling (Radiance)
 33  
 34  **** Utilities & Libraries
 35  - =lass= - CSS preprocessing in Lisp
 36  - =cl-json= - JSON encoding/decoding
 37  - =alexandria= - Common Lisp utilities
 38  - =local-time= - Time and date handling
 39  - =taglib= - Audio metadata extraction
 40  - =ironclad= - Cryptographic functions
 41  - =babel= - Character encoding conversion
 42  - =cl-fad= - File and directory operations
 43  - =bordeaux-threads= - Portable threading
 44  - =drakma= - HTTP client
 45  - =usocket= - Universal socket library
 46  - =cl-ppcre= - Perl-compatible regular expressions
 47  
 48  **** Radiance Interfaces
 49  - =:auth= - Authentication interface
 50  - =:database= - Database interface
 51  - =:user= - User management interface
 52  
 53  *** Ubuntu/Debian Installation
 54  #+BEGIN_SRC bash
 55  # Install system packages
 56  sudo apt update
 57  sudo apt install sbcl git docker.io docker-compose postgresql libtagc0-dev
 58  
 59  # Add user to docker group
 60  sudo usermod -a -G docker $USER
 61  # Log out and back in for group changes to take effect
 62  
 63  # Install Quicklisp (if not already installed)
 64  curl -O https://beta.quicklisp.org/quicklisp.lisp
 65  sbcl --load quicklisp.lisp --eval "(quicklisp-quickstart:install)" --quit
 66  
 67  # Note: PostgreSQL runs in Docker for development
 68  # See docs/POSTGRESQL-SETUP.org for database configuration
 69  #+END_SRC
 70  
 71  ** Project Setup
 72  
 73  *** Clone Repository
 74  #+BEGIN_SRC bash
 75  git clone https://github.com/fade/asteroid.git
 76  cd asteroid
 77  #+END_SRC
 78  
 79  *** Install Lisp Dependencies
 80  #+BEGIN_SRC bash
 81  # Start SBCL and load the system
 82  sbcl
 83  (ql:quickload :asteroid)
 84  #+END_SRC
 85  
 86  *** ASDF Configuration (Optional but Recommended)
 87  For easier development, configure ASDF to find the asteroid system:
 88  #+BEGIN_SRC bash
 89  # Create ASDF source registry configuration
 90  mkdir -p ~/.config/common-lisp
 91  cat > ~/.config/common-lisp/source-registry.conf
 92  ;; -*-lisp-*-
 93  (:source-registry
 94   (:tree "/path/to/your/projects/")
 95   :inherit-configuration)
 96  #+END_SRC
 97  
 98  This allows you to load the asteroid system from any directory without changing paths.
 99  
100  * Development Workflow
101  
102  ** Local Development Server
103  
104  *** Starting Development Environment
105  #+BEGIN_SRC bash
106  # Start Docker streaming services
107  cd docker/
108  docker compose up -d
109  
110  # Verify containers are running
111  docker compose ps
112  
113  # View logs
114  docker compose logs -f
115  
116  # Start RADIANCE web server (local development)
117  sbcl --eval "(ql:quickload :asteroid)" --eval "(asteroid:start-server)"
118  #+END_SRC
119  
120  *** Development URLs
121  - *Web Interface*: http://localhost:8080/asteroid/
122  - *Admin Panel*: http://localhost:8080/asteroid/admin
123  - *User Management*: http://localhost:8080/asteroid/admin/users
124  - *Web Player*: http://localhost:8080/asteroid/player
125  - *API Base*: http://localhost:8080/api/asteroid/
126  - *Live Stream*: http://localhost:8000/asteroid.mp3
127  - *Icecast Admin*: http://localhost:8000/admin/ (admin/asteroid_admin_2024)
128  
129  ** Music Library Management
130  
131  *** Directory Structure
132  The music directory is located directly under the asteroid root directory:
133  #+BEGIN_SRC
134  asteroid/music/                 # Music directory (can be symlink)
135  ├── artist1/
136  │   ├── album1/
137  │   │   ├── track1.mp3
138  │   │   └── track2.flac
139  │   └── album2/
140  │       └── track3.ogg
141  └── artist2/
142      └── single.wav
143  #+END_SRC
144  
145  The =music/= directory can be:
146  - A regular directory with music files
147  - A symlink to your actual music collection
148  - Multiple subdirectories or symlinks within it
149  
150  *** Recursive Scanning Capabilities
151  The Asteroid application includes built-in recursive directory scanning:
152  - *Function*: =scan-music-library= in =stream-media.lisp=
153  - *Supports*: MP3, FLAC, OGG, WAV formats
154  - *Recursive*: Automatically scans all subdirectories
155  - *Metadata*: Extracts title, artist, album, duration using taglib
156  - *Database*: Stores track information in RADIANCE database
157  
158  *** Adding Music to Development Environment
159  #+BEGIN_SRC bash
160  # Option 1: Copy music files directly
161  cp -r /path/to/your/music/* music/
162  
163  # Option 2: Symlink entire music directory
164  ln -s /path/to/existing/music music
165  
166  # Option 3: Symlink subdirectories within music/
167  mkdir -p music
168  ln -s /path/to/collection1 music/collection1
169  ln -s /path/to/collection2 music/collection2
170  
171  # Option 4: Mount remote directory (for large collections)
172  # Edit docker-compose.yml to change volume mount:
173  # volumes:
174  #   - /mnt/remote-music:/app/music:ro
175  
176  # Trigger library scan via API
177  curl -X POST http://localhost:8080/api/asteroid/admin/scan-library
178  #+END_SRC
179  
180  ** Code Organization
181  
182  *** Main Components
183  - =asteroid.lisp= - Main server with RADIANCE routes and API endpoints
184  - =asteroid.asd= - System definition with dependencies
185  - =template/= - CLIP HTML templates for web interface
186  - =static/= - CSS stylesheets and static assets
187  - =asteroid-radio.liq= - Liquidsoap streaming configuration
188  
189  *** Key Modules
190  - *Web Routes*: RADIANCE framework with =#@= URL patterns
191  - *Database*: RADIANCE DB abstraction for track metadata
192  - *Streaming*: Docker containers with Icecast2 and Liquidsoap
193  - *File Processing*: Metadata extraction and library management
194  - *Docker Integration*: Containerized streaming infrastructure
195  
196  ** Development Practices
197  
198  *** Code Style
199  - Use 2-space indentation for Lisp code
200  - Follow Common Lisp naming conventions
201  - Document functions with docstrings
202  - Use meaningful variable and function names
203  
204  *** Database Development
205  #+BEGIN_SRC lisp
206  ;; Always use quoted symbols for field names
207  (db:select 'tracks (db:query (:= 'artist "Artist Name")))
208  
209  ;; Primary key is "_id" internally, "id" in JSON responses
210  (gethash "_id" track-record)
211  #+END_SRC
212  
213  *** Template Development with CLIP
214  
215  Asteroid Radio uses CLIP (Common Lisp HTML Processor) for templating. Templates are in the =template/= directory.
216  
217  **** Custom =data-text= Attribute Processor
218  
219  We define a custom CLIP attribute processor in =template-utils.lisp= for dynamic text replacement:
220  
221  #+BEGIN_SRC lisp
222  ;; Defined in template-utils.lisp
223  (clip:define-attribute-processor data-text (node value)
224    "Process data-text attribute - replaces node text content with clipboard value"
225    (plump:clear node)
226    (plump:make-text-node node (clip:clipboard value)))
227  #+END_SRC
228  
229  **** Using =data-text= in Templates
230  
231  In your HTML templates (=.chtml= files):
232  
233  #+BEGIN_SRC html
234  <!-- The data-text attribute gets replaced with the value from the plist -->
235  <h1 data-text="page-title">Default Title</h1>
236  <span data-text="username">Guest</span>
237  <p data-text="status-message">Loading...</p>
238  #+END_SRC
239  
240  **** Rendering Templates from Lisp
241  
242  In your route handlers:
243  
244  #+BEGIN_SRC lisp
245  (define-page my-page #@"/my-page" ()
246    (render-template-with-plist "my-template"
247                                :page-title "My Page"
248                                :username (user:username (auth:current))
249                                :status-message "Ready"))
250  #+END_SRC
251  
252  **** How It Works
253  
254  1. =render-template-with-plist= passes keyword arguments to CLIP
255  2. CLIP processes the template and finds =data-text= attributes
256  3. The custom processor replaces the node's text with the value from the "clipboard" (keyword args)
257  4. Default text in the HTML is replaced with dynamic content
258  
259  **** CLIP Documentation
260  
261  - **CLIP GitHub**: https://github.com/Shinmera/clip
262  - **Attribute Processors**: Custom processors extend CLIP's functionality
263  - **Standard CLIP**: Uses =lquery= for more complex DOM manipulation
264  - **Our Approach**: Simple =data-text= processor for most use cases
265  
266  **** Template Development Tips
267  
268  - Keep templates in =template/= directory
269  - Use =data-text= for simple text replacement
270  - Test template changes with browser refresh (templates are cached)
271  - Clear cache during development: =(clear-template-cache)=
272  - Maintain responsive design principles
273  
274  *** CSS Development with LASS
275  - CSS is generated dynamically from =static/asteroid.lass= using LASS (Lisp Augmented Style Sheets)
276  - Edit the =.lass= file, not the generated =.css= file
277  - CSS is automatically compiled when the server starts via =compile-styles= function
278  - Use Lisp syntax for CSS: =(body :background "#0a0a0a" :color "#00ffff")=
279  - Supports nested selectors, variables, and programmatic CSS generation
280  
281  ** Testing
282  
283  *** Manual Testing Checklist
284  - [ ] Web interface loads correctly
285  - [ ] Admin panel functions work
286  - [ ] File upload and processing works
287  - [ ] Live stream plays audio
288  - [ ] Database queries return expected results
289  - [ ] API endpoints respond correctly
290  
291  *** Docker Container Testing
292  #+BEGIN_SRC bash
293  # Check container status
294  docker compose ps
295  
296  # Test stream connectivity
297  curl -I http://localhost:8000/asteroid.mp3
298  
299  # Test with media player
300  vlc http://localhost:8000/asteroid.mp3
301  
302  # Check container logs
303  docker compose logs icecast
304  docker compose logs liquidsoap
305  #+END_SRC
306  
307  *** API Testing
308  
309  Asteroid Radio includes a comprehensive automated test suite:
310  
311  #+BEGIN_SRC bash
312  # Run full test suite
313  ./test-server.sh
314  
315  # Run with verbose output
316  ./test-server.sh -v
317  
318  # Test specific endpoints manually
319  curl http://localhost:8080/api/asteroid/status
320  curl http://localhost:8080/api/asteroid/tracks
321  curl -X POST http://localhost:8080/api/asteroid/player/play -d "track-id=123"
322  #+END_SRC
323  
324  See [[file:TESTING.org][Testing Guide]] for complete documentation.
325  
326  *** API Endpoint Structure
327  
328  All API endpoints use Radiance's =define-api= macro and follow this pattern:
329  
330  - Base URL: =/api/asteroid/=
331  - Response format: JSON
332  - Authentication: Session-based for protected endpoints
333  
334  See [[file:API-ENDPOINTS.org][API Endpoints Reference]] for complete API documentation.
335  
336  ** Debugging
337  
338  *** Common Development Issues
339  
340  **** Stream Not Playing
341  - Check Docker container status: =docker compose ps=
342  - Check Liquidsoap container logs: =docker compose logs liquidsoap=
343  - Check Icecast2 container logs: =docker compose logs icecast=
344  - Verify music files exist in =docker/music/library/=
345  - Restart containers: =docker compose restart=
346  
347  **** Database Errors
348  - Ensure proper field name quoting in queries
349  - Check RADIANCE database configuration
350  - Verify database file permissions
351  
352  **** Template Rendering Issues
353  - Check CLIP template syntax
354  - Verify template file paths
355  - Test with simplified templates first
356  
357  *** Debug Configuration
358  #+BEGIN_SRC bash
359  # Enable verbose logging in Docker containers
360  # Edit docker/liquidsoap/asteroid-radio.liq
361  settings.log.level := 4
362  settings.log.stdout := true
363  settings.log.file := true
364  settings.log.file.path := "/var/log/liquidsoap/asteroid.log"
365  
366  # View real-time container logs
367  docker compose logs -f liquidsoap
368  docker compose logs -f icecast
369  #+END_SRC
370  
371  ** Contributing Guidelines
372  
373  *** Branch Strategy
374  - =main= - Stable production code
375  - =develop= - Integration branch for new features
376  - =feature/*= - Individual feature development
377  - =bugfix/*= - Bug fixes and patches
378  
379  *** Commit Messages
380  - Use clear, descriptive commit messages
381  - Reference issue numbers when applicable
382  - Keep commits focused on single changes
383  
384  *** Pull Request Process
385  1. Create feature branch from =develop=
386  2. Implement changes with tests
387  3. Update documentation if needed
388  4. Submit pull request with description
389  5. Address code review feedback
390  6. Merge after approval
391  
392  *** Code Review Checklist
393  - [ ] Code follows project style guidelines
394  - [ ] Functions are properly documented
395  - [ ] No hardcoded values or credentials
396  - [ ] Error handling is appropriate
397  - [ ] Performance considerations addressed
398  
399  ** Development Tools
400  
401  *** Recommended Editor Setup
402  - *Emacs*: SLIME for interactive Lisp development
403  
404  *** Useful Development Commands
405  #+BEGIN_SRC lisp
406  ;; Reload system during development
407  (ql:quickload :asteroid :force t)
408  
409  ;; Restart RADIANCE server
410  (radiance:shutdown)
411  (asteroid:start-server)
412  
413  ;; Clear database for testing
414  (db:drop 'tracks)
415  (asteroid:setup-database)
416  #+END_SRC
417  
418  ** Performance Considerations
419  
420  *** Development vs Production
421  - Use smaller music libraries in =docker/music/= for faster testing
422  - Enable debug logging in Docker containers only when needed
423  - Consider memory usage with large track collections in containers
424  - Test with realistic concurrent user loads using Docker scaling
425  - Use =docker compose.dev.yml= for development-specific settings
426  
427  *** Optimization Tips
428  - Cache database queries where appropriate
429  - Optimize playlist generation for large libraries
430  - Monitor memory usage during development
431  - Profile streaming performance under load
432  
433  * Configuration Files
434  - =radiance-core.conf.lisp= - RADIANCE framework configuration
435  - =docker/liquidsoap/asteroid-radio.liq= - Liquidsoap streaming setup
436  - =docker/icecast.xml= - Icecast2 server configuration
437  - =docker/docker-compose.yml= - Container orchestration
438  
439  ** Docker Development
440  #+BEGIN_SRC bash
441  # Start development containers
442  cd docker/
443  docker compose up -d
444  
445  # Build development container with changes
446  docker compose up --build
447  
448  # Access container shell for debugging
449  docker compose exec liquidsoap bash
450  docker compose exec icecast bash
451  
452  # Stop all containers
453  docker compose down
454  #+END_SRC
455  
456  * Troubleshooting
457  
458  ** Development Environment Issues
459  
460  *** SBCL/Quicklisp Problems
461  - Ensure Quicklisp is properly installed
462  - Check for conflicting Lisp installations
463  - Verify system dependencies are installed
464  
465  *** Docker Container Issues
466  - Check container status: =docker compose ps=
467  - Verify Docker daemon is running: =docker info=
468  - Check container logs: =docker compose logs [service]=
469  - Restart containers: =docker compose restart=
470  
471  *** Network Access Issues
472  - Check firewall settings for ports 8000, 8080
473  - Verify WSL networking configuration if applicable
474  - Test container networking: =docker compose exec liquidsoap ping icecast=
475  - Check port binding: =docker compose port icecast 8000=
476  
477  *** File Permission Issues
478  - Ensure =docker/music/= directory is accessible
479  - Check ownership: =ls -la docker/music/=
480  - Fix permissions: =sudo chown -R $USER:$USER docker/music/=
481  - Verify container volume mounts in =docker-compose.yml=
482  - For remote mounts: ensure network storage is accessible
483  
484  *** Music Library Issues
485  - Check if music files exist: =find docker/music/ -name "*.mp3" -o -name "*.flac"=
486  - Verify supported formats: MP3, FLAC, OGG, WAV
487  - Test recursive scanning: =curl -X POST http://localhost:8080/asteroid/api/scan-library=
488  - Check database for tracks: =curl http://localhost:8080/asteroid/api/tracks=
489  - For large collections: avoid network mounts, use local storage (see memory about 175+ files causing timeouts)
490  
491  ** Getting Help
492  - Check existing issues in project repository
493  - Review RADIANCE framework documentation
494  - Consult Liquidsoap manual for streaming issues
495  - Join our IRC chat room: **#asteroid.music** on **irc.libera.chat**
496  - Ask questions in project discussions
497  
498  This development guide provides the foundation for contributing to Asteroid Radio. For deployment and production considerations, see the Installation Guide and Performance Testing documentation.
499  
500  * Development Stack Links
501  
502  ** Core Technologies
503  - **SBCL** (Steel Bank Common Lisp): https://www.sbcl.org/
504  - **Quicklisp** (Common Lisp package manager): https://www.quicklisp.org/
505  - **ASDF** (Another System Definition Facility): https://common-lisp.net/project/asdf/
506  
507  ** Web Framework & Libraries
508  - **RADIANCE** (Web framework): https://shirakumo.github.io/radiance/
509  - **CLIP** (HTML templating): https://shinmera.github.io/clip/
510  - **LASS** (CSS in Lisp): https://shinmera.github.io/LASS/
511  - **Alexandria** (Utility library): https://alexandria.common-lisp.dev/
512  - **Local-Time** (Time handling): https://common-lisp.net/project/local-time/
513  
514  ** Audio & Streaming
515  - **Docker** (Containerization): https://www.docker.com/
516  - **Icecast2** (Streaming server): https://icecast.org/
517  - **Liquidsoap** (Audio streaming): https://www.liquidsoap.info/
518  - **TagLib** (Audio metadata): https://taglib.org/
519  
520  ** Database & Data
521  - **cl-json** (JSON handling): https://common-lisp.net/project/cl-json/
522  - **cl-fad** (File/directory utilities): https://edicl.github.io/cl-fad/
523  - **Ironclad** (Cryptography): https://github.com/sharplispers/ironclad
524  - **Babel** (Character encoding): https://common-lisp.net/project/babel/
525  
526  ** Development Tools
527  - **Emacs** (Editor): https://www.gnu.org/software/emacs/
528  - **SLIME** (Emacs Lisp IDE): https://common-lisp.net/project/slime/
529  - **Slynk** (SLIME backend): https://github.com/joaotavora/sly
530  - **Git** (Version control): https://git-scm.com/
531  
532  ** System Libraries
533  - **Bordeaux-Threads** (Threading): https://common-lisp.net/project/bordeaux-threads/
534  - **Drakma** (HTTP client): https://edicl.github.io/drakma/
535  - **CIFS-Utils** (Network file systems): https://wiki.samba.org/index.php/LinuxCIFS_utils
536  
537  ** Documentation & Standards
538  - **Common Lisp HyperSpec**: http://www.lispworks.com/documentation/HyperSpec/Front/
539  - **Docker Compose**: https://docs.docker.com/compose/
540  - **Org Mode** (Documentation format): https://orgmode.org/