Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions HOST_REASSIGNMENT_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Host Reassignment Feature - Implementation Summary

## Overview
This implementation adds automatic host reassignment when the current host leaves or gets disconnected from a JamSesh session. Previously, when the host left, the entire session would close. Now, the session continues with a new host automatically promoted.

## Changes Made

### 1. Server-side Changes (`signaling-server/server.js`)

#### Track Host in Room
- Modified room object to include `hostId` field to track the current host
- Updated `create_room` to initialize `hostId: null`

#### Join Room with Role
- Modified `joinroom` handler to accept and track user roles ('host' or 'join')
- Automatically set the first user or user with role='host' as the host
- Added `isHost` flag to participant objects
- Server now sends `hostId` and `isHost` in the `join_success` response

#### Host Reassignment on Disconnect
- Enhanced `handleClientDisconnect()` to detect when the host leaves
- When host disconnects and participants remain:
- Promotes the first remaining participant to host
- Updates room's `hostId` to the new host
- Sends `host-promoted` message to all remaining clients with:
- `newHostId`: ID of the new host
- `newHostUsername`: Username of the new host
- `isYou`: Boolean indicating if this client is the new host

#### Host-Only Actions
- Modified `start-call` and `end-call` handlers to only accept commands from the current host
- Prevents non-host participants from controlling the session

### 2. Host Client Changes (`public/js/host.js`)

#### Send Role Information
- Modified `init` case to send `role: 'host'` when joining a room
- This ensures the server recognizes them as the host

#### Handle Host Promotion Notification
- Added `host-promoted` case handler
- Shows alert when another participant is promoted to host
- Logs the new host information for debugging

### 3. Join Client Changes (`public/js/join.js`)

#### Variable Declarations
- Added `localStream` variable for when a joiner becomes host
- Added `roomCode` variable stored globally for host functionality
- Modified `init` case to store roomCode globally

#### Handle Being Promoted to Host
- Added comprehensive `host-promoted` case handler
- When promoted (`isYou: true`):
- Shows alert notification to user
- Enables the start button with updated text
- Adds event listener to acquire media and start streaming
- When new host clicks start:
- Acquires audio via `getDisplayMedia()` (same as original host)
- Sends `start-call` message to server
- Creates WebRTC offers to all other participants
- Manages peer connections and audio streaming

#### Add Answer Handler
- Added `answer` case to handle responses when promoted host sends offers
- Sets remote description and establishes peer connections
- Starts network quality monitoring for each peer

#### Helper Function for New Host
- Added `createAndSendOfferAsNewHost()` function
- Similar to host.js's `createAndSendOffer()`
- Creates peer connections with high-quality audio settings
- Sends WebRTC offers to other participants
- Configures audio encoding parameters for optimal quality

## How It Works

### Flow When Host Leaves:

1. **Server detects host disconnect**
- `handleClientDisconnect()` identifies the leaving user was the host

2. **Server promotes new host**
- Selects first remaining participant (index 0 in clients array)
- Updates room's `hostId`
- Marks new host with `isHost: true`

3. **Server notifies all clients**
- Sends `host-promoted` message to everyone
- Each client knows who the new host is
- New host knows they were promoted (`isYou: true`)

4. **New host prepares to stream**
- UI updates: start button enabled with "You are Host" text
- User sees alert: "You are now the host!"
- When ready, new host clicks start button

5. **New host starts streaming**
- Acquires audio stream via `getDisplayMedia()`
- Creates WebRTC peer connections to all other participants
- Sends audio tracks to everyone
- Session continues seamlessly

### Benefits:
- ✅ No session interruption when host leaves
- ✅ Automatic promotion of next participant
- ✅ Clear notification to new host
- ✅ Existing participants can continue listening
- ✅ New host can control session (start/end)
- ✅ Works with existing WebRTC infrastructure

## Testing Instructions

### Manual Test:
1. Start the signaling server: `node signaling-server/server.js`
2. Open browser and create a room as host
3. Join the room from another browser/tab as participant
4. Start the jam session from host
5. Close/disconnect the host browser
6. Verify:
- Participant receives "You are now the host!" alert
- Start button is enabled for new host
- New host can click start and stream audio
- Session continues without closing

## Files Modified
- `signaling-server/server.js` - Server logic for host tracking and reassignment
- `public/js/host.js` - Send role info and handle promotion notifications
- `public/js/join.js` - Handle becoming host, acquire media, create connections

## Future Enhancements
- UI indicator showing who the current host is
- Host transfer button for voluntary handoff
- Host selection preferences (e.g., by seniority)
- Notification history of host changes
99 changes: 99 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Host Reassignment Testing Guide

## How to Test the Fix

### Setup
1. Server is running on `http://localhost:8080`
2. Open browser console (F12) to see debug logs
3. You need TWO browser tabs/windows

### Test Scenario 1: Host Ends Jam
1. **Tab 1 (Host):**
- Go to http://localhost:8080
- Enter username: "Host1"
- Click "Host a Jam"
- Copy the room code
- Click "Start Jam" and share audio

2. **Tab 2 (Participant):**
- Go to http://localhost:8080
- Enter username: "Participant1"
- Click "Join a Jam"
- Enter the room code from Tab 1
- You should hear the host's audio

3. **Tab 1 (Host):**
- Click "End Jam" button

4. **Expected Result in Tab 2:**
- ✅ Alert popup: "You are now the host!"
- ✅ UI changes:
- "Live Stream" section disappears
- "You (Host Stream)" section appears
- "Exit" button disappears
- "Start Jam (You are Host)" button appears
- "End Jam" button appears (disabled)
- ✅ Console logs show:
- "🎉 You have been promoted to host!"
- "✅ Showed local audio wrapper"
- "✅ Hid remote audio wrapper"
- "✅ Enabled start button"
- "✅ Hid exit button"

5. **Tab 2 (New Host):**
- Click "Start Jam (You are Host)"
- Share your audio
- ✅ Should start streaming

### Test Scenario 2: Host Disconnects
1. Set up same as Scenario 1 (Steps 1-2)
2. **Tab 1 (Host):**
- Close the tab/window completely (or press Ctrl+W)

3. **Expected Result in Tab 2:**
- Same as Scenario 1, step 4

### What to Check in Browser Console

#### Server Logs (terminal):
```
Host [id] ended the call in room [code]
Promoting [id] ([username]) to host in room [code]
Sending host-promoted to [username] ([id]): { type: 'host-promoted', ... }
```

#### Client Logs (browser console Tab 2):
```
🎉 You have been promoted to host!
Promotion data: { type: 'host-promoted', newHostId: '...', isYou: true }
UI Elements found: { localAudioWrapper: true, remoteAudioWrapper: true, ... }
✅ Showed local audio wrapper
✅ Hid remote audio wrapper
✅ Enabled start button
✅ Showed end button
✅ Hid exit button
✅ Added event listener to start button
```

### Troubleshooting

If it doesn't work:
1. Check browser console for errors
2. Check server terminal for logs
3. Verify the room code is correct
4. Make sure both users are in the same room (check participants list)
5. Refresh and try again

### Common Issues

**Issue**: Alert shows but UI doesn't change
- **Check**: Are the console logs showing "UI Elements found: all true"?
- **Fix**: The elements might not exist in DOM

**Issue**: No alert appears
- **Check**: Server logs - is the promotion message being sent?
- **Check**: Is `isYou: true` in the message?

**Issue**: Start button doesn't work
- **Check**: Console logs when clicking button
- **Check**: Audio permissions granted?
60 changes: 48 additions & 12 deletions public/jamming.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,58 @@ <h2>Participants</h2>
const role = params.get('role');
const roomCode = params.get('code');
let currentClientId = null;
function updateParticipantList(participants) {
const participantsList = document.getElementById('participants-list');
participantsList.innerHTML = '';
participants.forEach(p => {
let currentHostId = null;

window.updateParticipantList = function(participants, hostId) {
currentHostId = hostId ?? currentHostId;
participantsList.innerHTML = '';
(participants || []).forEach(p => {
const li = document.createElement('li');
li.textContent = p.username;
let label = p.username;
if (p.id === currentHostId) {
label += ' 👑';
}
if (p.id === currentClientId) {
li.textContent += ' (You)';
label += ' (You)';
li.classList.add('you');
}
li.textContent = label;
participantsList.appendChild(li);
});
}
};

window.enableHostUI = function() {
if (localAudioWrapper) localAudioWrapper.style.display = '';
if (remoteAudioWrapper) remoteAudioWrapper.style.display = 'none';
if (startBtn) {
startBtn.style.display = '';
startBtn.disabled = false;
}
if (endBtn) {
endBtn.style.display = '';
}
if (exitBtn) {
exitBtn.style.display = 'none';
}
};

window.enableListenerUI = function() {
if (localAudioWrapper) localAudioWrapper.style.display = 'none';
if (remoteAudioWrapper) remoteAudioWrapper.style.display = '';
if (startBtn) {
startBtn.style.display = 'none';
}
if (endBtn) {
endBtn.style.display = 'none';
}
if (exitBtn) {
exitBtn.style.display = '';
}
};

window.setHostIdentity = function(hostId) {
currentHostId = hostId;
};
if (roomInfoDiv && roomCodeDisplay) {
roomInfoDiv.addEventListener('click', () => {
if (!roomCode) return;
Expand All @@ -166,12 +205,9 @@ <h2>Participants</h2>


if (role === 'host') {
remoteAudioWrapper.style.display = 'none';
exitBtn.style.display = 'none';
window.enableHostUI();
} else if (role === 'join') {
localAudioWrapper.style.display = 'none';
startBtn.style.display = 'none';
endBtn.style.display = 'none';
window.enableListenerUI();
}
exitBtn.addEventListener('click', () => {
if (window.ws) {
Expand Down
14 changes: 13 additions & 1 deletion public/js/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async function handleSignalingMessage(event) {
const urlParams = new URLSearchParams(window.location.search);
const roomCode = urlParams.get('code');
const username = urlParams.get('username');
ws.send(JSON.stringify({ type: 'joinroom', code: roomCode, from: clientId, username: username }));
ws.send(JSON.stringify({ type: 'joinroom', code: roomCode, from: clientId, username: username, role: 'host' }));
break;
}

Expand Down Expand Up @@ -190,6 +190,18 @@ async function handleSignalingMessage(event) {
break;
}

case 'host-promoted': {
// This message is sent when the original host leaves and we're promoted
if (data.isYou) {
console.log('🎉 You have been promoted to host!');
// Show notification to user
alert(`You are now the host! Previous host ${data.newHostUsername || 'someone'} left the session.`);
} else {
console.log(`${data.newHostUsername} is now the host.`);
}
break;
}

case 'end-call':
endCall();
break;
Expand Down
Loading