partey_workadventure/front/src/WebRtc/JitsiFactory.ts

193 lines
7.4 KiB
TypeScript
Raw Normal View History

import {JITSI_URL} from "../Enum/EnvironmentVariable";
import {mediaManager} from "./MediaManager";
import {coWebsiteManager} from "./CoWebsiteManager";
import {requestedCameraState, requestedMicrophoneState} from "../Stores/MediaStore";
declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any
2021-02-17 18:49:55 +01:00
interface jitsiConfigInterface {
startWithAudioMuted: boolean
startWithVideoMuted: boolean
prejoinPageEnabled: boolean
}
const getDefaultConfig = () : jitsiConfigInterface => {
const constraints = mediaManager.getConstraintRequestedByUser();
return {
startWithAudioMuted: !constraints.audio,
startWithVideoMuted: constraints.video === false,
prejoinPageEnabled: false
}
2021-02-09 14:17:48 +01:00
}
2021-02-17 18:49:55 +01:00
const mergeConfig = (config?: object) => {
const currentDefaultConfig = getDefaultConfig();
if(!config){
return currentDefaultConfig;
}
return {
2021-02-18 09:38:27 +01:00
...currentDefaultConfig,
...config,
2021-02-17 18:49:55 +01:00
startWithAudioMuted: (config as jitsiConfigInterface).startWithAudioMuted ? true : currentDefaultConfig.startWithAudioMuted,
startWithVideoMuted: (config as jitsiConfigInterface).startWithVideoMuted ? true : currentDefaultConfig.startWithVideoMuted,
prejoinPageEnabled: (config as jitsiConfigInterface).prejoinPageEnabled ? true : currentDefaultConfig.prejoinPageEnabled
}
}
2021-02-09 20:31:49 +01:00
const defaultInterfaceConfig = {
SHOW_CHROME_EXTENSION_BANNER: false,
MOBILE_APP_PROMO: false,
HIDE_INVITE_MORE_HEADER: true,
DISABLE_JOIN_LEAVE_NOTIFICATIONS: true,
DISABLE_VIDEO_BACKGROUND: true,
// Note: hiding brand does not seem to work, we probably need to put this on the server side.
SHOW_BRAND_WATERMARK: false,
SHOW_JITSI_WATERMARK: false,
SHOW_POWERED_BY: false,
SHOW_PROMOTIONAL_CLOSE_PAGE: false,
SHOW_WATERMARK_FOR_GUESTS: false,
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', /*'embedmeeting',*/ 'fullscreen',
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
'videoquality', 'filmstrip', /*'invite',*/ 'feedback', 'stats', 'shortcuts',
'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', /*'security'*/
],
};
const slugify = (...args: (string | number)[]): string => {
const value = args.join(' ')
return value
.normalize('NFD') // split an accented letter in the base letter and the accent
.replace(/[\u0300-\u036f]/g, '') // remove all previously split accents
.toLowerCase()
.trim()
.replace(/[^a-z0-9 ]/g, '') // remove all chars not letters, numbers and spaces (to be replaced)
.replace(/\s+/g, '-') // separator
}
class JitsiFactory {
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
private audioCallback = this.onAudioChange.bind(this);
private videoCallback = this.onVideoChange.bind(this);
private previousConfigMeet! : jitsiConfigInterface;
private jitsiScriptLoaded: boolean = false;
/**
* Slugifies the room name and prepends the room name with the instance
*/
public getRoomName(roomName: string, instance: string): string {
return slugify(instance.replace('/', '-') + "-" + roomName);
}
public start(roomName: string, playerName:string, jwt?: string, config?: object, interfaceConfig?: object, jitsiUrl?: string): void {
2021-02-17 19:21:37 +01:00
//save previous config
this.previousConfigMeet = getDefaultConfig();
coWebsiteManager.insertCoWebsite((async cowebsiteDiv => {
// Jitsi meet external API maintains some data in local storage
// which is sent via the appData URL parameter when joining a
// conference. Problem is that this data grows indefinitely. Thus
// after some time the URLs get so huge that loading the iframe
// becomes slow and eventually breaks completely. Thus lets just
// clear jitsi local storage before starting a new conference.
window.localStorage.removeItem("jitsiLocalStorage");
const domain = jitsiUrl || JITSI_URL;
if (domain === undefined) {
throw new Error('Missing JITSI_URL environment variable or jitsiUrl parameter in the map.')
}
await this.loadJitsiScript(domain);
const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any
roomName: roomName,
jwt: jwt,
width: "100%",
height: "100%",
parentNode: cowebsiteDiv,
2021-02-17 18:49:55 +01:00
configOverwrite: mergeConfig(config),
2021-02-09 20:31:49 +01:00
interfaceConfigOverwrite: {...defaultInterfaceConfig, ...interfaceConfig}
};
if (!options.jwt) {
delete options.jwt;
}
return new Promise((resolve, reject) => {
options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations.
2020-11-16 16:15:21 +01:00
setTimeout(() => resolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
this.jitsiApi.executeCommand('displayName', playerName);
this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback);
this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback);
});
}));
}
public async stop(): Promise<void> {
2020-10-31 14:04:55 +01:00
if(!this.jitsiApi){
return;
}
await coWebsiteManager.closeCoWebsite();
this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback);
this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback);
this.jitsiApi?.dispose();
2021-02-17 19:21:37 +01:00
//restore previous config
if(this.previousConfigMeet?.startWithAudioMuted){
await mediaManager.disableMicrophone();
requestedMicrophoneState.disableMicrophone();
2021-02-17 19:21:37 +01:00
}else{
await mediaManager.enableMicrophone();
requestedMicrophoneState.enableMicrophone();
2021-02-17 19:21:37 +01:00
}
if(this.previousConfigMeet?.startWithVideoMuted){
await mediaManager.disableCamera();
requestedCameraState.disableWebcam();
2021-02-17 19:21:37 +01:00
}else{
await mediaManager.enableCamera();
requestedCameraState.enableWebcam();
2021-02-17 19:21:37 +01:00
}
}
private onAudioChange({muted}: {muted: boolean}): void {
this.previousConfigMeet.startWithAudioMuted = muted;
}
private onVideoChange({muted}: {muted: boolean}): void {
this.previousConfigMeet.startWithVideoMuted = muted;
}
private async loadJitsiScript(domain: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (this.jitsiScriptLoaded) {
resolve();
return;
}
this.jitsiScriptLoaded = true;
// Load Jitsi if the environment variable is set.
const jitsiScript = document.createElement('script');
jitsiScript.src = 'https://' + domain + '/external_api.js';
jitsiScript.onload = () => {
resolve();
}
jitsiScript.onerror = () => {
reject();
}
document.head.appendChild(jitsiScript);
})
}
}
export const jitsiFactory = new JitsiFactory();