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
This commit is contained in:
parent
78fa57012b
commit
f101ff5757
|
|
@ -44,5 +44,6 @@ docker-compose.yml.backup.*
|
||||||
*.log
|
*.log
|
||||||
logs/
|
logs/
|
||||||
performance-logs/
|
performance-logs/
|
||||||
|
test-results-*.log
|
||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@
|
||||||
|
|
||||||
(defun liquidsoap-queue-track (file-path)
|
(defun liquidsoap-queue-track (file-path)
|
||||||
"Queue a specific track in Liquidsoap"
|
"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)
|
(defun parse-liquidsoap-metadata (metadata-string)
|
||||||
"Parse Liquidsoap metadata string into artist/title/album"
|
"Parse Liquidsoap metadata string into artist/title/album"
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,13 @@ settings.server.telnet.set(true)
|
||||||
settings.server.telnet.port.set(1234)
|
settings.server.telnet.port.set(1234)
|
||||||
settings.server.telnet.bind_addr.set("0.0.0.0")
|
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
|
# Create playlist source from generated M3U file
|
||||||
# This file is managed by Asteroid's stream control system
|
# This file is managed by Asteroid's stream control system
|
||||||
# Falls back to directory scan if playlist file doesn't exist
|
playlist_managed = playlist(
|
||||||
radio = playlist(
|
id="managed_playlist",
|
||||||
mode="normal", # Play in order (not randomized)
|
mode="normal", # Play in order (not randomized)
|
||||||
reload=30, # Check for playlist updates every 30 seconds
|
reload=30, # Check for playlist updates every 30 seconds
|
||||||
reload_mode="seconds", # Reload every N seconds (prevents running out of tracks)
|
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
|
# Fallback to directory scan if playlist file is empty/missing
|
||||||
radio_fallback = playlist.safe(
|
playlist_fallback = playlist.safe(
|
||||||
|
id="music",
|
||||||
mode="randomize",
|
mode="randomize",
|
||||||
reload=3600,
|
reload=3600,
|
||||||
"/app/music/"
|
"/app/music/"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use main playlist, fall back to directory scan
|
# Combine sources: queue (highest priority), managed playlist, then fallback to directory scan
|
||||||
radio = fallback(track_sensitive=false, [radio, radio_fallback])
|
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
|
# Simple crossfade for smooth transitions
|
||||||
radio = crossfade(
|
radio = crossfade(
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ NC='\033[0m' # No Color
|
||||||
BASE_URL="${ASTEROID_URL:-http://localhost:8080}"
|
BASE_URL="${ASTEROID_URL:-http://localhost:8080}"
|
||||||
API_BASE="${BASE_URL}/api/asteroid"
|
API_BASE="${BASE_URL}/api/asteroid"
|
||||||
VERBOSE="${VERBOSE:-0}"
|
VERBOSE="${VERBOSE:-0}"
|
||||||
|
LOG_FILE="test-results-$(date +%Y%m%d-%H%M%S).log"
|
||||||
|
|
||||||
# Test counters
|
# Test counters
|
||||||
TESTS_RUN=0
|
TESTS_RUN=0
|
||||||
|
|
@ -22,28 +23,39 @@ TESTS_PASSED=0
|
||||||
TESTS_FAILED=0
|
TESTS_FAILED=0
|
||||||
|
|
||||||
# Helper functions
|
# 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() {
|
print_header() {
|
||||||
|
local msg="\n========================================\n$1\n========================================\n"
|
||||||
echo -e "\n${BLUE}========================================${NC}"
|
echo -e "\n${BLUE}========================================${NC}"
|
||||||
echo -e "${BLUE}$1${NC}"
|
echo -e "${BLUE}$1${NC}"
|
||||||
echo -e "${BLUE}========================================${NC}\n"
|
echo -e "${BLUE}========================================${NC}\n"
|
||||||
|
log_to_file "$msg"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_test() {
|
print_test() {
|
||||||
echo -e "${YELLOW}TEST:${NC} $1"
|
echo -e "${YELLOW}TEST:${NC} $1"
|
||||||
|
log_to_file "TEST: $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_pass() {
|
print_pass() {
|
||||||
echo -e "${GREEN}✓ PASS:${NC} $1"
|
echo -e "${GREEN}✓ PASS:${NC} $1"
|
||||||
|
log_to_file "✓ PASS: $1"
|
||||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
print_fail() {
|
print_fail() {
|
||||||
echo -e "${RED}✗ FAIL:${NC} $1"
|
echo -e "${RED}✗ FAIL:${NC} $1"
|
||||||
|
log_to_file "✗ FAIL: $1"
|
||||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
print_info() {
|
print_info() {
|
||||||
echo -e "${BLUE}INFO:${NC} $1"
|
echo -e "${BLUE}INFO:${NC} $1"
|
||||||
|
log_to_file "INFO: $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Test function wrapper
|
# Test function wrapper
|
||||||
|
|
@ -212,6 +224,66 @@ test_playlist_endpoints() {
|
||||||
print_info "Note: Playlist creation requires authentication"
|
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 (HTML pages)
|
||||||
test_page_endpoints() {
|
test_page_endpoints() {
|
||||||
print_header "Testing HTML Page Endpoints"
|
print_header "Testing HTML Page Endpoints"
|
||||||
|
|
@ -278,21 +350,42 @@ test_api_format() {
|
||||||
print_summary() {
|
print_summary() {
|
||||||
print_header "Test 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 "Tests Run: $TESTS_RUN"
|
||||||
echo -e "Tests Passed: ${GREEN}$TESTS_PASSED${NC}"
|
echo -e "Tests Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||||
echo -e "Tests Failed: ${RED}$TESTS_FAILED${NC}"
|
echo -e "Tests Failed: ${RED}$TESTS_FAILED${NC}"
|
||||||
|
log_to_file "$summary"
|
||||||
|
|
||||||
if [ $TESTS_FAILED -eq 0 ]; then
|
if [ $TESTS_FAILED -eq 0 ]; then
|
||||||
echo -e "\n${GREEN}✓ All tests passed!${NC}\n"
|
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
|
exit 0
|
||||||
else
|
else
|
||||||
echo -e "\n${RED}✗ Some tests failed${NC}\n"
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main test execution
|
# Main test execution
|
||||||
main() {
|
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 -e "${BLUE}"
|
||||||
echo "╔═══════════════════════════════════════╗"
|
echo "╔═══════════════════════════════════════╗"
|
||||||
echo "║ Asteroid Radio Server Test Suite ║"
|
echo "║ Asteroid Radio Server Test Suite ║"
|
||||||
|
|
@ -301,6 +394,7 @@ main() {
|
||||||
|
|
||||||
print_info "Testing server at: ${BASE_URL}"
|
print_info "Testing server at: ${BASE_URL}"
|
||||||
print_info "Verbose mode: ${VERBOSE}"
|
print_info "Verbose mode: ${VERBOSE}"
|
||||||
|
print_info "Log file: ${LOG_FILE}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Run all test suites
|
# Run all test suites
|
||||||
|
|
@ -310,6 +404,7 @@ main() {
|
||||||
test_track_endpoints
|
test_track_endpoints
|
||||||
test_player_endpoints
|
test_player_endpoints
|
||||||
test_playlist_endpoints
|
test_playlist_endpoints
|
||||||
|
test_dj_control_endpoints
|
||||||
test_admin_endpoints
|
test_admin_endpoints
|
||||||
test_page_endpoints
|
test_page_endpoints
|
||||||
test_static_files
|
test_static_files
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue