From f101ff5757d1c23bfe6be589e5c897dd0fc13ed7 Mon Sep 17 00:00:00 2001 From: glenneth Date: Sun, 12 Oct 2025 06:34:37 +0300 Subject: [PATCH] feat: Add Liquidsoap DJ controls via telnet integration - Implement 5 DJ control API endpoints (skip, reload, queue, metadata, queue-status) - Update Liquidsoap config to support dynamic track queuing with request.queue - Add comprehensive DJ control tests to test-server.sh with dynamic track ID fetching - Add test result logging with timestamped log files - Update .gitignore to exclude test result logs - Change liquidsoap-queue-track to use queue.push command --- .gitignore | 1 + asteroid.lisp | 2 +- docker/asteroid-radio-docker.liq | 18 ++++-- test-server.sh | 95 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index dc0837c..d68eddd 100644 --- a/.gitignore +++ b/.gitignore @@ -44,5 +44,6 @@ docker-compose.yml.backup.* *.log logs/ performance-logs/ +test-results-*.log # Temporary files diff --git a/asteroid.lisp b/asteroid.lisp index 6b6b6b7..9d2d960 100644 --- a/asteroid.lisp +++ b/asteroid.lisp @@ -334,7 +334,7 @@ (defun liquidsoap-queue-track (file-path) "Queue a specific track in Liquidsoap" - (liquidsoap-telnet-command (format nil "request.push ~A" file-path))) + (liquidsoap-telnet-command (format nil "queue.push ~A" file-path))) (defun parse-liquidsoap-metadata (metadata-string) "Parse Liquidsoap metadata string into artist/title/album" diff --git a/docker/asteroid-radio-docker.liq b/docker/asteroid-radio-docker.liq index 953b67e..1f817de 100644 --- a/docker/asteroid-radio-docker.liq +++ b/docker/asteroid-radio-docker.liq @@ -19,10 +19,13 @@ settings.server.telnet.set(true) settings.server.telnet.port.set(1234) settings.server.telnet.bind_addr.set("0.0.0.0") +# Create request queue for dynamic track queuing (DJ controls) +queue = request.queue(id="queue") + # Create playlist source from generated M3U file # This file is managed by Asteroid's stream control system -# Falls back to directory scan if playlist file doesn't exist -radio = playlist( +playlist_managed = playlist( + id="managed_playlist", mode="normal", # Play in order (not randomized) reload=30, # Check for playlist updates every 30 seconds reload_mode="seconds", # Reload every N seconds (prevents running out of tracks) @@ -30,14 +33,19 @@ radio = playlist( ) # Fallback to directory scan if playlist file is empty/missing -radio_fallback = playlist.safe( +playlist_fallback = playlist.safe( + id="music", mode="randomize", reload=3600, "/app/music/" ) -# Use main playlist, fall back to directory scan -radio = fallback(track_sensitive=false, [radio, radio_fallback]) +# Combine sources: queue (highest priority), managed playlist, then fallback to directory scan +radio = fallback(track_sensitive=true, [queue, playlist_managed, playlist_fallback]) + +# Add some audio processing +radio = amplify(1.0, radio) +radio = normalize(radio) # Simple crossfade for smooth transitions radio = crossfade( diff --git a/test-server.sh b/test-server.sh index f57c85a..0ce72db 100755 --- a/test-server.sh +++ b/test-server.sh @@ -15,6 +15,7 @@ NC='\033[0m' # No Color BASE_URL="${ASTEROID_URL:-http://localhost:8080}" API_BASE="${BASE_URL}/api/asteroid" VERBOSE="${VERBOSE:-0}" +LOG_FILE="test-results-$(date +%Y%m%d-%H%M%S).log" # Test counters TESTS_RUN=0 @@ -22,28 +23,39 @@ TESTS_PASSED=0 TESTS_FAILED=0 # Helper functions +log_to_file() { + # Strip ANSI color codes for log file + echo "$1" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE" +} + print_header() { + local msg="\n========================================\n$1\n========================================\n" echo -e "\n${BLUE}========================================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}========================================${NC}\n" + log_to_file "$msg" } print_test() { echo -e "${YELLOW}TEST:${NC} $1" + log_to_file "TEST: $1" } print_pass() { echo -e "${GREEN}✓ PASS:${NC} $1" + log_to_file "✓ PASS: $1" TESTS_PASSED=$((TESTS_PASSED + 1)) } print_fail() { echo -e "${RED}✗ FAIL:${NC} $1" + log_to_file "✗ FAIL: $1" TESTS_FAILED=$((TESTS_FAILED + 1)) } print_info() { echo -e "${BLUE}INFO:${NC} $1" + log_to_file "INFO: $1" } # Test function wrapper @@ -212,6 +224,66 @@ test_playlist_endpoints() { print_info "Note: Playlist creation requires authentication" } +# Test DJ Control Endpoints (Liquidsoap telnet integration) +test_dj_control_endpoints() { + print_header "Testing DJ Control Endpoints" + + print_info "Note: DJ control endpoints require admin authentication" + + # Test metadata endpoint (available to authenticated users) + test_api_endpoint "/dj/metadata" \ + "DJ metadata endpoint" \ + "metadata" + + # Test queue status endpoint + test_api_endpoint "/dj/queue-status" \ + "DJ queue status endpoint" \ + "queue" + + # Test skip track endpoint (requires admin) + run_test "DJ skip track endpoint" + local skip_response=$(curl -s -X POST "${API_BASE}/dj/skip") + if echo "$skip_response" | grep -q "status"; then + print_pass "DJ skip track endpoint responds" + else + print_fail "DJ skip track endpoint not responding" + fi + + # Test reload playlist endpoint (requires admin) + run_test "DJ reload playlist endpoint" + local reload_response=$(curl -s -X POST "${API_BASE}/dj/reload-playlist") + if echo "$reload_response" | grep -q "status"; then + print_pass "DJ reload playlist endpoint responds" + else + print_fail "DJ reload playlist endpoint not responding" + fi + + # Test queue track endpoint with dynamic track ID + run_test "DJ queue track endpoint" + + # Fetch a random track ID from the database + local track_response=$(curl -s "${API_BASE}/tracks?page=1&per-page=1") + + # Extract track ID using grep (works without jq) + local track_id=$(echo "$track_response" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*') + + if [ -n "$track_id" ]; then + print_info "Using track ID: $track_id for queue test" + local queue_response=$(curl -s -X POST "${API_BASE}/dj/queue?track-id=${track_id}") + + if echo "$queue_response" | grep -q "queued successfully\|Track not found"; then + print_pass "DJ queue track endpoint responds (track ID: $track_id)" + else + print_fail "DJ queue track endpoint not responding properly" + if [ $VERBOSE -eq 1 ]; then + echo "Response: $queue_response" + fi + fi + else + print_info "No tracks available in database, skipping queue test" + fi +} + # Test Page Endpoints (HTML pages) test_page_endpoints() { print_header "Testing HTML Page Endpoints" @@ -278,21 +350,42 @@ test_api_format() { print_summary() { print_header "Test Summary" + local summary="Tests Run: $TESTS_RUN\nTests Passed: $TESTS_PASSED\nTests Failed: $TESTS_FAILED" + echo "Tests Run: $TESTS_RUN" echo -e "Tests Passed: ${GREEN}$TESTS_PASSED${NC}" echo -e "Tests Failed: ${RED}$TESTS_FAILED${NC}" + log_to_file "$summary" if [ $TESTS_FAILED -eq 0 ]; then echo -e "\n${GREEN}✓ All tests passed!${NC}\n" + log_to_file "\n✓ All tests passed!\n" + echo -e "${BLUE}Log file saved to: ${LOG_FILE}${NC}\n" exit 0 else echo -e "\n${RED}✗ Some tests failed${NC}\n" + log_to_file "\n✗ Some tests failed\n" + echo -e "${BLUE}Log file saved to: ${LOG_FILE}${NC}\n" exit 1 fi } # Main test execution main() { + # Initialize log file with header + cat > "$LOG_FILE" << EOF +╔═══════════════════════════════════════════════════════════════╗ +║ Asteroid Radio Server Test Suite ║ +║ Test Results Log ║ +╚═══════════════════════════════════════════════════════════════╝ + +Test Run Date: $(date '+%Y-%m-%d %H:%M:%S') +Server URL: ${BASE_URL} +API Base: ${API_BASE} +Verbose Mode: ${VERBOSE} + +EOF + echo -e "${BLUE}" echo "╔═══════════════════════════════════════╗" echo "║ Asteroid Radio Server Test Suite ║" @@ -301,6 +394,7 @@ main() { print_info "Testing server at: ${BASE_URL}" print_info "Verbose mode: ${VERBOSE}" + print_info "Log file: ${LOG_FILE}" echo "" # Run all test suites @@ -310,6 +404,7 @@ main() { test_track_endpoints test_player_endpoints test_playlist_endpoints + test_dj_control_endpoints test_admin_endpoints test_page_endpoints test_static_files