In this article, you will learn about Wexstream, a Video Conferencing Platform built with Node.js, React and Jitsi.
Table of Contents
- Introduction
- Prerequisites
- Quick Overview
- Installing
- Run from Source
- Using the Code
- API
- Frontend
- Terms of Service
- History
Wexstream is an open source video conferencing platform built with Node.js, React and Jitsi.
Wexstream lets you create a network and share your private video conferences with your network or create public video conferences and share them with people outside your network.
Wexstream lets you stay in touch with all your teams, family, friends, or colleagues. Instant video conferences, efficiently adapting to your scale for free.
- Unlimited users: There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors.
- Privacy settings, passwords and meeting locks put the control in your hands.
- Lock-protected rooms: Control the access to your conferences with a password.
- Desktop screen sharing, chat, and many useful features.
- Encrypted by default.
- Protected conferences using TLS encryption and end-to-server/transit encryption.
- High quality: Audio and video are delivered with the clarity and richness of Opus and VP8.
- Web ready: No downloads are required of your friends to join the conversation. Wexstream works directly within their browsers as well. Simply share your conference URL with others to get started.
- Mobile ready: Accessible, legible, and usable across all devices.
- Users' personal data is neither resold nor communicated to third parties.
- Users have the right to access, modify, rectify and delete their personal data.
Easy to Use
Wexstream is simple, flexible and easy to use, no matter your location.
Users can instantly jump into a webcast online, no download required.
Once registered, the user can benefit from the following services:
- Easy networking between platform members
- Provision of video conferencing tools
- Provision of communication tools between platform members
The platform works as follows:
- The user creates a network by connecting with others.
- The user broadcasts private or public conferences.
- When broadcasting a conference, the user gets a URL that he can share to invite others to join him.
- When broadcasting a conference, the user's network is notified.
Secure
Wexstream protects your live and hosted content using TLS encryption and end-to-server/transit encryption. Plus, added privacy settings passwords and meeting locks puts the control in your hands.
Wexstream is committed to using all means to ensure the security and privacy of users' personal data.
Users' personal data is neither resold nor communicated to third parties.
The user has the right to access, modify, rectify and delete his personal data.
Neat Content
The user is expressly forbidden to publish any content, engage in any activity, stream any feed or create any account that is offensive, pornographic, violent, abusive, defamatory, threatening or obscene, illegal or intended to promote or commit an illegal act, including violations of intellectual property rights, privacy rights or proprietary rights, denigrating, slanderous, racist, xenophobic, contrary to morality and good morals, infringing content, undermining public order or rights, likely to infringe the rights, reputation and image of the platform and more generally, the content of which would violate the law and/or regulations, in particular of a criminal nature, includes his password, or purposely includes someone else's password, personal data, or is intended to solicit such data, misleads or deceives, or is likely to mislead or deceive, others as to his identity or affiliation with another person or organisation, breaches any of his obligations under the terms of use of the platform or any of its incorporated policies.
- Node.js
- Express
- MongoDB
- React
- MUI
- JavaScript
- Git
In this section, you'll see a quick overview of the main pages.
Below is the login page:
The user can authenticate through Google, Facebook or email by creating an account from the sign up page:
Below is the about page:
Below is the terms of service page:
Below is the contact page:
When the user signs in, he arrives to the home page where he can see his timeline:
The timeline contains the video conferences of the user's network.
Below is the network page:
From the network page, the user can search for users and send connection requests or private messages.
Below is the connections page:
The connections page contains the user's network.
Below is the messages page:
From the messages page, the user can read and manage his messages or send new messages.
Below is the notifications page:
From the notifications page, the user can read and manage his notifications.
Below is the profile page:
From the profile page, the user can see the private and public video conferences of a member of his network or public video conferences of a user outside his network.
Below is the settings page:
From the settings page, the user can manage his settings or delete his account.
Below is the reset password page:
From this page, the user who signed up with his email can change his password.
Below is the dialog for creating a new video conference:
The user inputs a required title, an optional description and a flag that indicates whether the video conference is private or public. If the video conference is private, it will be available to its network only. Otherwise if the video conference is public, it will be available to everyone.
Below is the conference page:
The user will obtain a unique video conference URL that he can share with his network if the conference is private or everyone if the conference is public. The user can start/stop his camera, share his screen with the participants, protect his conference with a password, raise/lower his hand, see the participants, send private messages to the participants and so on.
You can find installation instructions here.
Below are the instructions to run Wexstream
from source code.
Prerequisites
Instructions
- Clone
Wexstream
repo:
git clone https://github.com/aelassas/wexstream.git
- Add api/.env file:
NODE_ENV = development
WS_PORT = 4003
WS_HTTPS = false
WS_PRIVATE_KEY = /etc/jitsi/meet/192.168.100.223.key
WS_CERTIFICATE = /etc/jitsi/meet/192.168.100.223.crt
WS_APP_HOST = localhost
WS_DB_HOST = 127.0.0.1
WS_DB_PORT = 27017
WS_DB_SSL = false
WS_DB_SSL_KEY = /etc/jitsi/meet/192.168.100.223.key
WS_DB_SSL_CERT = /etc/jitsi/meet/192.168.100.223.crt
WS_DB_SSL_CA = /etc/jitsi/meet/192.168.100.223.ca.pem
WS_DB_DEBUG = false
WS_DB_APP_NAME = wexstream
WS_DB_AUTH_SOURCE = admin
WS_DB_USERNAME = admin
WS_DB_PASSWORD = PASSWORD
WS_DB_NAME = wexstream
WS_JWT_SECRET = JWT_SECRET
WS_JWT_SUB = 192.168.100.223
WS_JWT_EXPIRE_AT = 86400
WS_TOKEN_EXPIRE_AT = 86400
WS_SMTP_HOST = host
WS_SMTP_PORT = 587
WS_SMTP_USER = USER
WS_SMTP_PASS = PASSWORD
WS_SMTP_FROM = no-reply@wexstream.com
WS_ADMIN_EMAIL = admin@wexstream.com
WS_DEFAULT_LANGUAGE = en
WS_CDN = /var/www/cdn/wexstream
You must configure the following options:
WS_DB_PASSWORD
WS_JWT_SECRET
WS_JWT_SUB
WS_SMTP_HOST
WS_SMTP_PORT
WS_SMTP_USER
WS_SMTP_PASS
WS_SMTP_FROM
WS_ADMIN_EMAIL
WS_JWT_SECRET
must be the same as the JWT secret used in Jitsi.
WS_JWT_SUB
must be the FQDN or IP of the server where Jitsi is installed.
- Add frontend/.env file:
REACT_APP_NODE_ENV = development
REACT_APP_WS_DEFAULT_LANGUAGE = en
REACT_APP_WS_DATE_FORMAT = llll
REACT_APP_WS_PAGE_SIZE = 30
REACT_APP_WS_JITSI_HOST = 192.168.100.223:444
REACT_APP_WS_JITSI_API = https://192.168.100.223:444/external_api.js
REACT_APP_WS_API_HOST = http://localhost:4003
REACT_APP_WS_CDN = https://localhost/cdn/wexstream
REACT_APP_WS_GOOGLE_CLIENT_ID = GOOGLE_CLIENT_ID
REACT_APP_WS_FACEBOOK_APP_ID = FACEBOOK_APP_ID
REACT_APP_WS_GOOGLE_CLIENT_ID
is used for Google authentication.
REACT_APP_WS_FACEBOOK_APP_ID
is used for Facebook authentication.
You must configure the following options:
REACT_APP_WS_JITSI_HOST
REACT_APP_WS_JITSI_API
REACT_APP_WS_GOOGLE_CLIENT_ID
REACT_APP_WS_FACEBOOK_APP_ID
- Install
nodemon
:
npm i -g nodemon
- Run
api
:
cd ./api
npm install
npm run dev
- Run
frontend
:
cd ./frontend
npm install
npm start
- Configure http://localhost/cdn
- On Windows, install IIS and create C:\inetpub\wwwroot\cdn\wexstream folder.
- On Linux, install NGINX and add cdn folder by changing /etc/nginx/sites-available/default as follows:
server {
listen 80 default_server;
server_name _;
...
location /cdn {
alias /var/www/cdn;
}
}
Create /var/www/cdn/wexstream folder and add the necessary permissions:
mkdir /var/www/cdn/wexstream
sudo chown -R $USER:$USER /var/www/cdn/wexstream
This section describes the software architecture of Wexstream
including the database, the API and the frontend.
Wexstream API exposes all Wexstream
functions needed for the frontend. The API follows the MVC design pattern. JWT is used for authentication. There are some functions that need authentication such as functions related to managing conferences and connections and others that do not need authentication such as signing up.
- ./api/models/ folder contains MongoDB models.
- ./api/routes/ folder contains Express routes.
- ./api/controllers/ folder contains controllers.
- ./api/middlewares/ folder contains middlewares.
- ./api/server.js is the main server where database connection is established and routes are loaded.
- ./api/app.js is the main entry point of Wexstream API.
app.js
app.js is the main entry point of Wexstream
API:
import app from './server.js'
import fs from 'fs'
import https from 'https'
const PORT = parseInt(process.env.WS_PORT) || 4000
const HTTPS = process.env.WS_HTTPS.toLocaleLowerCase() === 'true'
const PRIVATE_KEY = process.env.WS_PRIVATE_KEY
const CERTIFICATE = process.env.WS_CERTIFICATE
if (HTTPS) {
https.globalAgent.maxSockets = Infinity
const privateKey = fs.readFileSync(PRIVATE_KEY, 'utf8')
const certificate = fs.readFileSync(CERTIFICATE, 'utf8')
const credentials = { key: privateKey, cert: certificate }
const httpsServer = https.createServer(credentials, app)
httpsServer.listen(PORT, () => {
console.log('HTTPS server is running on Port:', PORT)
})
} else {
app.listen(PORT, () => {
console.log('HTTPS server is running on Port:', PORT)
})
}
In app.js, we retrieve HTTPS
setting variable which indicate whether https is enabled or not. If https is enabled, we create an https server using the provided private key and certificate and start listening. Otherwise, an http server is created and we start listening. app
is the main server where database connection is established and routes are loaded.
server.js
server.js is in the main server:
import express from 'express'
import cors from 'cors'
import mongoose from 'mongoose'
import compression from 'compression'
import helmet from 'helmet'
import nocache from 'nocache'
import strings from './config/app.config.js'
import userRoutes from './routes/userRoutes.js'
import connectionRoutes from './routes/connectionRoutes.js'
import notificationRoutes from './routes/notificationRoutes.js'
import messageRoutes from './routes/messageRoutes.js'
import conferenceRoutes from './routes/conferenceRoutes.js'
import timelineRoutes from './routes/timelineRoutes.js'
const DB_HOST = process.env.WS_DB_HOST
const DB_PORT = process.env.WS_DB_PORT
const DB_SSL = process.env.WS_DB_SSL.toLowerCase() === 'true'
const DB_SSL_KEY = process.env.WS_DB_SSL_KEY
const DB_SSL_CERT = process.env.WS_DB_SSL_CERT
const DB_SSL_CA = process.env.WS_DB_SSL_CA
const DB_DEBUG = process.env.WS_DB_DEBUG.toLowerCase() === 'true'
const DB_AUTH_SOURCE = process.env.WS_DB_AUTH_SOURCE
const DB_USERNAME = process.env.WS_DB_USERNAME
const DB_PASSWORD = process.env.WS_DB_PASSWORD
const DB_APP_NAME = process.env.WS_DB_APP_NAME
const DB_NAME = process.env.WS_DB_NAME
const DB_URI = `mongodb://${encodeURIComponent(DB_USERNAME)}:${encodeURIComponent
(DB_PASSWORD)}@${DB_HOST}:${DB_PORT}/${DB_NAME}?
authSource=${DB_AUTH_SOURCE}&appName=${DB_APP_NAME}`
let options = {}
if (DB_SSL) {
options = {
ssl: true,
sslValidate: true,
sslKey: DB_SSL_KEY,
sslCert: DB_SSL_CERT,
sslCA: [DB_SSL_CA]
}
}
mongoose.set('debug', DB_DEBUG)
mongoose.Promise = global.Promise
mongoose.connect(DB_URI, options)
.then(
() => { console.log('Database is connected') },
err => { console.error('Cannot connect to the database:', err) }
)
const app = express()
app.use(helmet.contentSecurityPolicy())
app.use(helmet.dnsPrefetchControl())
app.use(helmet.crossOriginEmbedderPolicy())
app.use(helmet.frameguard())
app.use(helmet.hidePoweredBy())
app.use(helmet.hsts())
app.use(helmet.ieNoOpen())
app.use(helmet.noSniff())
app.use(helmet.permittedCrossDomainPolicies())
app.use(helmet.referrerPolicy())
app.use(helmet.xssFilter())
app.use(helmet.originAgentCluster())
app.use(helmet.crossOriginResourcePolicy({ policy: 'cross-origin' }))
app.use(helmet.crossOriginOpenerPolicy())
app.use(nocache())
app.use(compression({ threshold: 0 }))
app.use(express.urlencoded({ limit: '50mb', extended: true }))
app.use(express.json({ limit: '50mb' }))
app.use(cors())
app.use('/', userRoutes)
app.use('/', connectionRoutes)
app.use('/', notificationRoutes)
app.use('/', messageRoutes)
app.use('/', conferenceRoutes)
app.use('/', timelineRoutes)
strings.setLanguage(process.env.WS_DEFAULT_LANGUAGE)
export default app
First of all, we build MongoDB connection string, then we establish a connection with Wexstream
MongoDB database. Then we create an Express app
and load middlewares. Finally, we load Express routes and export app
.
Routes
There are six routes in Wexstream
API. Each route has its own controller following the MVC design pattern and SOLID principles. Below are the main routes:
userRoutes
: Provides REST functions related to users connectionRoutes
: Provides REST functions related to connections between users conferenceRoutes
: Provides REST functions related to conferences timelineRoutes
: Provides REST functions related to timelines messageRoutes
: Provides REST functions related to messages notificationRoutes
: Provides REST functions related to notifications
We are not going to explain each route one by one. We'll take, for example, conferenceRoutes
and see how it was made:
import express from 'express'
import routeNames from '../config/conferenceRoutes.config.js'
import authJwt from '../middlewares/authJwt.js'
import * as conferenceController from '../controllers/conferenceController.js'
const routes = express.Router()
routes.route(routeNames.create).post(authJwt.verifyToken, conferenceController.create)
routes.route(routeNames.update).post(authJwt.verifyToken, conferenceController.update)
routes.route(routeNames.addMember).post(authJwt.verifyToken,
conferenceController.addMember)
routes.route(routeNames.removeMember).post(authJwt.verifyToken,
conferenceController.removeMember)
routes.route(routeNames.delete).delete(authJwt.verifyToken,
conferenceController.deleteConference)
routes.route(routeNames.getConference).get(authJwt.verifyToken,
conferenceController.getConference)
routes.route(routeNames.getConferences).get(authJwt.verifyToken,
conferenceController.getConferences)
routes.route(routeNames.getMembers).get(authJwt.verifyToken,
conferenceController.getMembers)
routes.route(routeNames.close).post(authJwt.verifyToken, conferenceController.close)
export default routes
First of all, we create an Express Router
. Then, we create routes using its name, its method, middlewares and its controller.
routeNames
contains conferenceRoutes
route names:
export default {
create: '/api/create-conference',
update: '/api/update-conference/:conferenceId',
addMember: '/api/add-member/:conferenceId/:userId',
removeMember: '/api/remove-member/:conferenceId/:userId',
delete: '/api/delete-conference/:conferenceId',
getConference: '/api/get-conference/:conferenceId',
getConferences: '/api/get-conferences/:userId/:isPrivate/:page/:pageSize',
getMembers: '/api/get-members/:conferenceId',
close: '/api/close-conference/:userId/:conferenceId'
}
conferenceController
contains the main business logic regarding conferences. We are not going to see all the source code of the controller since it's quite large but we'll take create
and getConferences
controller functions for example.
Below is Conference
model:
import mongoose from 'mongoose'
const Schema = mongoose.Schema
const conferenceSchema = new Schema({
title: {
type: String,
required: [true, "can't be blank"],
index: true
},
description: {
type: String,
index: true
},
isPrivate: {
type: Boolean,
default: true
},
isLive: {
type: Boolean,
default: false
},
speaker: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User'
},
members: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
broadcastedAt: {
type: Date
},
finishedAt: {
type: Date
},
}, {
timestamps: true,
strict: true,
collection: 'Conference'
})
const conferenceModel = mongoose.model('Conference', conferenceSchema)
conferenceModel.on('index', (err) => {
if (err) {
console.error('Conference index error: %s', err)
} else {
console.info('Conference indexing complete')
}
})
export default conferenceModel
A conference is composed of a title, an optional description, isPrivate
and isLive
flags, a speaker, members, broadcast and finish date times.
Below is create
controller function:
export const create = (req, res) => {
const conference = new Conference(req.body)
conference.save()
.then(conf => {
res.status(200).json(conf)
})
.catch(err => {
console.error(strings.DB_ERROR, err)
res.status(400).send(strings.DB_ERROR + err)
})
}
In this function, we retrieve the body of the request and we create the conference.
Below is getConferences
controller function:
export const getConferences = (req, res) => {
const page = parseInt(req.params.page)
const pageSize = parseInt(req.params.pageSize)
let query = { speaker: req.params.userId }
if (req.params.isPrivate === 'false') {
query.isPrivate = false
}
Conference.find(query, null, { skip: ((page - 1) * pageSize), limit: pageSize })
.sort({ createdAt: -1 })
.then(confs => {
res.json(confs)
})
.catch(err => {
console.error(strings.DB_ERROR, err)
res.status(400).send(strings.DB_ERROR + err)
})
}
In this controller function, we retrieve the conferences depending on the page
, pageSize
, userId
and isPrivate
flag.
The frontend
is a web application built with Node.js, React and MUI.
- ./frontend/assets/ folder contains CSS files.
- ./frontend/pages/ folder contains React pages.
- ./frontend/components/ folder contains React components.
- ./frontend/services/ contains
Wexstream
API client services. - ./frontend/App.js is the main React App that contains routes.
- ./frontend/index.js is the main entry point of the frontend.
App.js is the main react App:
import React, { lazy, Suspense } from 'react'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
const SignIn = lazy(() => import('./pages/SignIn'))
const SignUp = lazy(() => import('./pages/SignUp'))
const Home = lazy(() => import('./pages/Home'))
const Conference = lazy(() => import('./pages/Conference'))
const Profile = lazy(() => import('./pages/Profile'))
const Search = lazy(() => import('./pages/Search'))
const Notifications = lazy(() => import('./pages/Notifications'))
const Connections = lazy(() => import('./pages/Connections'))
const ToS = lazy(() => import('./pages/ToS'))
const About = lazy(() => import('./pages/About'))
const Messages = lazy(() => import('./pages/Messages'))
const Settings = lazy(() => import('./pages/Settings'))
const ResetPassword = lazy(() => import('./pages/ResetPassword'))
const Contact = lazy(() => import('./pages/Contact'))
const NoMatch = lazy(() => import('./pages/NoMatch'))
const App = () => (
<Router>
<div className="App">
<Suspense fallback={<></>}>
<Routes>
<Route exact path="/sign-in" element={<SignIn />} />
<Route exact path="/sign-up" element={<SignUp />} />
<Route exact path="/" element={<Home />} />
<Route exact path="/home" element={<Home />} />
<Route exact path="/conference" element={<Conference />} />
<Route exact path="/profile" element={<Profile />} />
<Route exact path="/search" element={<Search />} />
<Route exact path="/notifications" element={<Notifications />} />
<Route exact path="/connections" element={<Connections />} />
<Route exact path="/messages" element={<Messages />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/reset-password" element={<ResetPassword />} />
<Route exact path="/tos" element={<ToS />} />
<Route exact path="/about" element={<About />} />
<Route exact path="/contact" element={<Contact />} />
<Route path="*" element={<NoMatch />} />
</Routes>
</Suspense>
</div>
</Router>
)
export default App
We are using React lazy loading to load each route.
Below is the main function for starting a conference in Conference
page:
import { JITSI_API, JITSI_HOST, isMobile } from '../config/env'
const startConf = () => {
setConference(conference)
const script = document.createElement('script')
script.src = JITSI_API
script.id = 'external-api'
script.setAttribute('defer', 'defer')
document.body.appendChild(script)
const externalApi = document.getElementById('external-api')
externalApi.addEventListener('load', () => {
if (window.JitsiMeetExternalAPI) {
if (!conference.isLive && (conference.speaker._id === user._id)) {
updateConf(conference,
{ isLive: true, broadcastedAt: Date.now() }, () => {
setConferenceUrl(window.location.href)
setStartConf(true)
})
} else {
setConferenceUrl(window.location.href)
setStartConf(true)
}
} else {
setLoading(false)
setExternalApiError(true)
}
})
In this function, we load Jitsi external API.
Jitsi is a set of open source projects which empower users to use and deploy video conferencing platforms with state-of-the-art video quality and features.
We then start the conference through the following function:
import React, { useCallback, useEffect, useState } from 'react'
import { JITSI_API, JITSI_HOST, isMobile } from '../config/env'
let domain = JITSI_HOST
let api = {}
const startConference = useCallback(() => {
localStorage.removeItem('jitsiLocalStorage')
localStorage.setItem('jitsiLocalStorage',
JSON.stringify({ language: user.language }))
const options = {
roomName: conference._id,
width: '100%',
height: '100%',
configOverwrite: {
prejoinPageEnabled: false,
useHostPageLocalStorage: true,
disableDeepLinking: true,
disabledNotifications: ['dialog.thankYou']
},
interfaceConfigOverwrite: {
},
parentNode: document.querySelector('#conf'),
userInfo: {
displayName: user.fullName
},
lang: user.language,
jwt: user.accessToken
}
api = new window.JitsiMeetExternalAPI(domain, options)
api.addEventListeners({
readyToClose: handleClose,
videoConferenceJoined: handleVideoConferenceJoined,
videoConferenceLeft: handleVideoConferenceLeft,
participantLeft: handleParticipantLeft,
participantJoined: handleParticipantJoined,
participantRoleChanged: handleParticipantRoleChanged,
participantKickedOut: handleParticipantKickedOut,
audioMuteStatusChanged: handleMuteStatus,
videoMuteStatusChanged: handleVideoStatus
})
window.onbeforeunload = (e) => {
localStorage.removeItem('jitsiLocalStorage')
setExit(true)
}
if (conference.isLive && conference.speaker._id === user._id) {
createTimelineEntries(user._id, conference._id, true)
}
}, [user, conference])
In the function above, we create a new Jitsi API with the options and the event listeners, we start the conference, and create the timeline entries.
We are not going to cover each page of the frontend, but you can open the source code and see each one if you want to.
Preamble
Wexstream
lets you stay in touch with all your teams, family, friends, or colleagues. Instant video conferences, efficiently adapting to your scale for free.
Wexstream
was developed using most recent and performant technologies, which allows the platform to handle a large number of conferences and participants.
- Unlimited users: There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors.
- Privacy settings, passwords and meeting locks puts the control in your hands.
- Lock-protected rooms: Control the access to your conferences with a password.
- Desktop screen sharing, chat, and many useful features.
- Encrypted by default.
- Protected conferences using TLS encryption and end-to-server/transit encryption.
- High quality: Audio and video are delivered with the clarity and richness of Opus and VP8.
- Web ready: No downloads are required of your friends to join the conversation. Wexstream works directly within their browsers as well. Simply share your conference URL with others to get started.
- Mobile ready: Accessible, legible, and usable across all devices.
- Users' personal data is neither resold nor communicated to third parties.
- Users have the right to access, modify, rectify and delete their personal data.
Subscribing
To access the services, the user must create an account by registering for free.
The user is required to provide true and accurate information which he undertakes to update immediately in the event of any changes.
Access to the platform is protected by a username and password chosen by the user when registering. The user is solely responsible for any use that may be made of his username and password, and sole guarantor of their confidentiality, as well as any use of his account.
In the event that the user provides false, inaccurate, outdated or incomplete data, Wexstream will be entitled to suspend or close his account and to refuse him, in the future, access to all or part of the services.
Services
Once registered, the user can benefit from the following services:
- Easy networking between platform members
- Provision of video conferencing tools
- Provision of communication tools between platform members
The platform works as follows:
- The user creates a network by connecting with others.
- The user broadcasts private or public conferences.
- When broadcasting a conference, the user gets a URL that he can share to invite others to join him.
- When broadcasting a conference, the user's network is notified.
Access to Services
Access to the platform and the services is exclusively reserved for registered users. Users broadcast online learning videos, webinars, lectures, or live streams, or attend online learning courses, webinairs, conferences, or live streams, and have access to IT and telecommunication resources allowing access to the platform.
The platform is accessible 24/7 for all users. Wexstream reserves the right, without notice or compensation, to temporarily or permanently close the platform or access to one or more services to make any update, modifications or change in operational methods, servers and accessibility hours, without this list being exhaustive. Wexstream reserves the right to make any modifications and improvements to the platform and to the services that Wexstream finds necessary or useful for the proper functioning of the platform and its services.
Litigations
Wexstream is not responsible for the collaboration between the broadcasters and the users, but Wexstream may be able to help resolve any dispute.
Commitments
Users undertake to make fair use of the platform and services and expressly refrain from circumventing the services and the platform. Consequently, any user is prohibited from extracting content from the platform for a similar or concurrent activity.
Responsibility
The user is responsible for the direct or indirect damage that he is likely to suffer as a result of inaccurate, incomplete, and/or misleading information that he provides when registering or in the absence of updating his information, for which he alone assumes the consequences.
The user is responsible for all the content he chooses to broadcast.
The user is expressly forbidden to publish any content, engage in any activity, stream any feed or create any account that is offensive, pornographic, violent, abusive, defamatory, threatening or obscene, illegal or intended to promote or commit an illegal act, including violations of intellectual property rights, privacy rights or proprietary rights, denigrating, slanderous, racist, xenophobic, contrary to morality and good morals, infringing content, undermining public order or rights, likely to infringe the rights, reputation and image of the platform and more generally, the content of which would violate the law and/or regulations, in particular of a criminal nature, includes his password, or purposely includes someone else's password, personal data, or is intended to solicit such data, misleads or deceives, or is likely to mislead or deceive, others as to his identity or affiliation with another person or organisation, breaches any of his obligations under the terms of use of the platform or any of its incorporated policies.
Privacy
Wexstream
protects your live and hosted content using TLS encryption and end-to-server/transit encryption. Plus, added privacy settings passwords and meeting locks puts the control in your hands.
Wexstream
is committed to using all means to ensure the security and privacy of users' personal data.
Users' personal data is neither resold nor communicated to third parties.
The user has the right to access, modify, rectify and delete his personal data.
Duration, Termination and Sanctions
This contract is concluded for an indefinite period from the acceptance of the terms of use of the platform by the user.
If the user does not respect these terms of use and/or commits any breach of the laws and regulations in force, Wexstream
is entitled to suspend or close the user's account, automatically, and to deny him, in the future, access to all or part of the services, without prejudice to any damages and interest that Wexstream
will be entitled to claim.
Modifications of Terms of Service
Wexstream
reserves the right to modify all or part of these conditions of use.
Wexstream
will inform the user of the modifications made to these general conditions as soon as they are posted on the platform.
Applicable Law and Competent Jurisdiction
Any dispute relating to the formation of these terms of service, conclusion, interpretation and/or execution falls under the exclusive jurisdiction of the courts.