audio / public /script.js
vish85521's picture
Update public/script.js
cabf7d8 verified
Raw
History Blame Contribute Delete
6.7 kB
const socket = io();
const chatBox = document.getElementById('chat-box');
const chatInput = document.getElementById('chat-input');
const sendBtn = document.getElementById('send-btn');
const joinBtn = document.getElementById('join-btn');
const statusText = document.getElementById('status');
const audioContainer = document.getElementById('audio-container');
let localStream;
const peers = {}; // Store connections to other users
// --- WebRTC Configuration ---
// Using Google STUN and your dedicated Metered.live TURN server
const rtcConfig = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' }, // Backup STUN
{
urls: 'turn:saman.metered.live:80',
username: '1bb6c42832c6a26b4db391e4',
credential: 'axHWrYuQbsBaG5u9'
},
{
urls: 'turn:saman.metered.live:443',
username: '1bb6c42832c6a26b4db391e4',
credential: 'axHWrYuQbsBaG5u9'
},
{
urls: 'turn:saman.metered.live:443?transport=tcp',
username: '1bb6c42832c6a26b4db391e4',
credential: 'axHWrYuQbsBaG5u9'
}
]
};
// --- CHAT LOGIC ---
sendBtn.onclick = () => {
const text = chatInput.value.trim();
if (text) {
socket.emit('chat-message', text);
chatInput.value = '';
}
};
socket.on('chat-message', (data) => {
const div = document.createElement('div');
div.className = 'message ' + (data.id === socket.id ? 'self' : '');
div.innerText = `${data.id === socket.id ? 'You' : data.id.substring(0,4)}: ${data.text}`;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
});
// Allow hitting "Enter" to send a chat message
chatInput.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
event.preventDefault();
sendBtn.click();
}
});
// --- AUDIO/WEBRTC LOGIC ---
joinBtn.onclick = async () => {
try {
// Request microphone access
localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
statusText.innerText = "Mic Online - You are in the call";
joinBtn.disabled = true;
// Let the server know we are ready to connect to others
socket.emit('join-room');
} catch (err) {
alert("Microphone access denied or not found.");
console.error(err);
}
};
// When a new user connects, we (if we have our mic on) initiate a call to them
socket.on('user-connected', (userId) => {
if (!localStream) return; // Don't call if we aren't in the audio room
appendSystemMessage(`User ${userId.substring(0,4)} joined. Attempting connection...`);
const peer = createPeerConnection(userId, true);
peers[userId] = peer;
});
// Handle incoming signals (Offers, Answers, ICE candidates)
socket.on('signal', async (data) => {
if (!localStream) return;
const { from, signal } = data;
// If we don't have a peer connection for this user yet, create one
if (!peers[from]) {
peers[from] = createPeerConnection(from, false);
}
const peer = peers[from];
try {
if (signal.type === 'offer') {
await peer.setRemoteDescription(new RTCSessionDescription(signal));
const answer = await peer.createAnswer();
await peer.setLocalDescription(answer);
socket.emit('signal', { to: from, signal: peer.localDescription });
} else if (signal.type === 'answer') {
await peer.setRemoteDescription(new RTCSessionDescription(signal));
} else if (signal.candidate) {
await peer.addIceCandidate(new RTCIceCandidate(signal));
}
} catch (err) {
console.error("Signaling error:", err);
}
});
// Clean up when someone leaves
socket.on('user-disconnected', (userId) => {
if (peers[userId]) {
peers[userId].close();
delete peers[userId];
// Remove their audio element
const audioEl = document.getElementById(`audio-${userId}`);
if (audioEl) audioEl.remove();
appendSystemMessage(`User ${userId.substring(0,4)} left.`);
}
});
// Helper function to create a WebRTC Peer Connection
function createPeerConnection(targetUserId, isInitiator) {
const peer = new RTCPeerConnection(rtcConfig);
// Add our local microphone stream to the connection
localStream.getTracks().forEach(track => peer.addTrack(track, localStream));
// Send ICE candidates to the other user
peer.onicecandidate = (event) => {
if (event.candidate) {
socket.emit('signal', { to: targetUserId, signal: event.candidate });
}
};
// TRACK THE CONNECTION STATUS
peer.oniceconnectionstatechange = () => {
console.log(`Connection state with ${targetUserId}:`, peer.iceConnectionState);
if (peer.iceConnectionState === 'failed') {
appendSystemMessage(`❌ Connection to ${targetUserId.substring(0,4)} failed. Firewalls blocked it.`);
} else if (peer.iceConnectionState === 'connected') {
appendSystemMessage(`✅ Successfully connected to ${targetUserId.substring(0,4)}! Audio flowing.`);
} else if (peer.iceConnectionState === 'disconnected') {
appendSystemMessage(`⚠️ Connection to ${targetUserId.substring(0,4)} lost. Trying to reconnect...`);
}
};
// When we receive their audio stream, play it
peer.ontrack = (event) => {
let audioEl = document.getElementById(`audio-${targetUserId}`);
if (!audioEl) {
audioEl = document.createElement('audio');
audioEl.id = `audio-${targetUserId}`;
audioEl.autoplay = true;
audioContainer.appendChild(audioEl);
}
audioEl.srcObject = event.streams[0];
// Force the browser to play the audio and catch any autoplay errors
audioEl.play().catch(error => {
console.error("Browser blocked autoplay:", error);
appendSystemMessage("Audio blocked by browser. Click anywhere on the page to allow audio.");
});
};
// If we are initiating the call, create an offer
if (isInitiator) {
peer.createOffer().then(offer => {
peer.setLocalDescription(offer);
socket.emit('signal', { to: targetUserId, signal: offer });
});
}
return peer;
}
function appendSystemMessage(msg) {
const div = document.createElement('div');
div.className = 'message system';
div.innerText = msg;
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
}