feat: scripts portables et API détection devices audio
1. API /admin/devices/list - Auto-détection devices audio macOS (sox) - Support Linux (JACK/PipeWire/PulseAudio) - Fallback Windows (placeholder Phase 3) 2. Scripts d'installation multi-OS - install.sh : détection OS automatique - install/linux.sh : génération .env auto (comme macOS) - Messages améliorés avec IP détectée 3. Script start.sh unifié - Lance serveur + client (dev ou prod) - Détection IP réseau au démarrage - Modes : ./start.sh (prod) ou ./start.sh --dev - Cleanup propre (trap SIGINT/SIGTERM) Améliore drastiquement la portabilité du projet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PTT Live - Script d'installation multi-OS
|
||||
# Détecte automatiquement le système et lance l'installeur approprié
|
||||
|
||||
set -e
|
||||
|
||||
# Couleurs
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}=================================="
|
||||
echo "🚀 PTT Live - Installation"
|
||||
echo "==================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Détection du système d'exploitation
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo -e "${GREEN}📱 Système détecté : macOS${NC}"
|
||||
echo ""
|
||||
exec ./install/macos.sh
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
echo -e "${GREEN}🐧 Système détecté : Linux${NC}"
|
||||
echo ""
|
||||
exec ./install/linux.sh
|
||||
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
|
||||
echo -e "${YELLOW}🪟 Système détecté : Windows${NC}"
|
||||
echo ""
|
||||
echo -e "${RED}❌ Windows n'est pas encore supporté (Phase 3)${NC}"
|
||||
echo ""
|
||||
echo "Plateformes supportées :"
|
||||
echo " • macOS (via Homebrew)"
|
||||
echo " • Linux (Debian/Ubuntu/Fedora)"
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
echo -e "${RED}❌ Système non reconnu : $OSTYPE${NC}"
|
||||
echo ""
|
||||
echo "Plateformes supportées :"
|
||||
echo " • macOS"
|
||||
echo " • Linux"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
+69
-5
@@ -222,6 +222,61 @@ install_node_deps() {
|
||||
echo "Dépendances Node.js installées !"
|
||||
}
|
||||
|
||||
# Configuration réseau et génération .env
|
||||
configure_network() {
|
||||
echo ""
|
||||
echo "Configuration réseau..."
|
||||
|
||||
# Détection IP réseau
|
||||
NETWORK_IP=$(hostname -I | awk '{print $1}')
|
||||
|
||||
if [ -z "$NETWORK_IP" ]; then
|
||||
echo "⚠️ IP réseau non détectée, utilisation localhost"
|
||||
NETWORK_IP="localhost"
|
||||
else
|
||||
echo "✓ IP réseau détectée : ${NETWORK_IP}"
|
||||
fi
|
||||
|
||||
# Générer .env serveur
|
||||
echo "Génération configuration serveur..."
|
||||
|
||||
cat > "$PROJECT_ROOT/server/.env" << EOF
|
||||
# Configuration PTT Live Server
|
||||
# Généré automatiquement par install/linux.sh
|
||||
|
||||
USE_LOCAL_LIVEKIT=true
|
||||
|
||||
# LiveKit Configuration
|
||||
# AUTO = détection automatique IP réseau au démarrage
|
||||
LIVEKIT_URL=AUTO
|
||||
# En mode --dev, LiveKit utilise ces clés par défaut
|
||||
LIVEKIT_API_KEY=devkey
|
||||
LIVEKIT_API_SECRET=secret
|
||||
|
||||
# Server Configuration
|
||||
PORT=3000
|
||||
NODE_ENV=development
|
||||
EOF
|
||||
|
||||
echo "✓ Configuration serveur générée (server/.env)"
|
||||
|
||||
# Générer .env client
|
||||
echo "Génération configuration client..."
|
||||
|
||||
cat > "$PROJECT_ROOT/client/.env" << EOF
|
||||
# Configuration PTT Live Client
|
||||
# Généré automatiquement par install/linux.sh
|
||||
|
||||
# En développement local, utilise le proxy Vite
|
||||
VITE_API_URL=/api
|
||||
|
||||
# Pour accès réseau (autres devices), décommentez et mettez l'IP du serveur :
|
||||
# VITE_API_URL=http://${NETWORK_IP}:3000
|
||||
EOF
|
||||
|
||||
echo "✓ Configuration client générée (client/.env)"
|
||||
}
|
||||
|
||||
# Configuration audio
|
||||
configure_audio() {
|
||||
echo ""
|
||||
@@ -259,10 +314,10 @@ configure_audio() {
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " Installation terminée !"
|
||||
echo " ✅ Installation terminée !"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Prochaines étapes :"
|
||||
echo "📝 Prochaines étapes :"
|
||||
echo ""
|
||||
echo "1. Démarrer le serveur :"
|
||||
echo " cd $PROJECT_ROOT/server"
|
||||
@@ -272,10 +327,18 @@ print_summary() {
|
||||
echo " cd $PROJECT_ROOT/client"
|
||||
echo " npm run dev"
|
||||
echo ""
|
||||
echo "3. Accéder à l'interface :"
|
||||
echo " http://localhost:5173"
|
||||
echo "3. Accéder à l'application :"
|
||||
echo " • Développement local : http://localhost:5173"
|
||||
echo " • Depuis autre appareil (WiFi) : http://${NETWORK_IP}:5173"
|
||||
echo ""
|
||||
echo "💡 Configuration réseau :"
|
||||
echo " IP serveur détectée : ${NETWORK_IP}"
|
||||
echo " LiveKit URL : AUTO (détection dynamique)"
|
||||
echo ""
|
||||
echo "📖 Documentation :"
|
||||
echo " • README.md - Guide complet"
|
||||
echo " • README-PORTABLE.md - Déploiement portable"
|
||||
echo ""
|
||||
echo "Documentation : $PROJECT_ROOT/README.md"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
}
|
||||
@@ -286,6 +349,7 @@ main() {
|
||||
install_system_deps
|
||||
install_livekit_server
|
||||
install_node_deps
|
||||
configure_network
|
||||
configure_audio
|
||||
print_summary
|
||||
}
|
||||
|
||||
@@ -664,4 +664,124 @@ router.post('/audio/device', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /admin/devices/list
|
||||
* Liste tous les devices audio disponibles (auto-détection)
|
||||
* Supporte macOS (CoreAudio), Linux (JACK/PipeWire), Windows (WASAPI)
|
||||
*/
|
||||
router.get('/devices/list', async (req, res) => {
|
||||
try {
|
||||
const devices = {
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
platform: process.platform
|
||||
};
|
||||
|
||||
// Détection selon la plateforme
|
||||
if (process.platform === 'darwin') {
|
||||
// macOS : utiliser CoreAudio via sox
|
||||
const { exec } = await import('child_process');
|
||||
const { promisify } = await import('util');
|
||||
const execPromise = promisify(exec);
|
||||
|
||||
try {
|
||||
// Utiliser sox pour lister les devices audio
|
||||
const { stdout } = await execPromise('sox -V6 2>&1');
|
||||
|
||||
// Parser la sortie sox pour extraire les devices
|
||||
// Format typique : "Input Device [0]: MacBook Pro Microphone"
|
||||
const inputMatches = stdout.matchAll(/Input Device \[(\d+)\]: (.+)/g);
|
||||
const outputMatches = stdout.matchAll(/Output Device \[(\d+)\]: (.+)/g);
|
||||
|
||||
for (const match of inputMatches) {
|
||||
devices.inputs.push({
|
||||
id: parseInt(match[1], 10),
|
||||
name: match[2].trim()
|
||||
});
|
||||
}
|
||||
|
||||
for (const match of outputMatches) {
|
||||
devices.outputs.push({
|
||||
id: parseInt(match[1], 10),
|
||||
name: match[2].trim()
|
||||
});
|
||||
}
|
||||
} catch (soxError) {
|
||||
console.warn('⚠️ sox non disponible, devices limités:', soxError.message);
|
||||
|
||||
// Fallback : devices par défaut macOS
|
||||
devices.inputs.push({ id: 0, name: 'Default Input (Built-in Microphone)', isDefault: true });
|
||||
devices.outputs.push({ id: 0, name: 'Default Output (Built-in Speakers)', isDefault: true });
|
||||
}
|
||||
|
||||
} else if (process.platform === 'linux') {
|
||||
// Linux : JACK ou PipeWire
|
||||
const { exec } = await import('child_process');
|
||||
const { promisify } = await import('util');
|
||||
const execPromise = promisify(exec);
|
||||
|
||||
try {
|
||||
// Essayer JACK d'abord
|
||||
const { stdout: jackPorts } = await execPromise('jack_lsp 2>/dev/null || echo ""');
|
||||
|
||||
if (jackPorts.trim()) {
|
||||
// Parser les ports JACK
|
||||
const ports = jackPorts.split('\n').filter(Boolean);
|
||||
|
||||
ports.forEach(port => {
|
||||
if (port.includes('capture')) {
|
||||
devices.inputs.push({ id: port, name: port });
|
||||
} else if (port.includes('playback')) {
|
||||
devices.outputs.push({ id: port, name: port });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback : PipeWire via pactl
|
||||
const { stdout: paDevices } = await execPromise('pactl list short sources 2>/dev/null || echo ""');
|
||||
const { stdout: paSinks } = await execPromise('pactl list short sinks 2>/dev/null || echo ""');
|
||||
|
||||
if (paDevices.trim()) {
|
||||
paDevices.split('\n').filter(Boolean).forEach((line, idx) => {
|
||||
const name = line.split('\t')[1] || `Device ${idx}`;
|
||||
devices.inputs.push({ id: idx, name });
|
||||
});
|
||||
}
|
||||
|
||||
if (paSinks.trim()) {
|
||||
paSinks.split('\n').filter(Boolean).forEach((line, idx) => {
|
||||
const name = line.split('\t')[1] || `Device ${idx}`;
|
||||
devices.outputs.push({ id: idx, name });
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (linuxError) {
|
||||
console.warn('⚠️ Détection devices Linux échouée:', linuxError.message);
|
||||
devices.inputs.push({ id: 0, name: 'Default Input', isDefault: true });
|
||||
devices.outputs.push({ id: 0, name: 'Default Output', isDefault: true });
|
||||
}
|
||||
|
||||
} else if (process.platform === 'win32') {
|
||||
// Windows : WASAPI (Phase 3)
|
||||
// TODO: implémenter détection WASAPI
|
||||
devices.inputs.push({ id: 0, name: 'Default Input (Windows)', isDefault: true });
|
||||
devices.outputs.push({ id: 0, name: 'Default Output (Windows)', isDefault: true });
|
||||
}
|
||||
|
||||
addLog('info', 'Audio devices listed', {
|
||||
inputsCount: devices.inputs.length,
|
||||
outputsCount: devices.outputs.length
|
||||
});
|
||||
|
||||
res.json(devices);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur GET /admin/devices/list:', error);
|
||||
res.status(500).json({
|
||||
error: 'Failed to list audio devices',
|
||||
message: error.message,
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PTT Live - Script de démarrage unifié
|
||||
# Lance le serveur et le client en mode production
|
||||
|
||||
set -e
|
||||
|
||||
# Couleurs
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Détection IP réseau
|
||||
get_network_ip() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n 1
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
# Linux
|
||||
hostname -I | awk '{print $1}'
|
||||
else
|
||||
echo "localhost"
|
||||
fi
|
||||
}
|
||||
|
||||
NETWORK_IP=$(get_network_ip)
|
||||
|
||||
echo -e "${BLUE}=================================="
|
||||
echo "🚀 PTT Live - Démarrage"
|
||||
echo "==================================${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}📡 IP réseau détectée : ${NETWORK_IP}${NC}"
|
||||
echo ""
|
||||
|
||||
# Vérifier que les dépendances sont installées
|
||||
if [ ! -d "server/node_modules" ]; then
|
||||
echo -e "${RED}❌ Dépendances serveur manquantes${NC}"
|
||||
echo " Exécutez d'abord : ./install/macos.sh (ou linux.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "client/node_modules" ]; then
|
||||
echo -e "${RED}❌ Dépendances client manquantes${NC}"
|
||||
echo " Exécutez d'abord : ./install/macos.sh (ou linux.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Créer fichier PID pour cleanup
|
||||
PID_FILE="/tmp/ptt-live.pid"
|
||||
|
||||
# Fonction cleanup
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo -e "${YELLOW}⏹ Arrêt PTT Live...${NC}"
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
while read -r pid; do
|
||||
if ps -p "$pid" > /dev/null 2>&1; then
|
||||
kill "$pid" 2>/dev/null || true
|
||||
fi
|
||||
done < "$PID_FILE"
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Arrêté${NC}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup SIGINT SIGTERM EXIT
|
||||
|
||||
# Démarrer le serveur en arrière-plan
|
||||
echo -e "${BLUE}🔧 Démarrage serveur...${NC}"
|
||||
cd server
|
||||
npm start > ../server.log 2>&1 &
|
||||
SERVER_PID=$!
|
||||
echo "$SERVER_PID" > "$PID_FILE"
|
||||
cd ..
|
||||
|
||||
echo -e "${GREEN}✓ Serveur démarré (PID: $SERVER_PID)${NC}"
|
||||
|
||||
# Attendre que le serveur soit prêt
|
||||
echo -e "${YELLOW}⏳ Attente démarrage serveur...${NC}"
|
||||
for i in {1..30}; do
|
||||
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Serveur prêt${NC}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ $i -eq 30 ]; then
|
||||
echo -e "${RED}❌ Timeout : le serveur n'a pas démarré${NC}"
|
||||
echo " Consultez server.log pour plus de détails"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# Build client si pas déjà fait ou mode dev
|
||||
if [ "$1" == "--dev" ]; then
|
||||
echo -e "${BLUE}🎨 Démarrage client (dev)...${NC}"
|
||||
cd client
|
||||
npm run dev &
|
||||
CLIENT_PID=$!
|
||||
echo "$CLIENT_PID" >> "$PID_FILE"
|
||||
cd ..
|
||||
|
||||
echo -e "${GREEN}✓ Client dev démarré${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}=================================="
|
||||
echo "✅ PTT Live démarré (mode dev)"
|
||||
echo "==================================${NC}"
|
||||
echo ""
|
||||
echo "🌐 Accès :"
|
||||
echo " • Local : https://localhost:5173"
|
||||
echo " • Réseau : https://${NETWORK_IP}:5173"
|
||||
echo ""
|
||||
echo "📊 API serveur : http://${NETWORK_IP}:3000"
|
||||
echo "🎛️ Interface admin : http://${NETWORK_IP}:3000/admin"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Appuyez sur Ctrl+C pour arrêter${NC}"
|
||||
echo ""
|
||||
|
||||
# Attendre indéfiniment
|
||||
wait
|
||||
|
||||
else
|
||||
# Mode production : build et serve
|
||||
echo -e "${BLUE}🎨 Build client production...${NC}"
|
||||
cd client
|
||||
|
||||
if [ ! -d "dist" ] || [ "$1" == "--rebuild" ]; then
|
||||
npm run build
|
||||
echo -e "${GREEN}✓ Client buildé${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Build existant utilisé (--rebuild pour forcer)${NC}"
|
||||
fi
|
||||
|
||||
cd ..
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}=================================="
|
||||
echo "✅ PTT Live démarré (production)"
|
||||
echo "==================================${NC}"
|
||||
echo ""
|
||||
echo "🌐 Accès :"
|
||||
echo " • Local : http://localhost:3000"
|
||||
echo " • Réseau : http://${NETWORK_IP}:3000"
|
||||
echo ""
|
||||
echo "🎛️ Interface admin : http://${NETWORK_IP}:3000/admin"
|
||||
echo ""
|
||||
echo "📝 Logs serveur : tail -f server.log"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Appuyez sur Ctrl+C pour arrêter${NC}"
|
||||
echo ""
|
||||
|
||||
# Attendre indéfiniment
|
||||
wait
|
||||
fi
|
||||
Reference in New Issue
Block a user