2024-05-07 18:01:49 +02:00

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 */