Reputation: 175
Hi there I hope you guys are doing well.
I am learning nodeJs and trying to create video call where user can join by link. I am also using the mongoDB for rooms creation and data handling.
The issue i am facing is when I create a video call and share the link with someone instead of joining the same room new video call starts as the user still remain one.
Here is my app.js code:
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const crypto = require('crypto');
const secret = crypto.randomBytes(32).toString('hex');
const MONGODB_URI = 'mongodb+srv://**********:*******@******.majjl.mongodb.net/?retryWrites=true&w=majority&appName=******';
const http = require('http');
const csrf = require('csurf')
const {ExpressPeerServer} = require('peer')
// 1-Define controllers imports and Models
const errorController = require('./controllers/error');
const User = require ('./models/user')
// 2- Copre import like express Store
const app = express();
const store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions'
});
const csrfProtection = csrf()
app.set('view engine', 'ejs');
app.set('views', 'views');
// 3- Routes Imports in same order
const authRoutes = require('./routes/auth');
const homeRoutes = require('./routes/home');
const flash = require('connect-flash/lib/flash');
app.use(bodyParser.urlencoded({ extended: false }));
// app.use(multer({storage:fileStorage, fileFilter:fileFilter}).single('imageUrl'));
app.use(express.static(path.join(__dirname, 'public')));
// app.use('/images',express.static(path.join(__dirname, 'images')));
app.use(session({
secret: secret,
resave: false,
saveUninitialized: false,
store: store,
cookie : {
maxAge: 1000 * 60 * 60 * 24 * 1
}
}));
app.use(csrfProtection)
app.use(flash())
app.use((req,res,next)=>{
res.locals.isAuthenticated=req.session.isLoggedIn;
res.locals.csrfToken=req.csrfToken();
next();
})
app.use((req,res,next)=>{
if(!req.session.user)
{
return next();
}
User.findById(req.session.user)
.then(user=>{
if(!user)
{
return next();
}
req.user=user;
next()
})
.catch(err=>{
next(new Error(err))
})
})
// Use authentication routes
app.use(authRoutes);
app.use(homeRoutes);
app.use(errorController.get404);
app.use(errorController.get500);
app.use((err, req, res, next) => {
res.status(err.httpStatusCode || 500);
res.render('500', {
statusCode: err.httpStatusCode || 500,
message: err.message || 'Something went wrong!'
});
});
// Create HTTP server
const server = http.createServer(app);
const io = require('socket.io')(server)
// Integrate Peer server with the existing HTTP server
const peerServer = ExpressPeerServer(server, {
debug: true
});
app.use('/peerjs', peerServer);
io.on('connection',(socket)=>{
socket.on('join-room',(roomId,userId)=>{
socket.join(roomId);
setTimeout(()=>{
socket.io(roomId).broadcast.emit('user-connected',userId)
},1000)
socket.on('disconnect',()=>{
console.log("User Disconnected");
io.emit("user-disconnected",userId)
})
})
})
mongoose
.connect(MONGODB_URI)
.then(result => {
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
})
.catch(err => console.log(err));
Here is my rooms model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const roomSchema = new Schema({
roomId: {
type:String,
required:true,
},
adminName: {
type:String,
required:true,
},
adminEmail: {
type:String,
required:true,
},
isEnded:{
type:Boolean,
required:true,
},
inCall:{
type:Boolean,
required:true,
},
roomUsers:{
users : [
{
userId:{
type: Schema.Types.ObjectId,
required: true,
ref: 'User'
},
userName: {
type: Schema.Types.String,
required: true,
ref: 'User'
}
}
]
},
});
module.exports=mongoose.model('Room',roomSchema);
Here is my rooms controller:
const User = require('../models/user')
const Room = require('../models/room')
const bcrypt = require('bcryptjs')
const crypto = require('crypto');
const { validationResult } = require('express-validator')
exports.startRoomCall = async (req, res, next)=> {
const uuid = crypto.randomUUID();
try{
const room =await Room.findOne({adminEmail:req.session.user.email});
console.log(room)
if(room)
{
const error = new Error("You cannot create more then one call at same time");
error.httpStatusCode=500;
return next(error);
}else{
const room = new Room({
roomId : uuid,
adminName : req.session.user.fullName,
adminEmail : req.session.user.email,
isEnded: false,
inCall : true,
roomUsers : {
users : []
}
})
await room.save();
return res.render('room/room', {
path: '/room',
pageTitle: 'Call Room',
roomId: uuid
})
}
}
catch(err){
const error = new Error(err);
error.httpStatusCode=500;
return next(error);
}
}
exports.joinRoom = async (req, res, next) => {
const id = req.params.roomId;
try {
const room = await Room.findOne({ roomId: id });
if (!room) {
const error = new Error("Call does not exist.");
error.httpStatusCode = 404;
return next(error);
}
// Check if the user is the admin of the room trying to join
if (room.adminEmail === req.session.user.email) {
const error = new Error("You cannot join your own call.");
error.httpStatusCode = 400;
return next(error);
}
// Check if the room has ended
if (room.isEnded) {
const error = new Error("This call has already ended.");
error.httpStatusCode = 400;
return next(error);
}
// Update room with the new user
const existingUser = room.roomUsers.users.find(
(user) => user.userId.toString() === req.session.user._id.toString()
);
if (!existingUser) {
room.roomUsers.users.push({
userId: req.session.user._id,
userName: req.session.user.fullName,
});
await room.save();
}
// Render the room view with updated room data
res.render('room/room', {
path: '/room',
pageTitle: 'Call Room',
roomId: id,
});
} catch (err) {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
}
};
Here are my room routes:
const express = require('express');
const router = express.Router();
const homeController = require('../controllers/home')
const isAuth = require('../middleware/is-auth')
const { check, body } = require('express-validator')
router.get('/',isAuth, homeController.getHome),
router.post('/logout',isAuth,homeController.postLogout),
router.get('/update-password',isAuth,homeController.getUpdatePassword),
router.post('/update-password',isAuth,[ body('newPassword')
.isStrongPassword()
.withMessage('Password should be minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1.').trim(),
body('confirmPassword')
.custom((value, { req }) => {
if (value !== req.body.newPassword) {
throw new Error('Confirm password did not match');
}
return true;
}),],homeController.postUpdatePassword)
router.post('/room',isAuth, homeController.startRoomCall),
router.get('/room/:roomId',isAuth, homeController.joinRoom),
module.exports = router;
Here is my script.js:
const socket = io("/")
let myVideoStream;
const videoGrid = document.getElementById('video-grid');
const myVideo = document.createElement('video')
var peer = new Peer(undefined,{
path: "/peerjs",
host: "/",
port: "3000"
})
myVideo.muted=true;
navigator.mediaDevices.getUserMedia({
audio:true,
video: true,
}).then(stream=>{
myVideoStream=stream;
addVideoStream(myVideo,stream)
peer.on("call",(call)=>{
call.answer(stream)
const video = document.createElement("video");
call.on("stream",(userVideoStream)=>{
addVideoStream(video,userVideoStream)
})
})
socket.on("user-connected",(userId)=>{
connectToNewUser(userId,stream)
})
});
const connectToNewUser = (userId,stream)=>{
const call = peer.call(userId,stream);
const video = document.createElement("video")
call.on("stream",(userVideoStream)=>{
addVideoStream(video,userVideoStream)
})
}
peer.on("open",(id)=>{
socket.emit("join-room",ROOM_ID,id)
})
const addVideoStream = (video,stream)=>{
video.srcObject=stream;
video.addEventListener('loadedmetadata',()=>{
video.play(),
videoGrid.append(video)
})
}
const inviteButton = document.querySelector("#inviteButton")
const muteButton = document.querySelector("#muteButton")
const stopVideo = document.querySelector("#stopVideo")
const disconnectBtn = document.querySelector("#disconnect");
muteButton.addEventListener("click",() => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if(enabled){
myVideoStream.getAudioTracks()[0].enabled = false;
html = `<i class="fas fa-microphone-slash"></i>`;
muteButton.classList.toggle("background_red");
muteButton.innerHTML = html;
}
else{
myVideoStream.getAudioTracks()[0].enabled = true;
html = `<i class="fas fa-microphone"></i>`;
muteButton.classList.toggle("background_red");
muteButton.innerHTML = html;
}
})
stopVideo.addEventListener("click",() => {
const enabled = myVideoStream.getVideoTracks()[0].enabled;
if(enabled){
myVideoStream.getVideoTracks()[0].enabled = false;
html = `<i class="fas fa-video-slash"></i>`;
stopVideo.classList.toggle("background_red");
stopVideo.innerHTML = html;
}
else{
myVideoStream.getVideoTracks()[0].enabled = true;
html = `<i class="fas fa-video"></i>`;
stopVideo.classList.toggle("background_red");
stopVideo.innerHTML = html;
}
})
disconnectBtn.addEventListener("click",() => {
peer.destroy();
const myVideoElement = document.querySelector("video");
if(myVideoElement){
myVideoElement.remove();
}
socket.emit("disconnect");
window.location.href = "https://localhost:3000/";
})
inviteButton.addEventListener("click", async() => {
prompt("Copy this link and send it to people you want to have video call with",
window.location.href+"/"+ROOM_ID
);
})
Here is room.ejs for view:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>videoChatApp</title>
<link rel="stylesheet" href="/css/room.css" />
<script src="/socket.io/socket.io.js"></script>
<script src="https://kit.fontawesome.com/c939d0e917.js"></script>
<script src="https://unpkg.com/[email protected]/dist/peerjs.min.js"></script>
<script>
const ROOM_ID = "<%= roomId %>";
</script>
</head>
<body>
<div class="header">
<div class="logo">
<div class="header_back">
<i class="fas fa-angle-left"></i>
</div>
<h3>Video Chat</h2>
</div>
</div>
<div class="main">
<div class="main_left">
<div class="videos_group">
<div id="video-grid">
</div>
</div>
<div class="options">
<div class="options_left">
<div id="stopVideo" class="options_button">
<i class="fa fa-video-camera"></i>
</div>
<div id="muteButton" class="options_button">
<i class="fa fa-microphone"></i>
</div>
<div id="disconnect" class="options_button background_red">
<i class="fa fa-phone"></i>
</div>
</div>
<div class="options_right">
<div id="inviteButton" class="options_button">
<i class="fas fa-user-plus"></i>
</div>
</div>
</div>
</div>
</div>
</body>
<script src="/js/script.js"></script>
</html>
I KNOW ITS BEEN A LONG CODE POST BUT I TRIED TO PROVIDE ALL THE RELEVANT DETAILS WHICH CAN HELP TO FIGURE OUT THE ISSUE.
Any help will be appreciated please.
Upvotes: 0
Views: 34