fully working embedded player + chat
This commit is contained in:
parent
c497a55297
commit
67d81787eb
58
package-lock.json
generated
58
package-lock.json
generated
@ -589,6 +589,11 @@
|
||||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"foreachasync": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
|
||||
"integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY="
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
@ -658,6 +663,18 @@
|
||||
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
|
||||
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5",
|
||||
"neo-async": "^2.6.0",
|
||||
"source-map": "^0.6.1",
|
||||
"uglify-js": "^3.1.4",
|
||||
"wordwrap": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@ -670,6 +687,15 @@
|
||||
"integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
|
||||
"dev": true
|
||||
},
|
||||
"hbs": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/hbs/-/hbs-4.1.1.tgz",
|
||||
"integrity": "sha512-6QsbB4RwbpL4cb4DNyjEEPF+suwp+3yZqFVlhILEn92ScC0U4cDCR+FDX53jkfKJPhutcqhAvs+rOLZw5sQrDA==",
|
||||
"requires": {
|
||||
"handlebars": "4.7.6",
|
||||
"walk": "2.3.14"
|
||||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
@ -913,8 +939,7 @@
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
@ -926,6 +951,11 @@
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"neo-async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"nodemon": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
|
||||
@ -1293,6 +1323,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
@ -1421,6 +1456,12 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.12.5",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.5.tgz",
|
||||
"integrity": "sha512-SgpgScL4T7Hj/w/GexjnBHi3Ien9WS1Rpfg5y91WXMj9SY997ZCQU76mH4TpLwwfmMvoOU8wiaRkIf6NaH3mtg==",
|
||||
"optional": true
|
||||
},
|
||||
"undefsafe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
|
||||
@ -1484,6 +1525,14 @@
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||
},
|
||||
"walk": {
|
||||
"version": "2.3.14",
|
||||
"resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz",
|
||||
"integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==",
|
||||
"requires": {
|
||||
"foreachasync": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"widest-line": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||
@ -1493,6 +1542,11 @@
|
||||
"string-width": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"bad-words": "^3.0.4",
|
||||
"express": "^4.17.1",
|
||||
"hbs": "^4.1.1",
|
||||
"socket.io": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -90,21 +90,41 @@ button:disabled {
|
||||
}
|
||||
|
||||
.chat__sidebar {
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
top: 51vh;
|
||||
/* left: 1vh; */
|
||||
/* right: 1vh; */
|
||||
/* z-index: 2; */
|
||||
height: 49vh;
|
||||
color: rgb(223, 223, 223);
|
||||
background: #333744;
|
||||
width: 225px;
|
||||
width: 50%;
|
||||
overflow-y: scroll;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* embedded player div */
|
||||
.player {
|
||||
position: absolute;
|
||||
left: 1vh;
|
||||
top: 1vh;
|
||||
width: 49%;
|
||||
height: 49%;
|
||||
z-index: 1;
|
||||
/* margin-top: 5px; */
|
||||
/* margin-left: 5px; */
|
||||
}
|
||||
|
||||
/* Chat styles */
|
||||
|
||||
.chat__main {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100vh;
|
||||
height: 100vh;
|
||||
/* margin-left: 20vh; */
|
||||
background-color: rgb(233, 233, 233);
|
||||
}
|
||||
|
||||
.chat__messages {
|
||||
|
@ -17,17 +17,25 @@
|
||||
<div class="centered-form">
|
||||
<div class="centered-form__box">
|
||||
<h1>Join</h1>
|
||||
<form action="/chat.html">
|
||||
<form action="/chat">
|
||||
<label>Display name</label>
|
||||
<input type="text" name="username" placeholder="Display name" required>
|
||||
<label>Room</label>
|
||||
<input type="text" name="room" placeholder="Room" required>
|
||||
<button type="submit">Join</button>
|
||||
<input type="text" id="roomID" name="room" placeholder="Room" required>
|
||||
<button type="submit">Join or create</button>
|
||||
</form>
|
||||
<p class="myPar">or</p>
|
||||
<button type="submit">Generate room</button>
|
||||
<button type="submit" id="generateIdButton">Generate unique id</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
const generateIdButton = document.querySelector('#generateIdButton');
|
||||
const room = document.querySelector('#roomID');
|
||||
generateIdButton.addEventListener('click', e => {
|
||||
const id = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||||
room.value = id;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -7,6 +7,8 @@ const input = form.childNodes[1];
|
||||
const button = form.childNodes[3];
|
||||
const messageContainer = document.querySelector('#messages-container');
|
||||
const sidebarContainer = document.querySelector('.chat__sidebar');
|
||||
const changeVideoButton = document.querySelector('#changeVideo');
|
||||
const syncVideosButton = document.querySelector('#syncVideos');
|
||||
|
||||
// TEMPLATES
|
||||
const sidebarTemplate = `<h2 class="room-title">{{room}}</h2>
|
||||
@ -38,7 +40,83 @@ const htmlTemplate = [
|
||||
];
|
||||
|
||||
let message = '';
|
||||
// let user = window.prompt('Please state your username', 'User');
|
||||
let videoId = '';
|
||||
|
||||
// YT PLAYER LOGIC
|
||||
|
||||
// Load the IFrame Player API code asynchronously.
|
||||
var tag = document.createElement('script');
|
||||
tag.src = 'https://www.youtube.com/player_api';
|
||||
var firstScriptTag = document.getElementsByTagName('script')[0];
|
||||
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||
|
||||
// Replace the 'ytplayer' element with an <iframe> and
|
||||
// YouTube player after the API code downloads.
|
||||
var player;
|
||||
function onYouTubePlayerAPIReady() {
|
||||
player = new YT.Player('ytplayer', {
|
||||
height: '420',
|
||||
width: '860',
|
||||
videoId: 'xFqeAUKU09o',
|
||||
});
|
||||
|
||||
// STATE OF PLAYER
|
||||
player.addEventListener('onStateChange', (e) => {
|
||||
// 3 bufforing, 1 playing, 2 paused, -1 stopped entirely
|
||||
if (e.data !== 3) {
|
||||
const time = e.target.getCurrentTime();
|
||||
socket.emit('onPlayerState', e.data, time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ON PLAYER STATE DATA RECV
|
||||
socket.on('onPlayerState', (data, time) => {
|
||||
if (data === 1 || data === -1) {
|
||||
// player.seekTo(time, true);
|
||||
player.playVideo();
|
||||
} else if (data === 2) {
|
||||
if (Math.abs(player.getCurrentTime() - time) > 2) {
|
||||
player.seekTo(time, true);
|
||||
player.playVideo();
|
||||
} else {
|
||||
player.pauseVideo();
|
||||
}
|
||||
} else if (data === 0) player.stopVideo();
|
||||
});
|
||||
|
||||
const videoIdParse = (link) => {
|
||||
return link.slice(link.indexOf('v=') + 2);
|
||||
};
|
||||
|
||||
// SYNC VIDEOS
|
||||
syncVideosButton.addEventListener('click', () => {
|
||||
// console.log('syncing');
|
||||
const time = player.getCurrentTime();
|
||||
const id = videoIdParse(player.getVideoUrl());
|
||||
// console.log(time);
|
||||
socket.emit('videoSync', time, id);
|
||||
});
|
||||
|
||||
socket.on('videoSync', (time, id) => {
|
||||
// console.log(time, id);
|
||||
player.loadVideoById(id, time);
|
||||
// player.seekTo(time);
|
||||
});
|
||||
|
||||
// Change video
|
||||
changeVideoButton.addEventListener('click', () => {
|
||||
let link = input.value;
|
||||
if (!link) return alert('You should pass the youtube link');
|
||||
const videoId = videoIdParse(link);
|
||||
socket.emit('changeVideo', videoId);
|
||||
player.loadVideoById(`${videoId}`, 5, 'large');
|
||||
input.value = '';
|
||||
});
|
||||
|
||||
socket.on('changeVideo', (videoId) => {
|
||||
player.loadVideoById(`${videoId}`, 5, 'large');
|
||||
});
|
||||
|
||||
const autoscroll = () => {
|
||||
// New message elemnt
|
||||
@ -59,7 +137,6 @@ const autoscroll = () => {
|
||||
const scrolloffset = messageContainer.scrollTop + visibleHeight + 15;
|
||||
|
||||
if (containerHeight - newMessageHeight <= scrolloffset) {
|
||||
console.log('should scroll down');
|
||||
messageContainer.scrollTop = messageContainer.scrollHeight;
|
||||
}
|
||||
};
|
||||
@ -67,10 +144,9 @@ const autoscroll = () => {
|
||||
const { username, room } = Qs.parse(location.search, {
|
||||
ignoreQueryPrefix: true,
|
||||
});
|
||||
console.log(username, room);
|
||||
|
||||
socket.on('message', (object) => {
|
||||
console.log(object);
|
||||
// console.log(object);
|
||||
const string = object.text;
|
||||
const user = object.username;
|
||||
const createdAt = moment(object.createdAt).format('HH:mm:ss');
|
||||
@ -84,7 +160,7 @@ socket.on('message', (object) => {
|
||||
messageContainer.insertAdjacentHTML('beforeend', html);
|
||||
autoscroll();
|
||||
|
||||
console.log('not includes');
|
||||
// console.log('not includes');
|
||||
} else if (string.includes('Welcome to the server')) {
|
||||
const html = Mustache.render(htmlTemplate[2], {
|
||||
string,
|
||||
@ -115,7 +191,7 @@ form.addEventListener('submit', (e) => {
|
||||
// Re-enable form
|
||||
button.removeAttribute('disabled');
|
||||
if (error) return console.log(error);
|
||||
console.log('Message delivered');
|
||||
// console.log('Message delivered');
|
||||
input.value = '';
|
||||
input.focus();
|
||||
});
|
||||
@ -133,7 +209,7 @@ buttonLocation.addEventListener('click', (e) => {
|
||||
const coordsObj = `https://www.google.com/maps/?q=${latitude},${longitude}`;
|
||||
|
||||
socket.emit('message', coordsObj, () => {
|
||||
console.log('Location delivered');
|
||||
// console.log('Location delivered');
|
||||
buttonLocation.removeAttribute('disabled');
|
||||
});
|
||||
});
|
||||
@ -144,6 +220,10 @@ socket.on('roomData', ({ room, users }) => {
|
||||
sidebarContainer.innerHTML = html;
|
||||
});
|
||||
|
||||
socket.on('videoData', (videoData) => {
|
||||
player.loadVideoById(`${videoData}`, 5, 'large');
|
||||
});
|
||||
|
||||
socket.emit('join', { username, room }, (error) => {
|
||||
if (error) {
|
||||
alert(error);
|
||||
|
33
src/index.js
33
src/index.js
@ -12,14 +12,19 @@ const {
|
||||
} = require('./utils/users');
|
||||
|
||||
const app = express();
|
||||
app.set('view engine', 'hbs');
|
||||
app.engine('html', require('hbs').__express);
|
||||
const server = http.createServer(app);
|
||||
const io = socketio(server);
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
const publicDirectoryPath = path.join(__dirname, '../public');
|
||||
|
||||
app.use(express.static(publicDirectoryPath));
|
||||
|
||||
app.get('/chat', (req, res) => {
|
||||
res.render('chat.html', {});
|
||||
});
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
// Welcome message
|
||||
|
||||
@ -34,6 +39,7 @@ io.on('connection', (socket) => {
|
||||
'message',
|
||||
generateMessage(`Welcome to the server, ${username}!`)
|
||||
);
|
||||
|
||||
socket.broadcast
|
||||
.to(user.room)
|
||||
.emit('message', generateMessage(`${user.username} has joined`));
|
||||
@ -44,14 +50,24 @@ io.on('connection', (socket) => {
|
||||
callback();
|
||||
});
|
||||
|
||||
// On recv message
|
||||
socket.on('videoSync', (time, id) => {
|
||||
const user = getUser(socket.id);
|
||||
// console.log(time);
|
||||
io.to(user.room).emit('videoSync', time, id);
|
||||
});
|
||||
|
||||
socket.on('changeVideo', (videoId) => {
|
||||
const user = getUser(socket.id);
|
||||
if (!user) return;
|
||||
io.to(user.room).emit('changeVideo', videoId);
|
||||
});
|
||||
|
||||
socket.on('message', (message, callback) => {
|
||||
const user = getUser(socket.id);
|
||||
const filter = new Filter();
|
||||
|
||||
if (filter.isProfane(message)) message = filter.clean(message);
|
||||
//callback('Profanity is not allowed');
|
||||
console.log(user);
|
||||
try {
|
||||
io.to(user.room).emit(
|
||||
'message',
|
||||
@ -64,9 +80,17 @@ io.on('connection', (socket) => {
|
||||
callback();
|
||||
});
|
||||
|
||||
// Changing state of yt player after recv -> send data.e
|
||||
socket.on('onPlayerState', (data, time) => {
|
||||
const user = getUser(socket.id);
|
||||
if (user) {
|
||||
io.to(user.room).emit('onPlayerState', data, time);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
const user = removeUser(socket.id);
|
||||
if (user)
|
||||
if (user) {
|
||||
io.to(user.room).emit(
|
||||
'message',
|
||||
generateMessage(`${user.username} has left!`)
|
||||
@ -75,6 +99,7 @@ io.on('connection', (socket) => {
|
||||
room: user.room,
|
||||
users: getUsersInRoom(user.room),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('sendLocation', (object, callback) => {
|
||||
|
@ -20,8 +20,9 @@
|
||||
|
||||
<body>
|
||||
<div class="chat">
|
||||
<div class="chat__sidebar">
|
||||
<div class = player id="ytplayer"></div>
|
||||
|
||||
<div class="chat__sidebar">
|
||||
</div>
|
||||
<div class="chat__main">
|
||||
<div id="messages-container" class="chat__messages"> </div>
|
||||
@ -31,7 +32,11 @@
|
||||
<input placeholder="Write your message">
|
||||
<button type="submit" id="formButton">Send</button>
|
||||
</form>
|
||||
<button id="sendLocation">Send location!</button>
|
||||
<button id="sendLocation">Send location</button>
|
||||
<hr style="padding: 6px; border: 0 none; height: 5px; color: rgb(233, 233, 233)">
|
||||
<button id="changeVideo">Change video</button>
|
||||
<hr style="padding: 6px; border: 0 none; height: 5px; color: rgb(233, 233, 233)">
|
||||
<button id="syncVideos">Synchronize videos</button>
|
||||
</div>
|
||||
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user