349 lines
8.4 KiB
JavaScript
349 lines
8.4 KiB
JavaScript
var LivekitClient = window.LivekitClient;
|
|
var room;
|
|
var myRoomName;
|
|
var token;
|
|
var nickname;
|
|
var numVideos = 0;
|
|
|
|
var localVideoPublication;
|
|
var localAudioPublication;
|
|
|
|
/* OPENVIDU METHODS */
|
|
|
|
function joinRoom() {
|
|
// --- 0) Change the button ---
|
|
|
|
document.getElementById('join-btn').disabled = true;
|
|
document.getElementById('join-btn').innerHTML = 'Joining...';
|
|
const myParticipantName = `Participant${Math.floor(Math.random() * 100)}`;
|
|
const myRoomName = $('#roomName').val();
|
|
|
|
room = new LivekitClient.Room();
|
|
|
|
room.on(
|
|
LivekitClient.RoomEvent.TrackSubscribed,
|
|
(track, publication, participant) => {
|
|
const element = track.attach();
|
|
element.id = track.sid;
|
|
document.getElementById('video-container').appendChild(element);
|
|
if (track.kind === 'video') {
|
|
var audioTrackId;
|
|
var videoTrackId;
|
|
participant.getTracks().forEach((track) => {
|
|
if (track.kind === 'audio') {
|
|
audioTrackId = track.trackInfo.sid;
|
|
} else if (track.kind === 'video') {
|
|
videoTrackId = track.trackInfo.sid;
|
|
}
|
|
});
|
|
addIndividualRecordingButton(element.id, videoTrackId, audioTrackId);
|
|
updateNumVideos(1);
|
|
}
|
|
}
|
|
);
|
|
|
|
// On every new Track destroyed...
|
|
room.on(
|
|
LivekitClient.RoomEvent.TrackUnsubscribed,
|
|
(track, publication, participant) => {
|
|
track.detach();
|
|
document.getElementById(track.sid)?.remove();
|
|
if (track.kind === 'video') {
|
|
// removeUserData(participant);
|
|
updateNumVideos(-1);
|
|
}
|
|
}
|
|
);
|
|
|
|
room.on(LivekitClient.RoomEvent.RecordingStatusChanged, (isRecording) => {
|
|
console.log('Recording status changed: ' + status);
|
|
if (!isRecording) {
|
|
listRecordings();
|
|
}
|
|
});
|
|
|
|
getToken(myRoomName, myParticipantName).then(async (token) => {
|
|
const livekitUrl = getLivekitUrlFromMetadata(token);
|
|
|
|
try {
|
|
await room.connect(livekitUrl, token);
|
|
|
|
var participantName = $('#user').val();
|
|
$('#room-title').text(myRoomName);
|
|
$('#join').hide();
|
|
$('#room').show();
|
|
|
|
const [audioPublication, videoPublication] = await Promise.all([
|
|
room.localParticipant.setMicrophoneEnabled(true),
|
|
room.localParticipant.setCameraEnabled(true),
|
|
]);
|
|
localVideoPublication = videoPublication;
|
|
localAudioPublication = audioPublication;
|
|
|
|
console.log('Connected to room ' + myRoomName);
|
|
const element = videoPublication.track.attach();
|
|
element.id = videoPublication.track.sid;
|
|
document.getElementById('video-container').appendChild(element);
|
|
addIndividualRecordingButton(
|
|
element.id,
|
|
videoPublication.track.sid,
|
|
audioPublication.track.sid
|
|
);
|
|
updateNumVideos(1);
|
|
} catch (error) {
|
|
console.warn(
|
|
'There was an error connecting to the room:',
|
|
error.code,
|
|
error.message
|
|
);
|
|
enableBtn();
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
function leaveRoom() {
|
|
room.disconnect();
|
|
room = null;
|
|
|
|
$('#video-container').empty();
|
|
numVideos = 0;
|
|
|
|
$('#join').show();
|
|
$('#room').hide();
|
|
|
|
enableBtn();
|
|
}
|
|
|
|
/* OPENVIDU METHODS */
|
|
|
|
function enableBtn() {
|
|
document.getElementById('join-btn').disabled = false;
|
|
document.getElementById('join-btn').innerHTML = 'Join!';
|
|
}
|
|
|
|
/* APPLICATION REST METHODS */
|
|
|
|
function getToken(roomName, participantName) {
|
|
return new Promise((resolve, reject) => {
|
|
// Video-call chosen by the user
|
|
httpRequest(
|
|
'POST',
|
|
'token',
|
|
{ roomName, participantName },
|
|
'Error generating token',
|
|
(response) => resolve(response.token)
|
|
);
|
|
});
|
|
}
|
|
|
|
async function httpRequest(method, url, body, errorMsg, successCallback) {
|
|
try {
|
|
const response = await fetch(url, {
|
|
method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: method === 'GET' ? undefined : JSON.stringify(body),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
successCallback(data);
|
|
} else {
|
|
console.warn(errorMsg);
|
|
console.warn('Error: ' + response.statusText);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
function startComposedRecording() {
|
|
var hasAudio = $('#has-audio-checkbox').prop('checked');
|
|
var hasVideo = $('#has-video-checkbox').prop('checked');
|
|
|
|
httpRequest(
|
|
'POST',
|
|
'recordings/start',
|
|
{
|
|
roomName: room.roomInfo.name,
|
|
outputMode: 'COMPOSED',
|
|
videoOnly: hasVideo && !hasAudio,
|
|
audioOnly: hasAudio && !hasVideo,
|
|
},
|
|
'Start recording WRONG',
|
|
(res) => {
|
|
console.log(res);
|
|
document.getElementById('forceRecordingId').value = res.id;
|
|
checkBtnsRecordings();
|
|
$('#textarea-http').text(JSON.stringify(res, null, '\t'));
|
|
}
|
|
);
|
|
}
|
|
|
|
function startIndividualRecording(videoTrackId, audioTrackId) {
|
|
return new Promise((resolve, reject) => {
|
|
httpRequest(
|
|
'POST',
|
|
'recordings/start',
|
|
{
|
|
roomName: room.roomInfo.name,
|
|
outputMode: 'INDIVIDUAL',
|
|
audioTrackId,
|
|
videoTrackId,
|
|
},
|
|
'Start recording WRONG',
|
|
(res) => {
|
|
console.log(res);
|
|
$('#textarea-http').text(JSON.stringify(res.info, null, '\t'));
|
|
resolve(res);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
function stopRecording(id) {
|
|
var forceRecordingId = id ? id : $('#forceRecordingId').val();
|
|
httpRequest(
|
|
'POST',
|
|
'recordings/stop',
|
|
{
|
|
recordingId: forceRecordingId,
|
|
},
|
|
'Stop recording WRONG',
|
|
(res) => {
|
|
console.log(res);
|
|
$('#forceRecordingId').val('');
|
|
$('#textarea-http').text(JSON.stringify(res.info, null, '\t'));
|
|
}
|
|
);
|
|
}
|
|
|
|
function listRecordings() {
|
|
httpRequest('GET', 'recordings/list', {}, 'List recordings WRONG', (res) => {
|
|
console.log(res);
|
|
$('#recording-list').empty();
|
|
if (res.recordings && res.recordings.length > 0) {
|
|
res.recordings.forEach((recording) => {
|
|
var li = document.createElement('li');
|
|
var a = document.createElement('a');
|
|
a.href = recording.path;
|
|
a.target = '_blank';
|
|
a.appendChild(document.createTextNode(recording.name));
|
|
li.appendChild(a);
|
|
$('#recording-list').append(li);
|
|
});
|
|
$('#delete-recordings-btn').prop('disabled', res.recordings.length === 0);
|
|
}
|
|
});
|
|
}
|
|
|
|
function deleteRecordings() {
|
|
httpRequest('DELETE', 'recordings', {}, 'Delete recordings WRONG', (res) => {
|
|
console.log(res);
|
|
$('#recording-list').empty();
|
|
$('#delete-recordings-btn').prop('disabled', true);
|
|
$('#textarea-http').text(JSON.stringify(res, null, '\t'));
|
|
});
|
|
}
|
|
|
|
/* APPLICATION REST METHODS */
|
|
|
|
/* APPLICATION BROWSER METHODS */
|
|
|
|
events = '';
|
|
|
|
window.onbeforeunload = function () {
|
|
// Gracefully leave room
|
|
if (room) {
|
|
removeUser();
|
|
leaveRoom();
|
|
}
|
|
};
|
|
|
|
function getLivekitUrlFromMetadata(token) {
|
|
if (!token) throw new Error('Trying to get metadata from an empty token');
|
|
try {
|
|
const base64Url = token.split('.')[1];
|
|
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
const jsonPayload = decodeURIComponent(
|
|
window
|
|
.atob(base64)
|
|
.split('')
|
|
.map((c) => {
|
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
})
|
|
.join('')
|
|
);
|
|
|
|
const payload = JSON.parse(jsonPayload);
|
|
if (!payload?.metadata) throw new Error('Token does not contain metadata');
|
|
const metadata = JSON.parse(payload.metadata);
|
|
return metadata.livekitUrl;
|
|
} catch (error) {
|
|
throw new Error('Error decoding and parsing token: ' + error);
|
|
}
|
|
}
|
|
|
|
function updateNumVideos(i) {
|
|
numVideos += i;
|
|
$('video').removeClass();
|
|
switch (numVideos) {
|
|
case 1:
|
|
$('video').addClass('two');
|
|
break;
|
|
case 2:
|
|
$('video').addClass('two');
|
|
break;
|
|
case 3:
|
|
$('video').addClass('three');
|
|
break;
|
|
case 4:
|
|
$('video').addClass('four');
|
|
break;
|
|
}
|
|
}
|
|
|
|
function checkBtnsRecordings() {
|
|
if (document.getElementById('forceRecordingId').value === '') {
|
|
document.getElementById('buttonStopRecording').disabled = true;
|
|
} else {
|
|
document.getElementById('buttonStopRecording').disabled = false;
|
|
}
|
|
}
|
|
|
|
function addIndividualRecordingButton(elementId, videoTrackId, audioTrackId) {
|
|
const div = document.createElement('div');
|
|
|
|
var button = document.createElement('button');
|
|
// button.id = elementId + '-button';
|
|
button.className = 'recording-track-button btn btn-sm';
|
|
|
|
button.innerHTML = 'Record Track';
|
|
button.style = 'position: absolute; left: 0; z-index: 1000;';
|
|
|
|
button.onclick = async () => {
|
|
if (button.innerHTML === 'Record Track') {
|
|
button.innerHTML = 'Stop Recording';
|
|
button.className = 'recording-track-button btn btn-sm btn-danger';
|
|
var res = await startIndividualRecording(videoTrackId, audioTrackId);
|
|
button.id = res.info.egressId;
|
|
} else {
|
|
button.innerHTML = 'Record Track';
|
|
button.className = 'recording-track-button btn btn-sm';
|
|
stopRecording(button.id);
|
|
}
|
|
};
|
|
div.appendChild(button);
|
|
var element = document.getElementById(elementId);
|
|
element.parentNode.insertBefore(div, element.nextSibling);
|
|
}
|
|
|
|
function clearHttpTextarea() {
|
|
$('#textarea-http').text('');
|
|
}
|
|
|
|
/* APPLICATION BROWSER METHODS */
|