541 lines
17 KiB
Org Mode
541 lines
17 KiB
Org Mode
#+TITLE: Asteroid Radio - Development Guide
|
|
#+AUTHOR: Asteroid Radio Development Team
|
|
#+DATE: 2025-10-26
|
|
|
|
* Development Setup
|
|
|
|
#+BEGIN_QUOTE
|
|
*Note on Package Managers*: Examples use =apt= (Debian/Ubuntu). Replace with your distribution's package manager (=dnf=, =pacman=, =zypper=, =apk=, etc.).
|
|
#+END_QUOTE
|
|
|
|
** Prerequisites
|
|
|
|
*** System Dependencies
|
|
- SBCL (Steel Bank Common Lisp)
|
|
- Quicklisp package manager
|
|
- Git version control
|
|
- Docker and Docker Compose
|
|
- PostgreSQL (for production database)
|
|
- taglib for metadata extraction
|
|
|
|
*** Lisp Dependencies (via Quicklisp)
|
|
|
|
All Lisp dependencies are automatically installed via Quicklisp when you run =(ql:quickload :asteroid)=:
|
|
|
|
**** Core Framework
|
|
- =radiance= - Web framework and module system
|
|
- =slynk= - SLIME/SLY development server
|
|
- =i-log4cl= - Logging interface
|
|
- =r-clip= - CLIP template processor (Radiance)
|
|
- =r-simple-rate= - Rate limiting (Radiance)
|
|
- =r-simple-profile= - User profiles (Radiance)
|
|
- =r-data-model= - Data modeling (Radiance)
|
|
|
|
**** Utilities & Libraries
|
|
- =lass= - CSS preprocessing in Lisp
|
|
- =cl-json= - JSON encoding/decoding
|
|
- =alexandria= - Common Lisp utilities
|
|
- =local-time= - Time and date handling
|
|
- =taglib= - Audio metadata extraction
|
|
- =ironclad= - Cryptographic functions
|
|
- =babel= - Character encoding conversion
|
|
- =cl-fad= - File and directory operations
|
|
- =bordeaux-threads= - Portable threading
|
|
- =drakma= - HTTP client
|
|
- =usocket= - Universal socket library
|
|
- =cl-ppcre= - Perl-compatible regular expressions
|
|
|
|
**** Radiance Interfaces
|
|
- =:auth= - Authentication interface
|
|
- =:database= - Database interface
|
|
- =:user= - User management interface
|
|
|
|
*** Ubuntu/Debian Installation
|
|
#+BEGIN_SRC bash
|
|
# Install system packages
|
|
sudo apt update
|
|
sudo apt install sbcl git docker.io docker-compose postgresql libtagc0-dev
|
|
|
|
# Add user to docker group
|
|
sudo usermod -a -G docker $USER
|
|
# Log out and back in for group changes to take effect
|
|
|
|
# Install Quicklisp (if not already installed)
|
|
curl -O https://beta.quicklisp.org/quicklisp.lisp
|
|
sbcl --load quicklisp.lisp --eval "(quicklisp-quickstart:install)" --quit
|
|
|
|
# Note: PostgreSQL runs in Docker for development
|
|
# See docs/POSTGRESQL-SETUP.org for database configuration
|
|
#+END_SRC
|
|
|
|
** Project Setup
|
|
|
|
*** Clone Repository
|
|
#+BEGIN_SRC bash
|
|
git clone https://github.com/fade/asteroid.git
|
|
cd asteroid
|
|
#+END_SRC
|
|
|
|
*** Install Lisp Dependencies
|
|
#+BEGIN_SRC bash
|
|
# Start SBCL and load the system
|
|
sbcl
|
|
(ql:quickload :asteroid)
|
|
#+END_SRC
|
|
|
|
*** ASDF Configuration (Optional but Recommended)
|
|
For easier development, configure ASDF to find the asteroid system:
|
|
#+BEGIN_SRC bash
|
|
# Create ASDF source registry configuration
|
|
mkdir -p ~/.config/common-lisp
|
|
cat > ~/.config/common-lisp/source-registry.conf
|
|
;; -*-lisp-*-
|
|
(:source-registry
|
|
(:tree "/path/to/your/projects/")
|
|
:inherit-configuration)
|
|
#+END_SRC
|
|
|
|
This allows you to load the asteroid system from any directory without changing paths.
|
|
|
|
* Development Workflow
|
|
|
|
** Local Development Server
|
|
|
|
*** Starting Development Environment
|
|
#+BEGIN_SRC bash
|
|
# Start Docker streaming services
|
|
cd docker/
|
|
docker compose up -d
|
|
|
|
# Verify containers are running
|
|
docker compose ps
|
|
|
|
# View logs
|
|
docker compose logs -f
|
|
|
|
# Start RADIANCE web server (local development)
|
|
sbcl --eval "(ql:quickload :asteroid)" --eval "(asteroid:start-server)"
|
|
#+END_SRC
|
|
|
|
*** Development URLs
|
|
- *Web Interface*: http://localhost:8080/asteroid/
|
|
- *Admin Panel*: http://localhost:8080/asteroid/admin
|
|
- *User Management*: http://localhost:8080/asteroid/admin/users
|
|
- *Web Player*: http://localhost:8080/asteroid/player
|
|
- *API Base*: http://localhost:8080/api/asteroid/
|
|
- *Live Stream*: http://localhost:8000/asteroid.mp3
|
|
- *Icecast Admin*: http://localhost:8000/admin/ (admin/asteroid_admin_2024)
|
|
|
|
** Music Library Management
|
|
|
|
*** Directory Structure
|
|
The music directory is located directly under the asteroid root directory:
|
|
#+BEGIN_SRC
|
|
asteroid/music/ # Music directory (can be symlink)
|
|
├── artist1/
|
|
│ ├── album1/
|
|
│ │ ├── track1.mp3
|
|
│ │ └── track2.flac
|
|
│ └── album2/
|
|
│ └── track3.ogg
|
|
└── artist2/
|
|
└── single.wav
|
|
#+END_SRC
|
|
|
|
The =music/= directory can be:
|
|
- A regular directory with music files
|
|
- A symlink to your actual music collection
|
|
- Multiple subdirectories or symlinks within it
|
|
|
|
*** Recursive Scanning Capabilities
|
|
The Asteroid application includes built-in recursive directory scanning:
|
|
- *Function*: =scan-music-library= in =stream-media.lisp=
|
|
- *Supports*: MP3, FLAC, OGG, WAV formats
|
|
- *Recursive*: Automatically scans all subdirectories
|
|
- *Metadata*: Extracts title, artist, album, duration using taglib
|
|
- *Database*: Stores track information in RADIANCE database
|
|
|
|
*** Adding Music to Development Environment
|
|
#+BEGIN_SRC bash
|
|
# Option 1: Copy music files directly
|
|
cp -r /path/to/your/music/* music/
|
|
|
|
# Option 2: Symlink entire music directory
|
|
ln -s /path/to/existing/music music
|
|
|
|
# Option 3: Symlink subdirectories within music/
|
|
mkdir -p music
|
|
ln -s /path/to/collection1 music/collection1
|
|
ln -s /path/to/collection2 music/collection2
|
|
|
|
# Option 4: Mount remote directory (for large collections)
|
|
# Edit docker-compose.yml to change volume mount:
|
|
# volumes:
|
|
# - /mnt/remote-music:/app/music:ro
|
|
|
|
# Trigger library scan via API
|
|
curl -X POST http://localhost:8080/api/asteroid/admin/scan-library
|
|
#+END_SRC
|
|
|
|
** Code Organization
|
|
|
|
*** Main Components
|
|
- =asteroid.lisp= - Main server with RADIANCE routes and API endpoints
|
|
- =asteroid.asd= - System definition with dependencies
|
|
- =template/= - CLIP HTML templates for web interface
|
|
- =static/= - CSS stylesheets and static assets
|
|
- =asteroid-radio.liq= - Liquidsoap streaming configuration
|
|
|
|
*** Key Modules
|
|
- *Web Routes*: RADIANCE framework with =#@= URL patterns
|
|
- *Database*: RADIANCE DB abstraction for track metadata
|
|
- *Streaming*: Docker containers with Icecast2 and Liquidsoap
|
|
- *File Processing*: Metadata extraction and library management
|
|
- *Docker Integration*: Containerized streaming infrastructure
|
|
|
|
** Development Practices
|
|
|
|
*** Code Style
|
|
- Use 2-space indentation for Lisp code
|
|
- Follow Common Lisp naming conventions
|
|
- Document functions with docstrings
|
|
- Use meaningful variable and function names
|
|
|
|
*** Database Development
|
|
#+BEGIN_SRC lisp
|
|
;; Always use quoted symbols for field names
|
|
(db:select 'tracks (db:query (:= 'artist "Artist Name")))
|
|
|
|
;; Primary key is "_id" internally, "id" in JSON responses
|
|
(gethash "_id" track-record)
|
|
#+END_SRC
|
|
|
|
*** Template Development with CLIP
|
|
|
|
Asteroid Radio uses CLIP (Common Lisp HTML Processor) for templating. Templates are in the =template/= directory.
|
|
|
|
**** Custom =data-text= Attribute Processor
|
|
|
|
We define a custom CLIP attribute processor in =template-utils.lisp= for dynamic text replacement:
|
|
|
|
#+BEGIN_SRC lisp
|
|
;; Defined in template-utils.lisp
|
|
(clip:define-attribute-processor data-text (node value)
|
|
"Process data-text attribute - replaces node text content with clipboard value"
|
|
(plump:clear node)
|
|
(plump:make-text-node node (clip:clipboard value)))
|
|
#+END_SRC
|
|
|
|
**** Using =data-text= in Templates
|
|
|
|
In your HTML templates (=.chtml= files):
|
|
|
|
#+BEGIN_SRC html
|
|
<!-- The data-text attribute gets replaced with the value from the plist -->
|
|
<h1 data-text="page-title">Default Title</h1>
|
|
<span data-text="username">Guest</span>
|
|
<p data-text="status-message">Loading...</p>
|
|
#+END_SRC
|
|
|
|
**** Rendering Templates from Lisp
|
|
|
|
In your route handlers:
|
|
|
|
#+BEGIN_SRC lisp
|
|
(define-page my-page #@"/my-page" ()
|
|
(render-template-with-plist "my-template"
|
|
:page-title "My Page"
|
|
:username (user:username (auth:current))
|
|
:status-message "Ready"))
|
|
#+END_SRC
|
|
|
|
**** How It Works
|
|
|
|
1. =render-template-with-plist= passes keyword arguments to CLIP
|
|
2. CLIP processes the template and finds =data-text= attributes
|
|
3. The custom processor replaces the node's text with the value from the "clipboard" (keyword args)
|
|
4. Default text in the HTML is replaced with dynamic content
|
|
|
|
**** CLIP Documentation
|
|
|
|
- **CLIP GitHub**: https://github.com/Shinmera/clip
|
|
- **Attribute Processors**: Custom processors extend CLIP's functionality
|
|
- **Standard CLIP**: Uses =lquery= for more complex DOM manipulation
|
|
- **Our Approach**: Simple =data-text= processor for most use cases
|
|
|
|
**** Template Development Tips
|
|
|
|
- Keep templates in =template/= directory
|
|
- Use =data-text= for simple text replacement
|
|
- Test template changes with browser refresh (templates are cached)
|
|
- Clear cache during development: =(clear-template-cache)=
|
|
- Maintain responsive design principles
|
|
|
|
*** CSS Development with LASS
|
|
- CSS is generated dynamically from =static/asteroid.lass= using LASS (Lisp Augmented Style Sheets)
|
|
- Edit the =.lass= file, not the generated =.css= file
|
|
- CSS is automatically compiled when the server starts via =compile-styles= function
|
|
- Use Lisp syntax for CSS: =(body :background "#0a0a0a" :color "#00ffff")=
|
|
- Supports nested selectors, variables, and programmatic CSS generation
|
|
|
|
** Testing
|
|
|
|
*** Manual Testing Checklist
|
|
- [ ] Web interface loads correctly
|
|
- [ ] Admin panel functions work
|
|
- [ ] File upload and processing works
|
|
- [ ] Live stream plays audio
|
|
- [ ] Database queries return expected results
|
|
- [ ] API endpoints respond correctly
|
|
|
|
*** Docker Container Testing
|
|
#+BEGIN_SRC bash
|
|
# Check container status
|
|
docker compose ps
|
|
|
|
# Test stream connectivity
|
|
curl -I http://localhost:8000/asteroid.mp3
|
|
|
|
# Test with media player
|
|
vlc http://localhost:8000/asteroid.mp3
|
|
|
|
# Check container logs
|
|
docker compose logs icecast
|
|
docker compose logs liquidsoap
|
|
#+END_SRC
|
|
|
|
*** API Testing
|
|
|
|
Asteroid Radio includes a comprehensive automated test suite:
|
|
|
|
#+BEGIN_SRC bash
|
|
# Run full test suite
|
|
./test-server.sh
|
|
|
|
# Run with verbose output
|
|
./test-server.sh -v
|
|
|
|
# Test specific endpoints manually
|
|
curl http://localhost:8080/api/asteroid/status
|
|
curl http://localhost:8080/api/asteroid/tracks
|
|
curl -X POST http://localhost:8080/api/asteroid/player/play -d "track-id=123"
|
|
#+END_SRC
|
|
|
|
See [[file:TESTING.org][Testing Guide]] for complete documentation.
|
|
|
|
*** API Endpoint Structure
|
|
|
|
All API endpoints use Radiance's =define-api= macro and follow this pattern:
|
|
|
|
- Base URL: =/api/asteroid/=
|
|
- Response format: JSON
|
|
- Authentication: Session-based for protected endpoints
|
|
|
|
See [[file:API-ENDPOINTS.org][API Endpoints Reference]] for complete API documentation.
|
|
|
|
** Debugging
|
|
|
|
*** Common Development Issues
|
|
|
|
**** Stream Not Playing
|
|
- Check Docker container status: =docker compose ps=
|
|
- Check Liquidsoap container logs: =docker compose logs liquidsoap=
|
|
- Check Icecast2 container logs: =docker compose logs icecast=
|
|
- Verify music files exist in =docker/music/library/=
|
|
- Restart containers: =docker compose restart=
|
|
|
|
**** Database Errors
|
|
- Ensure proper field name quoting in queries
|
|
- Check RADIANCE database configuration
|
|
- Verify database file permissions
|
|
|
|
**** Template Rendering Issues
|
|
- Check CLIP template syntax
|
|
- Verify template file paths
|
|
- Test with simplified templates first
|
|
|
|
*** Debug Configuration
|
|
#+BEGIN_SRC bash
|
|
# Enable verbose logging in Docker containers
|
|
# Edit docker/liquidsoap/asteroid-radio.liq
|
|
settings.log.level := 4
|
|
settings.log.stdout := true
|
|
settings.log.file := true
|
|
settings.log.file.path := "/var/log/liquidsoap/asteroid.log"
|
|
|
|
# View real-time container logs
|
|
docker compose logs -f liquidsoap
|
|
docker compose logs -f icecast
|
|
#+END_SRC
|
|
|
|
** Contributing Guidelines
|
|
|
|
*** Branch Strategy
|
|
- =main= - Stable production code
|
|
- =develop= - Integration branch for new features
|
|
- =feature/*= - Individual feature development
|
|
- =bugfix/*= - Bug fixes and patches
|
|
|
|
*** Commit Messages
|
|
- Use clear, descriptive commit messages
|
|
- Reference issue numbers when applicable
|
|
- Keep commits focused on single changes
|
|
|
|
*** Pull Request Process
|
|
1. Create feature branch from =develop=
|
|
2. Implement changes with tests
|
|
3. Update documentation if needed
|
|
4. Submit pull request with description
|
|
5. Address code review feedback
|
|
6. Merge after approval
|
|
|
|
*** Code Review Checklist
|
|
- [ ] Code follows project style guidelines
|
|
- [ ] Functions are properly documented
|
|
- [ ] No hardcoded values or credentials
|
|
- [ ] Error handling is appropriate
|
|
- [ ] Performance considerations addressed
|
|
|
|
** Development Tools
|
|
|
|
*** Recommended Editor Setup
|
|
- *Emacs*: SLIME for interactive Lisp development
|
|
|
|
*** Useful Development Commands
|
|
#+BEGIN_SRC lisp
|
|
;; Reload system during development
|
|
(ql:quickload :asteroid :force t)
|
|
|
|
;; Restart RADIANCE server
|
|
(radiance:shutdown)
|
|
(asteroid:start-server)
|
|
|
|
;; Clear database for testing
|
|
(db:drop 'tracks)
|
|
(asteroid:setup-database)
|
|
#+END_SRC
|
|
|
|
** Performance Considerations
|
|
|
|
*** Development vs Production
|
|
- Use smaller music libraries in =docker/music/= for faster testing
|
|
- Enable debug logging in Docker containers only when needed
|
|
- Consider memory usage with large track collections in containers
|
|
- Test with realistic concurrent user loads using Docker scaling
|
|
- Use =docker compose.dev.yml= for development-specific settings
|
|
|
|
*** Optimization Tips
|
|
- Cache database queries where appropriate
|
|
- Optimize playlist generation for large libraries
|
|
- Monitor memory usage during development
|
|
- Profile streaming performance under load
|
|
|
|
* Configuration Files
|
|
- =radiance-core.conf.lisp= - RADIANCE framework configuration
|
|
- =docker/liquidsoap/asteroid-radio.liq= - Liquidsoap streaming setup
|
|
- =docker/icecast.xml= - Icecast2 server configuration
|
|
- =docker/docker-compose.yml= - Container orchestration
|
|
|
|
** Docker Development
|
|
#+BEGIN_SRC bash
|
|
# Start development containers
|
|
cd docker/
|
|
docker compose up -d
|
|
|
|
# Build development container with changes
|
|
docker compose up --build
|
|
|
|
# Access container shell for debugging
|
|
docker compose exec liquidsoap bash
|
|
docker compose exec icecast bash
|
|
|
|
# Stop all containers
|
|
docker compose down
|
|
#+END_SRC
|
|
|
|
* Troubleshooting
|
|
|
|
** Development Environment Issues
|
|
|
|
*** SBCL/Quicklisp Problems
|
|
- Ensure Quicklisp is properly installed
|
|
- Check for conflicting Lisp installations
|
|
- Verify system dependencies are installed
|
|
|
|
*** Docker Container Issues
|
|
- Check container status: =docker compose ps=
|
|
- Verify Docker daemon is running: =docker info=
|
|
- Check container logs: =docker compose logs [service]=
|
|
- Restart containers: =docker compose restart=
|
|
|
|
*** Network Access Issues
|
|
- Check firewall settings for ports 8000, 8080
|
|
- Verify WSL networking configuration if applicable
|
|
- Test container networking: =docker compose exec liquidsoap ping icecast=
|
|
- Check port binding: =docker compose port icecast 8000=
|
|
|
|
*** File Permission Issues
|
|
- Ensure =docker/music/= directory is accessible
|
|
- Check ownership: =ls -la docker/music/=
|
|
- Fix permissions: =sudo chown -R $USER:$USER docker/music/=
|
|
- Verify container volume mounts in =docker-compose.yml=
|
|
- For remote mounts: ensure network storage is accessible
|
|
|
|
*** Music Library Issues
|
|
- Check if music files exist: =find docker/music/ -name "*.mp3" -o -name "*.flac"=
|
|
- Verify supported formats: MP3, FLAC, OGG, WAV
|
|
- Test recursive scanning: =curl -X POST http://localhost:8080/asteroid/api/scan-library=
|
|
- Check database for tracks: =curl http://localhost:8080/asteroid/api/tracks=
|
|
- For large collections: avoid network mounts, use local storage (see memory about 175+ files causing timeouts)
|
|
|
|
** Getting Help
|
|
- Check existing issues in project repository
|
|
- Review RADIANCE framework documentation
|
|
- Consult Liquidsoap manual for streaming issues
|
|
- Join our IRC chat room: **#asteroid.music** on **irc.libera.chat**
|
|
- Ask questions in project discussions
|
|
|
|
This development guide provides the foundation for contributing to Asteroid Radio. For deployment and production considerations, see the Installation Guide and Performance Testing documentation.
|
|
|
|
* Development Stack Links
|
|
|
|
** Core Technologies
|
|
- **SBCL** (Steel Bank Common Lisp): https://www.sbcl.org/
|
|
- **Quicklisp** (Common Lisp package manager): https://www.quicklisp.org/
|
|
- **ASDF** (Another System Definition Facility): https://common-lisp.net/project/asdf/
|
|
|
|
** Web Framework & Libraries
|
|
- **RADIANCE** (Web framework): https://shirakumo.github.io/radiance/
|
|
- **CLIP** (HTML templating): https://shinmera.github.io/clip/
|
|
- **LASS** (CSS in Lisp): https://shinmera.github.io/LASS/
|
|
- **Alexandria** (Utility library): https://alexandria.common-lisp.dev/
|
|
- **Local-Time** (Time handling): https://common-lisp.net/project/local-time/
|
|
|
|
** Audio & Streaming
|
|
- **Docker** (Containerization): https://www.docker.com/
|
|
- **Icecast2** (Streaming server): https://icecast.org/
|
|
- **Liquidsoap** (Audio streaming): https://www.liquidsoap.info/
|
|
- **TagLib** (Audio metadata): https://taglib.org/
|
|
|
|
** Database & Data
|
|
- **cl-json** (JSON handling): https://common-lisp.net/project/cl-json/
|
|
- **cl-fad** (File/directory utilities): https://edicl.github.io/cl-fad/
|
|
- **Ironclad** (Cryptography): https://github.com/sharplispers/ironclad
|
|
- **Babel** (Character encoding): https://common-lisp.net/project/babel/
|
|
|
|
** Development Tools
|
|
- **Emacs** (Editor): https://www.gnu.org/software/emacs/
|
|
- **SLIME** (Emacs Lisp IDE): https://common-lisp.net/project/slime/
|
|
- **Slynk** (SLIME backend): https://github.com/joaotavora/sly
|
|
- **Git** (Version control): https://git-scm.com/
|
|
|
|
** System Libraries
|
|
- **Bordeaux-Threads** (Threading): https://common-lisp.net/project/bordeaux-threads/
|
|
- **Drakma** (HTTP client): https://edicl.github.io/drakma/
|
|
- **CIFS-Utils** (Network file systems): https://wiki.samba.org/index.php/LinuxCIFS_utils
|
|
|
|
** Documentation & Standards
|
|
- **Common Lisp HyperSpec**: http://www.lispworks.com/documentation/HyperSpec/Front/
|
|
- **Docker Compose**: https://docs.docker.com/compose/
|
|
- **Org Mode** (Documentation format): https://orgmode.org/
|