Merge branch 'develop' into feat-network-indicator

auto_join_3.3
Roman Drozd 2020-05-20 16:47:02 +02:00
commit ddd8c36c67
62 changed files with 1100 additions and 483 deletions

2
app/.env 100644
View File

@ -0,0 +1,2 @@
REACT_APP_VERSION=$npm_package_version
REACT_APP_NAME=$npm_package_name

View File

@ -159,6 +159,12 @@
"no-inner-declarations": 2,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-trailing-spaces": [
"error",
{
"ignoreComments": true
}
],
"no-lonely-if": 2,
"no-mixed-operators": 2,
"no-mixed-spaces-and-tabs": 2,

View File

@ -5,6 +5,26 @@ var config =
developmentPort : 3443,
productionPort : 443,
/**
* Supported browsers version
* in bowser satisfy format.
* See more:
* https://www.npmjs.com/package/bowser#filtering-browsers
* Otherwise you got a unsupported browser page
*/
supportedBrowsers :
{
'windows' : {
'internet explorer' : '>12',
'microsoft edge' : '>18'
},
'safari' : '>12',
'firefox' : '>=60',
'chrome' : '>=74',
'opera' : '>=62',
'samsung internet for android' : '>=11.1.1.52'
},
/**
* If defaultResolution is set, it will override user settings when joining:
* low ~ 320x240
@ -26,6 +46,15 @@ var config =
{ scaleResolutionDownBy: 1 }
],
/**
* Alternative simulcast setting:
* [
* { maxBitRate: 50000 },
* { maxBitRate: 1000000 },
* { maxBitRate: 4800000 }
*],
**/
/**
* White listing browsers that support audio output device selection.
* It is not yet fully implemented in Firefox.
@ -37,7 +66,8 @@ var config =
'opera'
],
// Socket.io request timeout
requestTimeout : 10000,
requestTimeout : 20000,
requestRetries : 3,
transportOptions :
{
tcp : true
@ -55,10 +85,12 @@ var config =
},
/**
* Set the auto mute / Push To Talk threshold
* default value is 4
* Set max number participants in one room that join
* unmuted. Next participant will join automatically muted
* Default value is 4
*
* Set it to 0 to disable auto mute functionality,
* Set it to 0 to auto mute all,
* Set it to negative (-1) to never automatically auto mute
* but use it with caution
* full mesh audio strongly decrease room capacity!
*/
@ -75,9 +107,12 @@ var config =
notificationPosition : 'right',
// Timeout for autohiding topbar and button control bar
hideTimeout : 3000,
// max number of participant that will be visible in
// as speaker
lastN : 4,
mobileLastN : 1,
// Highest number of speakers user can select
// Highest number of lastN the user can select manually in
// userinteface
maxLastN : 5,
// If truthy, users can NOT change number of speakers visible
lockLastN : false,

View File

@ -16,6 +16,44 @@
<title>Multiparty Meeting</title>
<script src='%PUBLIC_URL%/config/config.js' type='text/javascript'></script>
<!-- Show an error page to IE browsers -->
<script type='text/javascript'>
var fallback = '<style type="text/css">body{margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;color:#444;padding:0 10px}h1,h2,h3{line-height:1.2}</style><header><h1>Your browser is not supported</h1><aside>You need to change to a different browser.</aside></header><h3>Supported browsers</h3><ul><li>Google Chrome/Chromium 55 +</li><li>Microsoft Edge 18 +</li><li>Mozilla Firefox 60 +</li><li>Apple Safari 12 +</li><li>Opera 62 +</li><li>Samsung Internet 11.1.1.52 +</li></ul>';
var fallbackCall = function() {
document.body.innerHTML = fallback;
};
if(navigator.userAgent.indexOf('MSIE') !== -1)
{
document.attachEvent('onreadystatechange', function() {
if (document.readyState === 'complete')
{
document.detachEvent('onreadystatechange', arguments.callee);
fallbackCall();
}
});
}
if (navigator.appVersion.indexOf('Trident/') > -1)
{
if (
document.readyState === 'complete' ||
(document.readyState !== 'loading' && !document.documentElement.doScroll)
)
{
document.removeEventListener('DOMContentLoaded', fallbackCall);
fallbackCall();
}
else
{
document.addEventListener('DOMContentLoaded', fallbackCall);
}
}
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,6 +1,7 @@
import Logger from './Logger';
import hark from 'hark';
import { getSignalingUrl } from './urlFactory';
import { SocketTimeoutError } from './utils';
import * as requestActions from './actions/requestActions';
import * as meActions from './actions/meActions';
import * as roomActions from './actions/roomActions';
@ -575,7 +576,7 @@ export default class RoomClient
if (called)
return;
called = true;
callback(new Error('Request timeout.'));
callback(new SocketTimeoutError('Request timed out'));
},
ROOM_OPTIONS.requestTimeout
);
@ -591,13 +592,13 @@ export default class RoomClient
};
}
sendRequest(method, data)
_sendRequest(method, data)
{
return new Promise((resolve, reject) =>
{
if (!this._signalingSocket)
{
reject('No socket connection.');
reject('No socket connection');
}
else
{
@ -607,13 +608,9 @@ export default class RoomClient
this.timeoutCallback((err, response) =>
{
if (err)
{
reject(err);
}
else
{
resolve(response);
}
})
);
}
@ -650,6 +647,33 @@ export default class RoomClient
}
}
async sendRequest(method, data)
{
logger.debug('sendRequest() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = window.config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('sendRequest() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
async changeDisplayName(displayName)
{
logger.debug('changeDisplayName() [displayName:"%s"]', displayName);
@ -1037,20 +1061,20 @@ export default class RoomClient
this._hark = hark(this._harkStream,
{
play : false,
interval : 5,
interval : 10,
threshold : store.getState().settings.noiseThreshold,
history : 30
history : 100
});
this._hark.lastVolume = -100;
this._hark.on('volume_change', (volume) =>
{
volume = Math.round(volume);
if (this._micProducer && volume !== Math.round(this._hark.lastVolume))
if (this._micProducer && (volume !== Math.round(this._hark.lastVolume)))
{
if (volume < this._hark.lastVolume * 1.02)
if (volume < this._hark.lastVolume)
{
volume = this._hark.lastVolume * 1.02;
volume = this._hark.lastVolume - Math.pow((volume - this._hark.lastVolume)/(100 + this._hark.lastVolume),4)*2;
}
this._hark.lastVolume = volume;
store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume));
@ -2690,7 +2714,7 @@ export default class RoomClient
store.dispatch(requestActions.notify(
{
text : intl.formatMessage({
id : 'moderator.muteScreenSharingModerator',
id : 'moderator.stopScreenSharing',
defaultMessage : 'Moderator stopped your screen sharing'
})
}));
@ -3053,9 +3077,13 @@ export default class RoomClient
if (!this._muted)
{
await this.enableMic();
const { autoMuteThreshold } = store.getState().settings;
let autoMuteThreshold = 4;
if (autoMuteThreshold && peers.length > autoMuteThreshold)
if ('autoMuteThreshold' in window.config)
{
autoMuteThreshold = window.config.autoMuteThreshold;
}
if (autoMuteThreshold && peers.length >= autoMuteThreshold)
this.muteMic();
}

View File

@ -73,14 +73,14 @@ export const setNoiseSuppression = (noiseSuppression) =>
export const setVoiceActivatedUnmute = (voiceActivatedUnmute) =>
({
type: 'SET_VOICE_ACTIVATED_UNMUTE',
payload: { voiceActivatedUnmute }
type : 'SET_VOICE_ACTIVATED_UNMUTE',
payload : { voiceActivatedUnmute }
});
export const setNoiseThreshold = (noiseThreshold) =>
({
type: 'SET_NOISE_THRESHOLD',
payload: { noiseThreshold }
type : 'SET_NOISE_THRESHOLD',
payload : { noiseThreshold }
});
export const setDefaultAudio = (audio) =>
@ -89,21 +89,6 @@ export const setDefaultAudio = (audio) =>
payload : { audio }
});
export const toggleEchoCancellation = () =>
({
type : 'TOGGLE_ECHO_CANCELLATION'
});
export const toggleAutoGainControl = () =>
({
type : 'TOGGLE_AUTO_GAIN_CONTROL'
});
export const toggleNoiseSuppression = () =>
({
type : 'TOGGLE_NOISE_SUPPRESSION'
});
export const toggleHiddenControls = () =>
({
type : 'TOGGLE_HIDDEN_CONTROLS'

View File

@ -509,8 +509,10 @@ const Me = (props) =>
>
{ micState === 'on' ?
<MicIcon
color={me.isAutoMuted ? 'secondary' : 'primary'}
style={me.isAutoMuted ? { opacity: noiseVolume }
color={me.isAutoMuted && settings.voiceActivatedUnmute ?
'secondary' : 'primary'}
style={me.isAutoMuted && settings.voiceActivatedUnmute ?
{ opacity: noiseVolume }
: { opacity: 1 }}
/>
:

View File

@ -58,27 +58,27 @@ const styles = () =>
'&.level6' :
{
height : '60%',
backgroundColor : 'rgba(255, 0, 0, 0.65)'
backgroundColor : 'rgba(255, 165, 0, 0.65)'
},
'&.level7' :
{
height : '70%',
backgroundColor : 'rgba(255, 0, 0, 0.65)'
backgroundColor : 'rgba(255, 100, 0, 0.65)'
},
'&.level8' :
{
height : '80%',
backgroundColor : 'rgba(0, 0, 0, 0.65)'
backgroundColor : 'rgba(255, 60, 0, 0.65)'
},
'&.level9' :
{
height : '90%',
backgroundColor : 'rgba(0, 0, 0, 0.65)'
backgroundColor : 'rgba(255, 30, 0, 0.65)'
},
'&.level10' :
{
height : '100%',
backgroundColor : 'rgba(0, 0, 0, 0.65)'
backgroundColor : 'rgba(255, 0, 0, 0.65)'
}
},
volumeSmall :

View File

@ -43,7 +43,8 @@ const styles = (theme) =>
link :
{
display : 'block',
textAlign : 'center'
textAlign : 'center',
marginBottom : theme.spacing(1)
}
});
@ -68,15 +69,16 @@ const About = ({
/>
</DialogTitle>
<DialogContent dividers='true'>
<DialogContentText>
<DialogContentText paragraph>
Contributions to this work were made on behalf of the GÉANT
project, a project that has received funding from the
European Unions Horizon 2020 research and innovation
programme under Grant Agreement No. 731122 (GN4-2).
On behalf of GÉANT project, GÉANT Association is the sole
owner of the copyright in all material which was developed
by a member of the GÉANT project.<br />
<br />
by a member of the GÉANT project.
</DialogContentText>
<DialogContentText paragraph>
GÉANT Vereniging (Association) is registered with the
Chamber of Commerce in Amsterdam with registration number
40535155 and operates in the UK as a branch of GÉANT
@ -87,6 +89,13 @@ const About = ({
<Link href='https://edumeet.org' target='_blank' rel='noreferrer' color='secondary' variant='h6' className={classes.link}>
https://edumeet.org
</Link>
<DialogContentText align='center' variant='h7'>
<FormattedMessage
id='label.version'
defaultMessage='Version'
/>
:{` ${process.env.REACT_APP_VERSION}`}
</DialogContentText>
</DialogContent>
<DialogActions>
{ window.config.logo && <img alt='Logo' className={classes.logo} src={window.config.logo} /> }

View File

@ -472,14 +472,6 @@ const TopBar = (props) =>
}
</div>
<div className={classes.sectionMobile}>
<IconButton
aria-haspopup='true'
onClick={handleMobileMenuOpen}
color='inherit'
>
<MoreIcon />
</IconButton>
</div>
{ lobbyPeers.length > 0 &&
<Tooltip
title={intl.formatMessage({
@ -508,6 +500,14 @@ const TopBar = (props) =>
</span>
</Tooltip>
}
<IconButton
aria-haspopup='true'
onClick={handleMobileMenuOpen}
color='inherit'
>
<MoreIcon />
</IconButton>
</div>
<div className={classes.divider} />
<Button
aria-label={intl.formatMessage({
@ -697,33 +697,6 @@ const TopBar = (props) =>
/>
</p>
</MenuItem>
{ lobbyPeers.length > 0 &&
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.lobby',
defaultMessage : 'Show lobby'
})}
disabled={!canPromote}
onClick={() =>
{
handleMenuClose();
setLockDialogOpen(!room.lockDialogOpen);
}}
>
<PulsingBadge
color='secondary'
badgeContent={lobbyPeers.length}
>
<SecurityIcon />
</PulsingBadge>
<p className={classes.moreAction}>
<FormattedMessage
id='tooltip.lobby'
defaultMessage='Show lobby'
/>
</p>
</MenuItem>
}
<MenuItem
aria-label={intl.formatMessage({
id : 'tooltip.participants',

View File

@ -4,13 +4,14 @@ import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../../RoomContext';
import * as settingsActions from '../../actions/settingsActions';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useIntl, FormattedMessage } from 'react-intl';
import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Select from '@material-ui/core/Select';
import Checkbox from '@material-ui/core/Checkbox';
import Switch from '@material-ui/core/Switch';
const styles = (theme) =>
({
@ -21,6 +22,13 @@ const styles = (theme) =>
formControl :
{
display : 'flex'
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1),
marginRight : 0
}
});
@ -37,16 +45,18 @@ const AdvancedSettings = ({
return (
<React.Fragment>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.advancedMode} onChange={onToggleAdvancedMode} value='advancedMode' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.advancedMode',
defaultMessage : 'Advanced mode'
})}
/>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.notificationSounds} onChange={onToggleNotificationSounds} value='notificationSounds' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.notificationSounds} onChange={onToggleNotificationSounds} value='notificationSounds' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.notificationSounds',
defaultMessage : 'Notification sounds'

View File

@ -4,6 +4,7 @@ import * as appPropTypes from '../appPropTypes';
import { withStyles } from '@material-ui/core/styles';
import * as roomActions from '../../actions/roomActions';
import * as settingsActions from '../../actions/settingsActions';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { useIntl, FormattedMessage } from 'react-intl';
import MenuItem from '@material-ui/core/MenuItem';
@ -11,7 +12,7 @@ import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Select from '@material-ui/core/Select';
import Checkbox from '@material-ui/core/Checkbox';
import Switch from '@material-ui/core/Switch';
const styles = (theme) =>
({
@ -22,6 +23,13 @@ const styles = (theme) =>
formControl :
{
display : 'flex'
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1),
marginRight : 0
}
});
@ -90,24 +98,28 @@ const AppearenceSettings = ({
</FormControl>
</form>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
className={classnames(classes.setting, classes.switchLabel)}
control={
<Switch checked={settings.permanentTopBar} onChange={onTogglePermanentTopBar} value='permanentTopBar' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.permanentTopBar',
defaultMessage : 'Permanent top bar'
})}
/>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.hiddenControls} onChange={onToggleHiddenControls} value='hiddenControls' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.hiddenControls} onChange={onToggleHiddenControls} value='hiddenControls' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.hiddenControls',
defaultMessage : 'Hidden media controls'
})}
/>
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.buttonControlBar} onChange={onToggleButtonControlBar} value='buttonControlBar' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.buttonControlBar} onChange={onToggleButtonControlBar} value='buttonControlBar' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.buttonControlBar',
defaultMessage : 'Separate media controls'
@ -115,8 +127,9 @@ const AppearenceSettings = ({
/>
{ !isMobile &&
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.drawerOverlayed} onChange={onToggleDrawerOverlayed} value='drawerOverlayed' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.drawerOverlayed} onChange={onToggleDrawerOverlayed} value='drawerOverlayed' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.drawerOverlayed',
defaultMessage : 'Side drawer over content'
@ -124,8 +137,9 @@ const AppearenceSettings = ({
/>
}
<FormControlLabel
className={classes.setting}
control={<Checkbox checked={settings.showNotifications} onChange={onToggleShowNotifications} value='showNotifications' />}
className={classnames(classes.setting, classes.switchLabel)}
control={<Switch checked={settings.showNotifications} onChange={onToggleShowNotifications} value='showNotifications' />}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.showNotifications',
defaultMessage : 'Show notifications'

View File

@ -12,9 +12,15 @@ import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Select from '@material-ui/core/Select';
import Checkbox from '@material-ui/core/Checkbox';
import Slider from '@material-ui/core/Slider';
import Typography from '@material-ui/core/Typography';
import Collapse from '@material-ui/core/Collapse';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import Switch from '@material-ui/core/Switch';
const NoiseSlider = withStyles(
{
@ -52,6 +58,23 @@ const styles = (theme) => ({
{
height : theme.spacing(3)
},
root : {
width : '100%',
backgroundColor : theme.palette.background.paper
},
switchLabel : {
justifyContent : 'space-between',
flex : 'auto',
display : 'flex',
padding : theme.spacing(1)
},
nested : {
display : 'block',
paddingTop : 0,
paddingBottom : 0,
paddingLeft : '25px',
paddingRight : '25px'
},
formControl :
{
display : 'flex'
@ -129,6 +152,13 @@ const MediaSettings = ({
else
audioOutputDevices = [];
const [ open, setOpen ] = React.useState(true);
const advancedAudioSettings = () =>
{
setOpen(!open);
};
return (
<React.Fragment>
<form className={classes.setting} autoComplete='off'>
@ -287,90 +317,120 @@ const MediaSettings = ({
</FormControl>
</form>
}
<form className={classes.setting} autoComplete='off'>
<List className={classes.root} component='nav'>
<ListItem button onClick={advancedAudioSettings}>
<ListItemText primary={intl.formatMessage({
id : 'settings.showAdvancedAudio',
defaultMessage : 'Show advanced audio settings'
})}
/>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={!open} timeout='auto'>
<List component='div'>
<ListItem className={classes.nested}>
<FormControlLabel
className={classes.setting}
className={classnames(classes.setting, classes.switchLabel)}
control={
<Checkbox checked={settings.echoCancellation} onChange={
<Switch color='secondary'
checked={settings.echoCancellation}
onChange={
(event) =>
{
setEchoCancellation(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.echoCancellation',
defaultMessage : 'Echo cancellation'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classes.setting}
className={classnames(classes.setting, classes.switchLabel)}
control={
<Checkbox checked={settings.autoGainControl} onChange={
<Switch color='secondary'
checked={settings.autoGainControl} onChange={
(event) =>
{
setAutoGainControl(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.autoGainControl',
defaultMessage : 'Auto gain control'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classes.setting}
className={classnames(classes.setting, classes.switchLabel)}
control={
<Checkbox checked={settings.noiseSuppression} onChange={
<Switch color='secondary'
checked={settings.noiseSuppression} onChange={
(event) =>
{
setNoiseSuppression(event.target.checked);
roomClient.changeAudioDevice(settings.selectedAudioDevice);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.noiseSuppression',
defaultMessage : 'Noise suppression'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<FormControlLabel
className={classes.setting}
className={classnames(classes.setting, classes.switchLabel)}
control={
<Checkbox checked={settings.voiceActivatedUnmute} onChange={
<Switch color='secondary'
checked={settings.voiceActivatedUnmute} onChange={
(event) =>
{
setVoiceActivatedUnmute(event.target.checked);
}}
/>}
labelPlacement='start'
label={intl.formatMessage({
id : 'settings.voiceActivatedUnmute',
defaultMessage : 'Voice activated unmute'
})}
/>
</ListItem>
<ListItem className={classes.nested}>
<div className={classes.margin} />
<Typography gutterBottom>
{
intl.formatMessage({
id : 'settings.noiseThreshold',
defaultMessage : 'Noise threshold:'
defaultMessage : 'Noise threshold'
})
}
}:
</Typography>
<NoiseSlider className={classnames(classes.slider, classnames.setting)}
key={'noise-threshold-slider'}
min={-100}
value={settings.noiseThreshold}
max={0}
valueLabelDisplay={'off'}
valueLabelDisplay={'auto'}
onChange={
(event, value) =>
{
roomClient._setNoiseThreshold(value);
}}
marks={[ { value: volume, label: 'level' } ]}
marks={[ { value: volume, label: `${volume} dB` } ]}
/>
<div className={classes.margin} />
</form>
</ListItem>
</List>
</Collapse>
</List>
</React.Fragment>
);
};
@ -399,8 +459,8 @@ const mapStateToProps = (state) =>
const mapDispatchToProps = {
setEchoCancellation : settingsActions.setEchoCancellation,
setAutoGainControl : settingsActions.toggleAutoGainControl,
setNoiseSuppression : settingsActions.toggleNoiseSuppression,
setAutoGainControl : settingsActions.setAutoGainControl,
setNoiseSuppression : settingsActions.setNoiseSuppression,
setVoiceActivatedUnmute : settingsActions.setVoiceActivatedUnmute
};

View File

@ -0,0 +1,154 @@
import React from 'react';
import { withStyles } from '@material-ui/core/styles';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import Grid from '@material-ui/core/Grid';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import WebAssetIcon from '@material-ui/icons/WebAsset';
import ErrorIcon from '@material-ui/icons/Error';
import Hidden from '@material-ui/core/Hidden';
const styles = (theme) =>
({
dialogPaper :
{
width : '40vw',
[theme.breakpoints.down('lg')] :
{
width : '40vw'
},
[theme.breakpoints.down('md')] :
{
width : '50vw'
},
[theme.breakpoints.down('sm')] :
{
width : '70vw'
},
[theme.breakpoints.down('xs')] :
{
width : '90vw'
}
// display : 'flex',
// flexDirection : 'column'
},
list : {
backgroundColor : theme.palette.background.paper
},
errorAvatar : {
width : theme.spacing(20),
height : theme.spacing(20)
}
});
const open=true;
const dividers=true;
let dense=false;
const supportedBrowsers=[
{ name: 'Chrome/Chromium', version: '74', vendor: 'Google' },
{ name: 'Edge', version: '18', vendor: 'Microsoft' },
{ name: 'Firefox', version: '60', vendor: 'Mozilla' },
{ name: 'Safari', version: '12', vendor: 'Apple' },
{ name: 'Opera', version: '62', vendor: '' },
// { name: 'Brave', version: '1.5', vendor: '' },
// { name: 'Vivaldi', version: '3', vendor: '' },
{ name: 'Samsung Internet', version: '11.1.1.52', vendor: '' }
];
const UnsupportedBrowser = ({
platform,
webrtcUnavailable,
classes
}) =>
{
if (platform !== 'desktop')
{
dense=true;
}
return (
<Dialog
open={open}
scroll={'body'}
classes={{
paper : classes.dialogPaper
}}
>
<DialogTitle id='form-dialog-title'>
{!webrtcUnavailable &&
<FormattedMessage
id='unsupportedBrowser.titleUnsupportedBrowser'
defaultMessage='Detected unsupported browser!'
/>
}
{webrtcUnavailable &&
<FormattedMessage
id='unsupportedBrowser.titlewebrtcUnavailable'
defaultMessage='Required functionality not availble in your browser!'
/>
}
</DialogTitle>
<DialogContent dividers={dividers} >
<FormattedMessage
id='unsupportedBrowser.bodyText'
defaultMessage='This meeting service requires a
functionality that is not supported by your browser.
Please upgrade, or switch to a different browser, or
check your settings. Supported browsers:'
/>
<Grid container spacing={2} justify='center' alignItems='center'>
<Grid item xs={12} md={7}>
<div className={classes.list}>
<List dense={dense}>
{supportedBrowsers.map((browser, index) =>
{
const supportedBrowser = `${browser.vendor} ${browser.name}`;
const supportedVersion = `${browser.version}+`;
return (
<ListItem key={index}>
<ListItemAvatar>
<Avatar>
<WebAssetIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={supportedBrowser}
secondary={supportedVersion}
/>
</ListItem>
);
})}
</List>
</div>
</Grid>
<Grid item xs={12} md={5} align='center'>
<Hidden mdDown>
<ErrorIcon className={classes.errorAvatar} color='error'/>
</Hidden>
</Grid>
</Grid>
</DialogContent>
</Dialog>
);
};
UnsupportedBrowser.propTypes =
{
webrtcUnavailable : PropTypes.bool.isRequired,
platform : PropTypes.string.isRequired,
classes : PropTypes.object.isRequired
};
export default withStyles(styles)(UnsupportedBrowser);

View File

@ -12,6 +12,7 @@ import RoomClient from './RoomClient';
import RoomContext from './RoomContext';
import deviceInfo from './deviceInfo';
import * as meActions from './actions/meActions';
import UnsupportedBrowser from './components/UnsupportedBrowser';
import ChooseRoom from './components/ChooseRoom';
import LoadingView from './components/LoadingView';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
@ -20,7 +21,7 @@ import { persistor, store } from './store';
import { SnackbarProvider } from 'notistack';
import * as serviceWorker from './serviceWorker';
import { ReactLazyPreload } from './components/ReactLazyPreload';
import { detectDevice } from 'mediasoup-client';
// import messagesEnglish from './translations/en';
import messagesNorwegian from './translations/nb';
import messagesGerman from './translations/de';
@ -70,6 +71,18 @@ const messages =
'lv' : messagesLatvian
};
const supportedBrowsers={
'windows' : {
'internet explorer' : '>12',
'microsoft edge' : '>18'
},
'safari' : '>12',
'firefox' : '>=60',
'chrome' : '>=74',
'opera' : '>=62',
'samsung internet for android' : '>=11.1.1.52'
};
const browserLanguage = (navigator.language || navigator.browserLanguage).toLowerCase();
let locale = browserLanguage.split(/[-_]/)[0]; // language without region code
@ -138,6 +151,58 @@ function run()
// Get current device.
const device = deviceInfo();
let unsupportedBrowser=false;
let webrtcUnavailable=false;
if (detectDevice() === undefined)
{
logger.error('Unsupported browser detected by mediasoup client detectDevice! deviceInfo: %o', device);
unsupportedBrowser=true;
}
else
if (
navigator.mediaDevices === undefined ||
navigator.mediaDevices.getUserMedia === undefined ||
window.RTCPeerConnection === undefined
)
{
logger.error('WebRTC is unavialable in your browser! deviceInfo: %o', device);
webrtcUnavailable=true;
}
else
if (!device.bowser.satisfies(
window.config.supportedBrowsers ? window.config.supportedBrowsers : supportedBrowsers)
)
{
logger.error(
'Your browser is not on the supported list! Ask your server admin to add your browser to the supported list, if you think that your browser should be supported! deviceInfo: %o',
device
);
unsupportedBrowser=true;
}
else
{
logger.debug('Supported Browser! deviceInfo: %o', device);
}
if (unsupportedBrowser || webrtcUnavailable)
{
render(
<MuiThemeProvider theme={theme}>
<RawIntlProvider value={intl}>
<UnsupportedBrowser
webrtcUnavailable={webrtcUnavailable}
platform={device.platform}
/>
</RawIntlProvider>
</MuiThemeProvider>,
document.getElementById('multiparty-meeting')
);
return;
}
store.dispatch(
meActions.setMe({
peerId,

View File

@ -0,0 +1,42 @@
class AudioAnalyzer extends EventEmitter
{
constructor()
{
if (prefix)
{
this._debug = debug(`${APP_NAME}:${prefix}`);
this._warn = debug(`${APP_NAME}:WARN:${prefix}`);
this._error = debug(`${APP_NAME}:ERROR:${prefix}`);
}
else
{
this._debug = debug(APP_NAME);
this._warn = debug(`${APP_NAME}:WARN`);
this._error = debug(`${APP_NAME}:ERROR`);
}
/* eslint-disable no-console */
this._debug.log = console.info.bind(console);
this._warn.log = console.warn.bind(console);
this._error.log = console.error.bind(console);
/* eslint-enable no-console */
}
get debug()
{
return this._debug;
}
get warn()
{
return this._warn;
}
get error()
{
return this._error;
}
}

View File

@ -10,13 +10,13 @@ const peerVolumes = (state = initialState, action) =>
peerId
} = action.payload;
return { ...state, [peerId]: 0 };
return { ...state, [peerId]: -100 };
}
case 'ADD_PEER':
{
const { peer } = action.payload;
return { ...state, [peer.id]: 0 };
return { ...state, [peer.id]: -100 };
}
case 'REMOVE_PEER':

View File

@ -22,7 +22,6 @@ const initialState =
notificationSounds : true,
buttonControlBar : window.config.buttonControlBar || false,
drawerOverlayed : window.config.drawerOverlayed || true,
autoMuteThreshold : window.config.autoMuteThreshold || 4,
...window.config.defaultAudio
};
@ -122,27 +121,6 @@ const settings = (state = initialState, action) =>
return { ...state, audio };
}
case 'TOGGLE_AUTO_GAIN_CONTROL':
{
const autoGainControl = !state.autoGainControl;
return { ...state, autoGainControl };
}
case 'TOGGLE_ECHO_CANCELLATION':
{
const echoCancellation = !state.echoCancellation;
return { ...state, echoCancellation };
}
case 'TOGGLE_NOISE_SUPPRESSION':
{
const noiseSuppression = !state.noiseSuppression;
return { ...state, noiseSuppression };
}
case 'SET_SAMPLE_SIZE':
{
const { sampleSize } = action.payload;

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "设置",
"settings.camera": "视频设备",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "无法保存文件",
"filesharing.startingFileShare": "正在尝试共享文件",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -118,6 +118,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Nastavení",
"settings.camera": "Kamera",
@ -143,6 +144,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Není možné uložit soubor",
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
@ -188,5 +191,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Video hinzufügen",
"label.promoteAllPeers": "Alle Teilnehmer reinlassen",
"label.moreActions": "Weitere Aktionen",
"label.version": null,
"settings.settings": "Einstellungen",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Automatische Pegelregelung (Audioeingang)",
"settings.noiseSuppression": "Rauschunterdrückung",
"settings.drawerOverlayed": "Seitenpanel verdeckt Hauptinhalt",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Fehler beim Speichern der Datei",
"filesharing.startingFileShare": "Starte Teilen der Datei",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Moderator hat geteilte Dateiliste gelöscht",
"moderator.muteAudio": "Moderator hat dich stummgeschaltet",
"moderator.muteVideo": "Moderator hat dein Video gestoppt",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Indstillinger",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Kan ikke gemme fil",
"filesharing.startingFileShare": "Forsøger at dele filen",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ρυθμίσεις",
"settings.camera": "Κάμερα",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Add video",
"label.promoteAllPeers": "Promote all",
"label.moreActions": "More actions",
"label.version": "Version",
"settings.settings": "Settings",
"settings.camera": "Camera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Auto gain control",
"settings.noiseSuppression": "Noise suppression",
"settings.drawerOverlayed": "Side drawer over content",
"settings.voiceActivatedUnmute": "Voice activated unmute",
"settings.noiseThreshold": "Noise threshold",
"filesharing.saveFileError": "Unable to save file",
"filesharing.startingFileShare": "Attempting to share file",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Moderator cleared the files",
"moderator.muteAudio": "Moderator muted your audio",
"moderator.muteVideo": "Moderator muted your video",
"moderator.muteScreenSharing": "Moderator muted your screen sharing"
"moderator.stopScreenSharing": "Moderator stopped your screen sharing",
"unsupportedBrowser.titleUsnsupportedBrowser": "Detected unsupported browser!",
"unsupportedBrowser.titlewebrtcUnavailable": "Required functionality not available in your browser!",
"unsupportedBrowser.bodyText": "This meeting service requires a functionality that is not supported by your browser. Please upgrade, or switch to a different browser, or check your settings. Supported browsers:"
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ajustes",
"settings.camera": "Cámara",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
"filesharing.startingFileShare": "Intentando compartir el fichero",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Paramètres",
"settings.camera": "Caméra",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Impossible d'enregistrer le fichier",
"filesharing.startingFileShare": "Début du transfert de fichier",
@ -188,5 +191,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Dodaj video",
"label.promoteAllPeers": "Promoviraj sve",
"label.moreActions": "Više akcija",
"label.version": null,
"settings.settings": "Postavke",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Automatsko upravljanje jačinom zvuka",
"settings.noiseSuppression": "Poništavanje šuma",
"settings.drawerOverlayed": "Bočni izbornik iznad sadržaja",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Nije moguće spremiti datoteku",
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Moderator je izbrisao datoteke",
"moderator.muteAudio": "Moderator je utišao tvoj zvuk",
"moderator.muteVideo": "Moderator je zaustavio tvoj video",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -66,7 +66,7 @@
"room.browsePeersSpotlight": "Résztvevők böngészése",
"room.stopAllScreenSharing": "Összes képernyőmegosztás leállítása",
"me.mutedPTT": "Némítva vagy, ha beszélnél nyomd le a szóköz billentyűt",
"me.mutedPTT": "Némítva vagy, ha beszélnél nyomd le a SZÓKÖZ billentyűt",
"roles.gotRole": "{role} szerepet kaptál",
"roles.lostRole": "Elvesztetted a {role} szerepet",
@ -86,9 +86,9 @@
"tooltip.muteParticipantVideo": "Résztvevő videóstreamének némítása",
"tooltip.raisedHand": "Jelentkezés",
"tooltip.muteScreenSharing": "Képernyőmegosztás szüneteltetése",
"tooltip.muteParticipantAudioModerator": "Résztvevő hangjának általános némítása",
"tooltip.muteParticipantVideoModerator": "Résztvevő videójának általános némítása",
"tooltip.muteScreenSharingModerator": "Résztvevő képernyőmegosztásának általános némítása",
"tooltip.muteParticipantAudioModerator": "Résztvevő hangjának némítása mindenkinél",
"tooltip.muteParticipantVideoModerator": "Résztvevő videójának némítása mindenkinél",
"tooltip.muteScreenSharingModerator": "Résztvevő képernyőmegosztásának leállítása mindenkinél",
"label.roomName": "Konferencia",
"label.chooseRoomButton": "Tovább",
@ -119,6 +119,7 @@
"label.addVideo": "Videó hozzáadása",
"label.promoteAllPeers": "Mindenkit beengedek",
"label.moreActions": "További műveletek",
"label.version": "Verzió",
"settings.settings": "Beállítások",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Automatikus hangerő",
"settings.noiseSuppression": "Zajelnyomás",
"settings.drawerOverlayed": "Oldalsáv a tartalom felett",
"settings.voiceActivatedUnmute": "Beszéd aktivált mikrofon némítás",
"settings.noiseThreshold": "Zajszint",
"filesharing.saveFileError": "A file-t nem sikerült elmenteni",
"filesharing.startingFileShare": "Fájl megosztása",
@ -189,5 +192,9 @@
"moderator.clearFiles": "A moderátor kiürítette a file megosztás történelmet",
"moderator.muteAudio": "A moderátor elnémította a hangod",
"moderator.muteVideo": "A moderátor elnémította a videód",
"moderator.muteScreenSharing": "A moderátor leállította képernyőmegosztásod"
"moderator.stopScreenSharing": "A moderátor leállította a képernyőmegosztásod",
"unsupportedBrowser.titleUnsupportedBrowser": "A bőngésző verziód sajnos nem támogatott! :-(",
"unsupportedBrowser.titlewebrtcUnavailable": "A böngésződ egy szükséges funkciója nem elérhető!",
"unsupportedBrowser.bodyText": "Kérlek frissítsd a böngésződ, válts másik böngészőre, vagy ellenőrizd a böngésződ beállításait! Támogatott böngészők:"
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Aggiungi video",
"label.promoteAllPeers": "Promuovi tutti",
"label.moreActions": "Altre azioni",
"label.version": null,
"settings.settings": "Impostazioni",
"settings.camera": "Videocamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Controllo guadagno automatico",
"settings.noiseSuppression": "Riduzione del rumore",
"settings.drawerOverlayed": "Barra laterale sovrapposta",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Impossibile salvare file",
"filesharing.startingFileShare": "Tentativo di condivisione file",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Il moderatore ha pulito i file",
"moderator.muteAudio": "Il moderatore ha mutato il tuo audio",
"moderator.muteVideo": "Il moderatore ha fermato il tuo video",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -116,6 +116,7 @@
"label.advanced": "Advancēts",
"label.addVideo": "Pievienot video",
"label.moreActions": null,
"label.version": null,
"settings.settings": "Iestatījumi",
"settings.camera": "Kamera",
@ -138,6 +139,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Nav iespējams saglabāt failu",
"filesharing.startingFileShare": "Tiek mēģināts kopīgot failu",
@ -183,5 +186,9 @@
"moderator.clearFiles": "Moderators notīrīja failus",
"moderator.muteAudio": "Moderators noklusināja jūsu mikrofonu",
"moderator.muteVideo": "Moderators atslēdza jūsu kameru",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Legg til video",
"label.promoteAllPeers": "Slipp inn alle",
"label.moreActions": "Flere handlinger",
"label.version": null,
"settings.settings": "Innstillinger",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Auto gain kontroll",
"settings.noiseSuppression": "Støy reduksjon",
"settings.drawerOverlayed": "Sidemeny over innhold",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Klarte ikke å lagre fil",
"filesharing.startingFileShare": "Starter fildeling",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Moderator fjernet filer",
"moderator.muteAudio": "Moderator mutet lyden din",
"moderator.muteVideo": "Moderator mutet videoen din",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": "Dodaj wideo",
"label.promoteAllPeers": "Wpuść wszystkich",
"label.moreActions": "Więcej akcji",
"label.version": null,
"settings.settings": "Ustawienia",
"settings.camera": "Kamera",
@ -144,6 +145,8 @@
"settings.autoGainControl": "Auto korekta wzmocnienia",
"settings.noiseSuppression": "Wyciszenie szumów",
"settings.drawerOverlayed": "Szuflada nad zawartością",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Nie można zapisać pliku",
"filesharing.startingFileShare": "Próba udostępnienia pliku",
@ -189,5 +192,9 @@
"moderator.clearFiles": "Moderator wyczyścił pliki",
"moderator.muteAudio": "Moderator wyciszył audio",
"moderator.muteVideo": "Moderator wyciszył twoje video",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Definições",
"settings.camera": "Camera",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Impossível de gravar o ficheiro",
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Setări",
"settings.camera": "Cameră video",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Încercarea de a salva fișierul a eșuat",
"filesharing.startingFileShare": "Partajarea fișierului",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Ayarlar",
"settings.camera": "Kamera",
@ -141,6 +142,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Dosya kaydedilemiyor",
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
@ -181,5 +184,14 @@
"devices.cameraDisconnected": "Kamera bağlı değil",
"devices.cameraError": "Kameranıza erişilirken bir hata oluştu",
"moderator.muteScreenSharing": null
"moderator.clearChat": null,
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -118,6 +118,7 @@
"label.addVideo": "新增視訊",
"label.promoteAllPeers": "提升全部",
"label.moreActions": "更多",
"label.version": null,
"settings.settings": "設置",
"settings.camera": "視訊來源",
@ -143,6 +144,8 @@
"settings.autoGainControl": "自動增益控制",
"settings.noiseSuppression": "噪音消除",
"settings.drawerOverlayed": "側邊欄覆蓋畫面",
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "無法保存文件",
"filesharing.startingFileShare": "開始分享文件",
@ -188,5 +191,9 @@
"moderator.clearFiles": "管理員清除了所有檔案",
"moderator.muteAudio": "您已被管理員靜音",
"moderator.muteVideo": "您的視訊已被管理員關閉",
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -119,6 +119,7 @@
"label.addVideo": null,
"label.promoteAllPeers": null,
"label.moreActions": null,
"label.version": null,
"settings.settings": "Налаштування",
"settings.camera": "Камера",
@ -144,6 +145,8 @@
"settings.autoGainControl": null,
"settings.noiseSuppression": null,
"settings.drawerOverlayed": null,
"settings.voiceActivatedUnmute": null,
"settings.noiseThreshold": null,
"filesharing.saveFileError": "Неможливо зберегти файл",
"filesharing.startingFileShare": "Спроба поділитися файлом",
@ -189,5 +192,9 @@
"moderator.clearFiles": null,
"moderator.muteAudio": null,
"moderator.muteVideo": null,
"moderator.muteScreenSharing": null
"moderator.stopScreenSharing": null,
"unsupportedBrowser.titleUnsupportedBrowser": null,
"unsupportedBrowser.titlewebrtcUnavailable": null,
"unsupportedBrowser.bodyText": null
}

View File

@ -17,3 +17,21 @@ export const idle = (callback, delay) =>
handle = setTimeout(callback, delay);
};
};
/**
* Error produced when a socket request has a timeout.
*/
export class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}

View File

@ -106,6 +106,12 @@
"no-invalid-regexp": 2,
"no-invalid-this": 2,
"no-irregular-whitespace": 2,
"no-trailing-spaces": [
"error",
{
"ignoreComments": true
}
],
"no-lonely-if": 2,
"no-mixed-operators": 2,
"no-mixed-spaces-and-tabs": 2,

View File

@ -276,6 +276,10 @@ module.exports =
// maxUsersPerRoom : 20,
// Room size before spreading to new router
routerScaleSize : 40,
// Socket timout value
requestTimeout : 20000,
// Socket retries when timeout
requestRetries : 3,
// Mediasoup settings
mediasoup :
{

View File

@ -3,6 +3,7 @@ const AwaitQueue = require('awaitqueue');
const axios = require('axios');
const Logger = require('./Logger');
const Lobby = require('./Lobby');
const { SocketTimeoutError } = require('./errors');
const { v4: uuidv4 } = require('uuid');
const jwt = require('jsonwebtoken');
const userRoles = require('../userRoles');
@ -1759,9 +1760,9 @@ class Room extends EventEmitter
if (called)
return;
called = true;
callback(new Error('Request timeout.'));
callback(new SocketTimeoutError('Request timed out'));
},
10000
config.requestTimeout || 20000
);
return (...args) =>
@ -1775,7 +1776,7 @@ class Room extends EventEmitter
};
}
_request(socket, method, data = {})
_sendRequest(socket, method, data = {})
{
return new Promise((resolve, reject) =>
{
@ -1797,6 +1798,33 @@ class Room extends EventEmitter
});
}
async _request(socket, method, data)
{
logger.debug('_request() [method:"%s", data:"%o"]', method, data);
const {
requestRetries = 3
} = config;
for (let tries = 0; tries < requestRetries; tries++)
{
try
{
return await this._sendRequest(socket, method, data);
}
catch (error)
{
if (
error instanceof SocketTimeoutError &&
tries < requestRetries
)
logger.warn('_request() | timeout, retrying [attempt:"%s"]', tries);
else
throw error;
}
}
}
_notification(socket, method, data = {}, broadcast = false, includeSender = false)
{
if (broadcast)

View File

@ -0,0 +1,22 @@
/**
* Error produced when a socket request has a timeout.
*/
class SocketTimeoutError extends Error
{
constructor(message)
{
super(message);
this.name = 'SocketTimeoutError';
if (Error.hasOwnProperty('captureStackTrace')) // Just in V8.
Error.captureStackTrace(this, SocketTimeoutError);
else
this.stack = (new Error(message)).stack;
}
}
module.exports =
{
SocketTimeoutError
};