@@ -497,6 +524,31 @@ export default class Chat extends Component {
}
labelPlacement="end"
/>
+ {
+ admin ?
+
+ }
+ label={
+
+ Admin Only Mode (can be changed later)
+
+
+ {adminOnly ? 'Only admin can update player right now' : 'All party members can update player right now'}
+
+
+ }
+ labelPlacement="end"
+ /> : null
+ }
PARTY MEMBERS
diff --git a/app/src/Create.js b/app/src/Create.js
index 489da53..0358268 100644
--- a/app/src/Create.js
+++ b/app/src/Create.js
@@ -18,7 +18,7 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
************************************************************************/
/*global chrome */
import React, { Component } from 'react';
-import { Button, TextField, Typography, Paper, ButtonGroup } from '@material-ui/core';
+import { Button, FormControlLabel, Switch, TextField, Typography, Paper, ButtonGroup } from '@material-ui/core';
import {
JoinRoomApi,
CreateRoomApi,
@@ -52,7 +52,8 @@ export default class Create extends Component {
name: '',
pin: '',
mode: defaultMode,
- parties: []
+ parties: [],
+ adminOnly: false,
};
this.createCall = this.createCall.bind(this);
this.joinCall = this.joinCall.bind(this);
@@ -72,7 +73,7 @@ export default class Create extends Component {
type: 'GET_PARTIES'
},
(response) => {
- this.setState({parties: response});
+ this.setState({ parties: response });
}
);
}
@@ -133,6 +134,7 @@ export default class Create extends Component {
time: 0,
url: window.location.href,
},
+ adminOnly: this.state.adminOnly,
};
CreateRoomApi(apidata)
.then((res) => res.json())
@@ -179,21 +181,21 @@ export default class Create extends Component {
this.props.updateLoading(false);
goTo(Chat, this.props);
}
- partyNameAutocompleteProcessor (v) {
- if (v !== null && v !== undefined && typeof(v) === 'object') {
+ partyNameAutocompleteProcessor(v) {
+ if (v !== null && v !== undefined && typeof (v) === 'object') {
if (v.name) this.partyNameChange(v.name);
else this.partyNameChange('');
if (v.pin) this.partyPinChange(v.pin);
else this.partyPinChange('');
- } else if (typeof(v) === 'object' && (v === null || v === undefined)) {
+ } else if (typeof (v) === 'object' && (v === null || v === undefined)) {
this.partyNameChange('');
this.partyPinChange('');
- } else if (v !== undefined && v !== null && typeof(v) === 'string') {
+ } else if (v !== undefined && v !== null && typeof (v) === 'string') {
this.partyNameChange(v);
}
- }
+ }
render() {
- const { mode, name, pin, userName, parties } = this.state;
+ const { mode, name, pin, userName, parties, adminOnly } = this.state;
return (
this.partyNameAutocompleteProcessor(v)}
+ onInputChange={(e, v, r) => this.partyNameAutocompleteProcessor(v)}
onChange={(e, v) => this.partyNameAutocompleteProcessor(v)}
- renderInput={(params) =>
+ renderInput={(params) =>
}
+ />}
/>
this.partyPinChange(e.target.value)}
helperText={pin ? `${pin.length}/${CONSTANTS.PIN_LENGTH} Digits` : null}
/>
+ {
+ mode === 'create' ?
+ this.setState({ adminOnly: !adminOnly })}
+ name="adminOnly"
+ color={adminOnly ? 'secondary' : 'primary'}
+ />
+ }
+ label={
+
+ Admin Only Mode (can be changed later)
+
+
+ {adminOnly ? 'Only admin will update currently playing' : 'All members will update currently playing'}
+
+
+ }
+ labelPlacement="end"
+ /> : null
+ }
{
bodydata.uid = user.uid;
req.body = JSON.stringify(bodydata);
}
- if (req.method === 'GET' || req.method === 'DELETE') {
+ if (req.method === 'GET' || req.method === 'DELETE' || req.method === 'PUT') {
req.body = JSON.stringify({ uid: user.uid });
}
return next();
diff --git a/functions/modules/createRoom.js b/functions/modules/createRoom.js
index 257dd16..714e50a 100644
--- a/functions/modules/createRoom.js
+++ b/functions/modules/createRoom.js
@@ -24,6 +24,7 @@ module.exports = async (req, res) => {
createdAt: new Date(),
updatedAt: new Date(),
adminUid: bodydata.uid,
+ adminOnly: bodydata.adminOnly,
})
.catch((err) => {
res.status(500).send({ message: 'Unable to start party.' });
@@ -46,23 +47,6 @@ module.exports = async (req, res) => {
err
);
});
- await admin
- .firestore()
- .collection('rooms')
- .doc(bodydata.partyName)
- .collection('messages')
- .add({
- uid: '',
- name: '',
- text: `Voice calls are unavailable temporarily. Voice calls will be back within 12 hours.`,
- time: new Date(),
- })
- .catch((err) => {
- console.error(
- `Failed to add system voice call message : \n\t Room: ${bodydata.partyName}`,
- err
- );
- });
await admin
.firestore()
.collection('rooms')
diff --git a/functions/modules/deleteRoom.js b/functions/modules/deleteRoom.js
index e0b37fb..e872ca6 100644
--- a/functions/modules/deleteRoom.js
+++ b/functions/modules/deleteRoom.js
@@ -2,7 +2,6 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => {
console.log('Requested Delete - Room - ', req.params.rid);
- console.log(req.body);
const isAdmin = await admin
.firestore()
.collection('rooms')
diff --git a/functions/modules/index.js b/functions/modules/index.js
index 19d7b75..b3888b1 100644
--- a/functions/modules/index.js
+++ b/functions/modules/index.js
@@ -11,6 +11,7 @@ const updatePlayer = require('./updatePlayer');
const tokenValidator = require('./tokenValidator');
const tokenRefresher = require('./tokenRefresher');
const updateVoxeet = require('./updateVoxeet');
+const updateAdminOnly = require('./updateAdminOnly');
module.exports = {
authenticator,
@@ -26,4 +27,5 @@ module.exports = {
tokenValidator,
tokenRefresher,
updateVoxeet,
+ updateAdminOnly,
};
diff --git a/functions/modules/joinRoom.js b/functions/modules/joinRoom.js
index 03732f2..0f4192d 100644
--- a/functions/modules/joinRoom.js
+++ b/functions/modules/joinRoom.js
@@ -1,5 +1,4 @@
const admin = require('firebase-admin');
-const { firestore } = require('firebase-admin');
module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body);
@@ -137,7 +136,7 @@ module.exports = async (req, res) => {
.update({
player: {
showName: null,
- source: null,
+ source: bodydata.source,
status: 'NOT_PLAYING',
time: 0,
updatedBy: bodydata.uid,
diff --git a/functions/modules/tokenRefresher.js b/functions/modules/tokenRefresher.js
index 92def06..8981197 100644
--- a/functions/modules/tokenRefresher.js
+++ b/functions/modules/tokenRefresher.js
@@ -1,21 +1,20 @@
const VoxeetSecret = require('../VoxeetSecret.json');
module.exports = async (req, res) => {
- res.status(403).send({ message: 'Voice calls are unavailable temporarily.' });
- // const { key, secret } = VoxeetSecret;
- // const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret));
- // const headers = {
- // 'Content-Type': 'application/json',
- // 'Cache-Control': 'no-cache',
- // Authorization: 'Basic ' + credentials,
- // };
- // const oauth = await fetch(VoxeetSecret.refreshURL, {
- // method: 'POST',
- // headers,
- // body: JSON.stringify({
- // refresh_token: req.params.token,
- // }),
- // })
- // .then((oauthResponse) => oauthResponse.json())
- // .catch((err) => res.status(500).send(err));
- // if (oauth) res.status(200).send(oauth);
+ const { key, secret } = VoxeetSecret;
+ const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret));
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache',
+ Authorization: 'Basic ' + credentials,
+ };
+ const oauth = await fetch(VoxeetSecret.refreshURL, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ refresh_token: req.params.token,
+ }),
+ })
+ .then((oauthResponse) => oauthResponse.json())
+ .catch((err) => res.status(500).send(err));
+ if (oauth) res.status(200).send(oauth);
};
diff --git a/functions/modules/tokenValidator.js b/functions/modules/tokenValidator.js
index 5173201..dc28d91 100644
--- a/functions/modules/tokenValidator.js
+++ b/functions/modules/tokenValidator.js
@@ -1,22 +1,21 @@
const VoxeetSecret = require('../VoxeetSecret.json');
const fetch = require('node-fetch');
module.exports = async (req, res) => {
- res.status(403).send({ message: 'Voice calls are unavailable temporarily.' });
- // const { key, secret } = VoxeetSecret;
- // const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
- // const headers = {
- // 'Content-Type': 'application/json',
- // 'Cache-Control': 'no-cache',
- // Authorization: 'Basic ' + credentials,
- // };
- // const oauth = await fetch(VoxeetSecret.validateURL, {
- // method: 'POST',
- // headers,
- // body: JSON.stringify({
- // grant_type: 'client_credentials',
- // }),
- // })
- // .then((oauthResponse) => oauthResponse.json())
- // .catch((err) => res.status(500).send(err));
- // if (oauth) res.status(200).send(oauth);
+ const { key, secret } = VoxeetSecret;
+ const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'Cache-Control': 'no-cache',
+ Authorization: 'Basic ' + credentials,
+ };
+ const oauth = await fetch(VoxeetSecret.validateURL, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ grant_type: 'client_credentials',
+ }),
+ })
+ .then((oauthResponse) => oauthResponse.json())
+ .catch((err) => res.status(500).send(err));
+ if (oauth) res.status(200).send(oauth);
};
diff --git a/functions/modules/updateAdminOnly.js b/functions/modules/updateAdminOnly.js
new file mode 100644
index 0000000..8525377
--- /dev/null
+++ b/functions/modules/updateAdminOnly.js
@@ -0,0 +1,36 @@
+const admin = require('firebase-admin');
+
+module.exports = async (req, res) => {
+ const bodydata = JSON.parse(req.body);
+ const isAdmin = await admin
+ .firestore()
+ .collection('rooms')
+ .doc(req.params.rid)
+ .collection('users')
+ .doc(bodydata.uid)
+ .get()
+ .then((v) => {
+ if (v.exists) {
+ return v.data().admin;
+ }
+ return false;
+ })
+ .catch((err) => {
+ console.err('Failed to get party to update adminOnly', err);
+ return res.status(404).send({ message: 'Failed to get party.' });
+ });
+ if (!isAdmin) {
+ console.log('[UNAUTHORIZED] Requested Admin Mode Update - Room - ', req.params.rid);
+ res.status(401).send({ message: 'Not authorized to update admin only mode for this party.' });
+ return;
+ }
+ const adminOnly = req.params.adminOnly === 'true';
+ await admin.firestore().collection('rooms').doc(req.params.rid).update({
+ adminOnly
+ }).catch((err) => {
+ console.err('Failed to update adminOnly for party', err);
+ res.status(409).send({ message: 'Failed to update party.' });
+ });
+ res.status(200).send({ message: 'Updated admin only mode for party' });
+ return;
+};
diff --git a/functions/modules/updatePlayer.js b/functions/modules/updatePlayer.js
index 777a155..7ae9253 100644
--- a/functions/modules/updatePlayer.js
+++ b/functions/modules/updatePlayer.js
@@ -14,6 +14,7 @@ module.exports = async (req, res) => {
// player.time > 3600
// ? new Date(player.time * 1000).toISOString().substr(11, 8)
// : new Date(player.time * 1000).toISOString().substr(14, 5);
+ if (!roomId) return res.status(204).send({ message: 'Party name not sent.' });
const [roomExists, roomData] = await admin
.firestore()
.collection('rooms')
@@ -30,7 +31,7 @@ module.exports = async (req, res) => {
.get()
.then((userSnap) => userSnap.exists);
if (userExists) {
- if (showChanged === true) {
+ if (showChanged === true && player.showName) {
await admin
.firestore()
.collection('rooms')
diff --git a/functions/package-lock.json b/functions/package-lock.json
index 069b38c..86dbca4 100644
--- a/functions/package-lock.json
+++ b/functions/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "watch-party-api",
- "version": "1.2.1",
+ "version": "1.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1909,9 +1909,9 @@
}
},
"lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ "version": "4.17.19",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+ "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.at": {
"version": "4.6.0",
From 3dc4efb60d000331cb7f64ff9777930082e21fa2 Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sat, 1 Aug 2020 13:59:49 -0400
Subject: [PATCH 06/48] v1.6.0/loading color match
---
app/src/Loading.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/Loading.js b/app/src/Loading.js
index 50ffdd3..8917a2a 100644
--- a/app/src/Loading.js
+++ b/app/src/Loading.js
@@ -21,7 +21,7 @@ import { CircularProgress } from '@material-ui/core';
const Loading = () => (
-
+
);
From 67bc392968e47dacbe9405bea06741c28b5b1690 Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sat, 1 Aug 2020 14:32:00 -0400
Subject: [PATCH 07/48] v1.6.0/fix autocomplete with localdata
---
app/src/Create.js | 8 ++++----
app/src/index.css | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/src/Create.js b/app/src/Create.js
index 0358268..ee70e04 100644
--- a/app/src/Create.js
+++ b/app/src/Create.js
@@ -49,8 +49,8 @@ export default class Create extends Component {
const defaultMode = LocalData().partyName ? 'join' : 'create';
this.state = {
userName: LocalData().userName || '',
- name: '',
- pin: '',
+ name: LocalData().partyName || '',
+ pin: LocalData().partyPin || '',
mode: defaultMode,
parties: [],
adminOnly: false,
@@ -246,8 +246,8 @@ export default class Create extends Component {
getOptionLabel={o => o.name}
autoComplete
freeSolo
- inputValue={name}
- onInputChange={(e, v, r) => this.partyNameAutocompleteProcessor(v)}
+ value={{name:name, pin: pin}}
+ onInputChange={(e,v,r) => this.partyNameAutocompleteProcessor({name: v, pin: pin})}
onChange={(e, v) => this.partyNameAutocompleteProcessor(v)}
renderInput={(params) =>
Date: Sat, 1 Aug 2020 14:35:22 -0400
Subject: [PATCH 08/48] v1.6.0/auto complete color fix
---
app/src/index.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/index.css b/app/src/index.css
index e61f70e..d2894e4 100644
--- a/app/src/index.css
+++ b/app/src/index.css
@@ -614,9 +614,9 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
.MuiAutocomplete-popper {
z-index: 99999 !important;
+ color: white !important;
}
.admin-only-switch {
margin-bottom: 20px !important;
- color: white !important;
}
\ No newline at end of file
From ab33ea06ce10e0f80030d5dacd21af9e9c9b0c40 Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sat, 1 Aug 2020 14:37:48 -0400
Subject: [PATCH 09/48] v1.6.0/font color fix
---
app/src/index.css | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/index.css b/app/src/index.css
index d2894e4..870de2a 100644
--- a/app/src/index.css
+++ b/app/src/index.css
@@ -614,6 +614,9 @@ body.amazon-prime .notification-switch .MuiSwitch-thumb {
.MuiAutocomplete-popper {
z-index: 99999 !important;
+}
+
+.MuiAutocomplete-popper * {
color: white !important;
}
From c80a99cea1531a137cfb618601a831bec9d2e0da Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sat, 1 Aug 2020 14:49:33 -0400
Subject: [PATCH 10/48] v1.6.0/optimize dependencies
---
app/package.json | 29 ++++++++++++++---------------
1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/app/package.json b/app/package.json
index af2a935..89714cf 100644
--- a/app/package.json
+++ b/app/package.json
@@ -10,23 +10,12 @@
"@material-ui/lab": "^4.0.0-alpha.56",
"@voxeet/voxeet-web-sdk": "^2.3.1",
"autoprefixer": "7.1.6",
- "babel-core": "6.26.3",
- "babel-eslint": "10.x",
- "babel-jest": "20.0.3",
- "babel-preset-react-app": "^9.0.1",
"babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
- "eslint": "6.x",
- "eslint-config-react-app": "^5.0.1",
- "eslint-loader": "2.2.1",
- "eslint-plugin-flowtype": "3.x",
- "eslint-plugin-import": "2.x",
- "eslint-plugin-jsx-a11y": "6.x",
- "eslint-plugin-react": "7.x",
"file-loader": "4.2.0",
"firebase": "^7.14.0",
"fs-extra": "3.0.1",
@@ -39,8 +28,6 @@
"prop-types": "^15.7.2",
"raf": "3.4.0",
"react": "^16.4.1",
- "react-chat-ui": "^0.3.2",
- "react-dev-utils": "^9.0.0-next.68",
"react-dom": "^16.4.1",
"react-frame-component": "^4.0.0",
"resolve": "1.6.0",
@@ -48,7 +35,6 @@
"style-loader": "0.19.0",
"sw-precache-webpack-plugin": "^0.11.5",
"url-loader": "0.6.2",
- "webpack-dev-server": "^3.11.0",
"webpack-manifest-plugin": "2.0.4",
"whatwg-fetch": "2.0.3"
},
@@ -111,6 +97,19 @@
"eslint-plugin-react-hooks": "^2.0.1",
"mini-css-extract-plugin": "^0.8.0",
"webpack": "^4.42.1",
- "webpack-cli": "^3.3.11"
+ "webpack-cli": "^3.3.11",
+ "babel-core": "6.26.3",
+ "babel-eslint": "10.x",
+ "babel-jest": "20.0.3",
+ "babel-preset-react-app": "^9.0.1",
+ "eslint": "6.x",
+ "eslint-config-react-app": "^5.0.1",
+ "eslint-loader": "2.2.1",
+ "eslint-plugin-flowtype": "3.x",
+ "eslint-plugin-import": "2.x",
+ "eslint-plugin-jsx-a11y": "6.x",
+ "eslint-plugin-react": "7.x",
+ "webpack-dev-server": "^3.11.0",
+ "react-dev-utils": "^9.0.0-next.68"
}
}
From 86ee1680451908cb1ff7dd1657a754825e00a4dd Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sun, 2 Aug 2020 01:11:48 -0400
Subject: [PATCH 11/48] v1.6.0/conference call upgrade - anyone can start call
- colision check for calls - better error handing for calls
---
app/src/Api.js | 4 +-
app/src/CallManager.js | 39 ++++++++++---
app/src/Chat.js | 8 ++-
functions/app.js | 9 ++-
functions/index.js | 2 +
functions/modules/tokenValidator.js | 51 +++++++++++++++--
functions/modules/updateVoxeet.js | 87 ++++++++++++++++++-----------
functions/webhooks/conferenceEnd.js | 30 ++++++++++
functions/webhooks/index.js | 5 ++
9 files changed, 184 insertions(+), 51 deletions(-)
create mode 100644 functions/webhooks/conferenceEnd.js
create mode 100644 functions/webhooks/index.js
diff --git a/app/src/Api.js b/app/src/Api.js
index d33b65b..3a5dcf1 100644
--- a/app/src/Api.js
+++ b/app/src/Api.js
@@ -149,9 +149,9 @@ export const UpdatePlayerApi = async (data) => {
});
};
-export const GetVoxeetTokenApi = async () => {
+export const GetVoxeetTokenApi = async (data) => {
return fetcher({
- endpoint: 'validator',
+ endpoint: `validator/${data.partyName}`,
method: 'GET',
});
};
diff --git a/app/src/CallManager.js b/app/src/CallManager.js
index bfc51d7..be96e4f 100644
--- a/app/src/CallManager.js
+++ b/app/src/CallManager.js
@@ -33,10 +33,12 @@ export default class CallManager {
this.__accessToken = LocalData().vrT;
this.__sessionInitialized = false;
this.__admin = false;
+ this.__adminOnly = true;
}
- toggleCall(onCall, conference, admin) {
+ toggleCall(onCall, conference, admin, adminOnly) {
this.listener({ callActionPending: true });
this.__admin = admin;
+ this.__adminOnly = adminOnly;
if (onCall === true) this.leaveCall();
else this.boot(conference);
}
@@ -56,13 +58,17 @@ export default class CallManager {
console.error('Failed to fetch conference', err);
this.startCall();
});
- } else if (opened && !conference) this.startCall();
- else console.error('Unable to open Voxeet Session');
+ } else if (opened && !conference) {
+ this.startCall();
+ } else {
+ this.listener({ onCall: false, muted: false, callActionPending: false });
+ console.error('Unable to open Voxeet Session');
+ }
}
async startCall() {
- if (this.__admin === true) {
+ if (!this.__adminOnly || (this.__adminOnly && this.__admin)) {
VoxeetSDK.conference
- .create({ alias: LocalData().partyName, pin: LocalData().partyPin })
+ .create({ alias: `AUTODELETE_${LocalData().partyName}`, pin: LocalData().partyPin })
.then(async (conference) => {
const updatedVoxeet = await UpdateVoxeetApi({
conference,
@@ -72,10 +78,21 @@ export default class CallManager {
})
.then((res) => res.json())
.then((res) => res.status <= 299)
- .catch((err) => console.error('Update Voxeet API Error', err));
+ .catch((err) => {
+ if (err.conference) {
+ this.joinCall(err.conference);
+ return true;
+ } else {
+ this.notify(err.message);
+ this.listener({ onCall: false, muted: false, callActionPending: false });
+ console.error('Update Voxeet API Error', err);
+ return false;
+ }
+ });
if (updatedVoxeet === true) {
this.joinCall(conference);
} else {
+ this.listener({ onCall: false, muted: false, callActionPending: false });
console.error('Failed to update party Voxeet');
}
});
@@ -87,7 +104,13 @@ export default class CallManager {
async joinCall(conference) {
const joined = await VoxeetSDK.conference
.join(conference, { constraints: { audio: true, video: false } })
- .then(() => true);
+ .then(() => true)
+ .catch(e => {
+ this.listener({ onCall: false, muted: false, callActionPending: false });
+ this.notify('Unable to join call. Try again in a moment.');
+ console.error('Error joining call', e);
+ return true;
+ });
if (!joined) {
this.startCall();
}
@@ -158,7 +181,7 @@ export default class CallManager {
this.__accessToken !== null ||
this.__refreshToken !== null
) {
- await GetVoxeetTokenApi()
+ await GetVoxeetTokenApi({ partyName: LocalData().partyName })
.then((res) => res.json())
.then((tokenResult) => {
if (tokenResult.status <= 299) {
diff --git a/app/src/Chat.js b/app/src/Chat.js
index 77c7e6a..616e966 100644
--- a/app/src/Chat.js
+++ b/app/src/Chat.js
@@ -235,6 +235,7 @@ export default class Chat extends Component {
const data = {
partyName: LocalData().partyName,
};
+ if (this.state.onCall) this.mCallManager.toggleCall(this.state.onCall, this.state.conference, this.state.admin, this.state.adminOnly);
DeleteRoomApi(data)
.then((res) => {
this.props.notify('Ended party');
@@ -255,6 +256,7 @@ export default class Chat extends Component {
partyName: LocalData().partyName,
userUid: LocalData().userUid,
};
+ if (this.state.onCall) this.mCallManager.toggleCall(this.state.onCall, this.state.conference, this.state.admin, this.state.adminOnly);
LeaveRoomApi(data)
.then((res) => {
this.props.notify('Left party');
@@ -312,7 +314,7 @@ export default class Chat extends Component {
: giphyFetch.search(this.state.newMessage, { offset, limit: 1 });
}
handlePhoneCall() {
- const { onCall, conference, admin } = this.state;
+ const { onCall, conference, admin, adminOnly } = this.state;
if (window.navigator && onCall === false) {
try {
const overlay = document.createElement('div');
@@ -328,7 +330,7 @@ export default class Chat extends Component {
})
.then(() => {
document.getElementById('mic-permission-overlay').remove();
- this.mCallManager.toggleCall(onCall, conference, admin);
+ this.mCallManager.toggleCall(onCall, conference, admin, adminOnly);
})
.catch(() => {
document.getElementById('mic-permission-overlay').remove();
@@ -339,7 +341,7 @@ export default class Chat extends Component {
} catch (err) {
console.error('Handle Phone Call Error', err);
}
- } else this.mCallManager.toggleCall(onCall, conference, admin);
+ } else this.mCallManager.toggleCall(onCall, conference, admin, adminOnly);
}
handleMute() {
this.mCallManager.toggleMute(this.state.muted);
diff --git a/functions/app.js b/functions/app.js
index 399ea2f..c4a460f 100644
--- a/functions/app.js
+++ b/functions/app.js
@@ -1,13 +1,19 @@
const FunctionModules = require('./modules/index');
const FunctionModules2 = require('./modules2/index');
+const Webhooks = require('./webhooks/index');
const express = require('express');
const cors = require('cors');
+// WEBHOOKS
+const webhooks = express();
+webhooks.use(cors({ origin: true }));
+webhooks.all('/conferenceEnded',Webhooks.conferenceEnd);
+
// VERSION 1
const app = express();
app.use(cors({ origin: true }));
app.use(FunctionModules.authenticator);
-app.get('/validator', FunctionModules.tokenValidator);
+app.get('/validator/:partyName', FunctionModules.tokenValidator);
app.get('/refresher/:token', FunctionModules.tokenRefresher);
app.post('/create', FunctionModules.createRoom);
app.post('/join', FunctionModules.joinRoom);
@@ -35,4 +41,5 @@ app2.delete('/delete/:rid', FunctionModules2.deleteRoom);
module.exports = {
v1: app,
v2: app2,
+ webhooks,
};
diff --git a/functions/index.js b/functions/index.js
index e1aad21..605dd62 100644
--- a/functions/index.js
+++ b/functions/index.js
@@ -19,3 +19,5 @@ exports.widgets2 = functions.https.onRequest(REST.v2);
exports.scheduledCleanUp = functions.pubsub
.schedule('every 60 minutes')
.onRun(FunctionModules.scheduledCleanUp);
+
+exports.webhooks = functions.https.onRequest(REST.webhooks);
diff --git a/functions/modules/tokenValidator.js b/functions/modules/tokenValidator.js
index dc28d91..de9bdc5 100644
--- a/functions/modules/tokenValidator.js
+++ b/functions/modules/tokenValidator.js
@@ -1,6 +1,9 @@
const VoxeetSecret = require('../VoxeetSecret.json');
+const admin = require('firebase-admin');
const fetch = require('node-fetch');
+
module.exports = async (req, res) => {
+ const bodyData = JSON.parse(req.body);
const { key, secret } = VoxeetSecret;
const credentials = Buffer.from(encodeURI(key) + ':' + encodeURI(secret)).toString('base64');
const headers = {
@@ -14,8 +17,48 @@ module.exports = async (req, res) => {
body: JSON.stringify({
grant_type: 'client_credentials',
}),
- })
- .then((oauthResponse) => oauthResponse.json())
- .catch((err) => res.status(500).send(err));
- if (oauth) res.status(200).send(oauth);
+ }).then((oauthResponse) => oauthResponse.json())
+ .catch((err) => res.status(500).send({err, message: 'Unable to generate token for call'}));
+
+ const [exists, conferencePending, conference, adminOnly, adminUid] = await admin
+ .firestore()
+ .collection('rooms')
+ .doc(req.params.partyName)
+ .get()
+ .then(snap => {
+ const data = snap.exists ? snap.data() : {};
+ return [snap.exists, data.conferencePending, data.conference, data.adminOnly, data.adminUid];
+ });
+
+ if (exists === true) {
+ if (conference === undefined || conference === null) {
+ if (conferencePending === undefined || conferencePending === null) {
+ if (!adminOnly || (adminOnly && (adminUid === bodyData.uid)) ) {
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(req.params.partyName)
+ .update({
+ conferencePending: bodyData.uid
+ });
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(req.params.partyName)
+ .collection('messages')
+ .add({
+ uid: '',
+ name: '',
+ text: 'A call session is being created (will be available soon).',
+ time: new Date(),
+ });
+ }
+ }
+ }
+ if (oauth) res.status(200).send(oauth);
+ } else {
+ res.status(400).send({ message: 'Unable to get party for call.' });
+ }
+
+ return;
};
diff --git a/functions/modules/updateVoxeet.js b/functions/modules/updateVoxeet.js
index 73ae0c0..386f0d9 100644
--- a/functions/modules/updateVoxeet.js
+++ b/functions/modules/updateVoxeet.js
@@ -1,47 +1,68 @@
const admin = require('firebase-admin');
module.exports = async (req, res) => {
- const { conference, partyName, partyPin, userName } = JSON.parse(req.body);
+ const { conference, partyName, partyPin, userName, uid } = JSON.parse(req.body);
// GET ROOM PIN
- const roomPin = await admin
+ const [exists, roomPin, conferencePending, partyConference] = await admin
.firestore()
.collection('rooms')
.doc(partyName)
.get()
- .then((roomSnap) => (roomSnap.exists ? roomSnap.data().pin : null));
+ .then((roomSnap) => {
+ const data = roomSnap.exists ? roomSnap.data() : {};
+ return [roomSnap.exists, data.pin, data.conferencePending, data.conference];
+ });
// CHECK IF ROOM EXISTS
- if (roomPin) {
- // CHECK FOR CORRECT PIN
- if (roomPin === partyPin) {
- // UPDATE CONFERENCE DETAILS
- await admin
- .firestore()
- .collection('rooms')
- .doc(partyName)
- .update({ conference })
- .catch((err) =>
- res.status(500).send({ message: 'Unable to update the call details', error: err })
- );
- // ADD SYSTEM MESSAGE
- await admin
- .firestore()
- .collection('rooms')
- .doc(partyName)
- .collection('messages')
- .add({
- uid: '',
- name: '',
- text: `${userName} started the call.`,
- time: new Date(),
- })
- .catch((err) => console.error('Failed to add update conference message', err));
- res.status(200).send({ message: 'Started the conference' });
- } else {
- // WRONG ROOM PIN
- return res.status(401).send({ message: 'Wrong party pin, try rejoining' });
+ if (exists === true) {
+ if (roomPin) {
+ // CHECK FOR CORRECT PIN
+ if (roomPin === partyPin) {
+ if (conferencePending) {
+ if (conferencePending === uid) {
+ // UPDATE CONFERENCE DETAILS
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(partyName)
+ .update({ conference })
+ .catch((err) =>
+ res.status(500).send({ message: 'Unable to update the call details', error: err })
+ );
+ // REMOVE PENDING STATUS
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(partyName)
+ .update({ conferencePending : admin.firestore.FieldValue.delete() })
+ .catch((err) => console.error('Failed to remove pending status', err));
+ // ADD SYSTEM MESSAGE
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(partyName)
+ .collection('messages')
+ .add({
+ uid: '',
+ name: '',
+ text: `${userName} started the call, everyone can join now.`,
+ time: new Date(),
+ })
+ .catch((err) => console.error('Failed to add update conference message', err));
+ res.status(200).send({ message: 'Started the conference' });
+ } else {
+ res.status(409).send({ message: 'Looks like someone is already starting a call' });
+ }
+ } else if (partyConference) {
+ res.status(409).send({ message: 'Call already exists', conference: partyConference });
+ }
+ } else {
+ // WRONG ROOM PIN
+ res.status(401).send({ message: 'Wrong party pin, try rejoining' });
+ }
}
} else {
// ROOM DOESN'T EXIST
- return res.status(404).send({ message: 'Party Not Found' });
+ res.status(404).send({ message: 'Party Not Found' });
}
+ return;
};
diff --git a/functions/webhooks/conferenceEnd.js b/functions/webhooks/conferenceEnd.js
new file mode 100644
index 0000000..60febff
--- /dev/null
+++ b/functions/webhooks/conferenceEnd.js
@@ -0,0 +1,30 @@
+const admin = require('firebase-admin');
+
+module.exports = async (req, res) => {
+ console.info(`[START]\tWEBHOOKS/Conference End\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`);
+ if (req.body.eventType === 'Conference.Ended') {
+ if (req.body.participants.length > 0) {
+ if (req.body.confAlias.startsWith('AUTODELETE_')) {
+ const partyName = req.body.confAlias.replace(/AUTODELETE_/, "");
+ admin.firestore()
+ .collection('rooms')
+ .doc(partyName)
+ .update({ conference: admin.firestore.FieldValue.delete() })
+ .then(() => {
+ console.info(`[DONE]\tWEBHOOKS/Conference End\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`);
+ return;
+ })
+ .catch(err => {
+ console.error(`[ERROR]\tWEBHOOKS/Conference End/Firebase Error\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}\nError:${JSON.stringify(err)}`)
+ });
+ res.status(200).send({ message: 'Processed conference end'});
+ }
+ } else {
+ console.info(`[DONE]\tWEBHOOKS/Conference End/End Of Duplicate Conference\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`);
+ }
+ } else {
+ console.error(`[ERROR]\tWEBHOOKS/Conference End/Wrong Event Type\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`)
+ res.status(400).send({ message: 'Wrong event type' });
+ }
+ return;
+};
diff --git a/functions/webhooks/index.js b/functions/webhooks/index.js
new file mode 100644
index 0000000..5cf0ebb
--- /dev/null
+++ b/functions/webhooks/index.js
@@ -0,0 +1,5 @@
+const conferenceEnd = require('./conferenceEnd');
+
+module.exports = {
+ conferenceEnd,
+};
From e388dbf283a4916abcf7bf5291c7de99e7f0c7a2 Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sun, 2 Aug 2020 20:41:52 -0400
Subject: [PATCH 12/48] v1.6.0/added support, cleanup webhook
---
app/public/background.js | 42 +++++-
app/public/manifest.json | 8 +-
app/src/Api.js | 4 +
app/src/VideoListener.js | 135 ++++++++++++++++--
app/src/index.css | 45 +++++-
app/src/index.js | 6 +
functions/app.js | 3 +-
functions/index.js | 7 +-
functions/modules/index.js | 4 -
functions/modules/updatePlayer.js | 53 ++-----
functions/modules2/updatePlayer.js | 4 +-
.../{modules => webhooks}/cleanUpRoom.js | 0
functions/webhooks/index.js | 2 +
.../{modules => webhooks}/scheduledCleanUp.js | 6 +-
14 files changed, 247 insertions(+), 72 deletions(-)
rename functions/{modules => webhooks}/cleanUpRoom.js (100%)
rename functions/{modules => webhooks}/scheduledCleanUp.js (92%)
diff --git a/app/public/background.js b/app/public/background.js
index 69fd28c..be9a26c 100644
--- a/app/public/background.js
+++ b/app/public/background.js
@@ -19,6 +19,9 @@ HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia
/* global chrome */
/* global firebase */
+const APP_VERSION = '1.6.0';
+const APP_VERSION_STRING = APP_VERSION.replace(/\./g, '');
+
const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
authDomain: 'watch-party-crx.firebaseapp.com',
@@ -68,11 +71,40 @@ const sendAuth = async (sendResponse) => {
sendResponse(firebase.auth().currentUser);
};
-chrome.runtime.onInstalled.addListener(() => {
- chrome.tabs.create({
- url: 'https://www.chromewatchparty.com/welcome',
- active: true,
- });
+chrome.runtime.onInstalled.addListener((details) => {
+ if (details.reason) {
+ if (details.reason === 'install') {
+ if (window.localStorage.getItem('watchPartyVersion')) {
+ if (window.localStorage.getItem('watchPartyVersion') !== APP_VERSION) {
+ chrome.tabs.create({
+ url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
+ active: true,
+ });
+ }
+ } else {
+ chrome.tabs.create({
+ url: 'https://www.chromewatchparty.com/welcome',
+ active: true,
+ });
+ window.localStorage.setItem('watchPartyVersion', APP_VERSION);
+ }
+ } else if (details.reason === 'update') {
+ if (window.localStorage.getItem('watchPartyVersion')) {
+ if (window.localStorage.getItem('watchPartyVersion') !== APP_VERSION) {
+ chrome.tabs.create({
+ url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
+ active: true,
+ });
+ }
+ } else {
+ chrome.tabs.create({
+ url: `http://www.chromewatchparty.com/changes/v${APP_VERSION}`,
+ active: true,
+ });
+ window.localStorage.setItem('watchPartyVersion', APP_VERSION);
+ }
+ }
+ }
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
diff --git a/app/public/manifest.json b/app/public/manifest.json
index 90a4f9c..5df15e1 100644
--- a/app/public/manifest.json
+++ b/app/public/manifest.json
@@ -17,7 +17,9 @@
"https://*.youtube.com/*",
"https://play.hbonow.com/*",
"https://play.hbomax.com/*",
- "https://*.peacocktv.com/*"
+ "https://*.peacocktv.com/*",
+ "https://play.google.com/movies/*",
+ "https://*.crunchyroll.com/*"
],
"content_security_policy": "script-src 'self' https://www.gstatic.com/ https://*.firebaseio.com https://www.googleapis.com; object-src 'self'",
"content_scripts": [
@@ -30,7 +32,9 @@
"https://*.youtube.com/*",
"https://play.hbonow.com/*",
"https://play.hbomax.com/*",
- "https://*.peacocktv.com/*"
+ "https://*.peacocktv.com/*",
+ "https://play.google.com/*",
+ "https://*.crunchyroll.com/*"
],
"run_at": "document_idle",
"js": ["/static/js/content.js"],
diff --git a/app/src/Api.js b/app/src/Api.js
index 3a5dcf1..4721bde 100644
--- a/app/src/Api.js
+++ b/app/src/Api.js
@@ -35,6 +35,8 @@ export const PLAYER_SOURCE = {
HBONOW: 'hbonow.',
HBOMAX: 'hbomax.',
PEACOCK: 'peacocktv.',
+ GOOGLEPLAY: 'play.google.',
+ CRUNCHYROLL: 'crunchyroll.',
LOCAL: 'localhost',
};
@@ -48,6 +50,8 @@ export const getPlayerSource = () => {
if (HREF.includes(PLAYER_SOURCE.HBONOW)) return PLAYER_SOURCE.HBONOW;
if (HREF.includes(PLAYER_SOURCE.HBOMAX)) return PLAYER_SOURCE.HBOMAX;
if (HREF.includes(PLAYER_SOURCE.PEACOCK)) return PLAYER_SOURCE.PEACOCK;
+ if (HREF.includes(PLAYER_SOURCE.GOOGLEPLAY)) return PLAYER_SOURCE.GOOGLEPLAY;
+ if (HREF.includes(PLAYER_SOURCE.CRUNCHYROLL)) return PLAYER_SOURCE.CRUNCHYROLL;
if (HREF.includes(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL;
};
diff --git a/app/src/VideoListener.js b/app/src/VideoListener.js
index b6ad2ba..2c489b3 100644
--- a/app/src/VideoListener.js
+++ b/app/src/VideoListener.js
@@ -39,6 +39,7 @@ export default class VideoListener {
this.onpause = this.onpause.bind(this);
this.onplay = this.onplay.bind(this);
this.shouldPushSync = this.shouldPushSync.bind(this);
+ this.crunchyRollListener = this.crunchyRollListener.bind(this);
}
shouldPushSync() {
return (
@@ -73,8 +74,13 @@ export default class VideoListener {
this.isUpdating = true;
if (this.video.element) {
// REMOVE LISTENER BEFORE SYNCING
- this.video.element.removeEventListener('playing', this.onplay);
- this.video.element.removeEventListener('pause', this.onpause);
+ switch(this.video.source) {
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ window.removeEventListener('watchParty', this.crunchyRollListener);
+ default:
+ this.video.element.removeEventListener('playing', this.onplay);
+ this.video.element.removeEventListener('pause', this.onpause);
+ }
await this.timeout(250);
// SEEK
if (Math.abs(serverPlayer.time - this.video.element.currentTime) > 1.5) {
@@ -114,6 +120,17 @@ export default class VideoListener {
simulate(sliderHead, 'mouseup');
await this.timeout(500);
break;
+ case PLAYER_SOURCE.GOOGLEPLAY:
+ this.video.element.currentTime = serverPlayer.time;
+ break;
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ window.postMessage(JSON.stringify({
+ watchPartyDirection: 'TO_SCRIPT',
+ type: 'WATCH_PARTY_SEEK',
+ status: serverPlayer.status,
+ time: serverPlayer.time
+ }));
+ break;
default:
break;
}
@@ -126,9 +143,18 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source
- )
+ ) {
window.postMessage({ type: 'PAUSE', time: serverPlayer.time }, '*');
- else await this.video.element.pause();
+ } else if (PLAYER_SOURCE.CRUNCHYROLL === this.video.source) {
+ window.postMessage(JSON.stringify({
+ watchPartyDirection: 'TO_SCRIPT',
+ type: 'WATCH_PARTY_STATUS',
+ status: serverPlayer.status,
+ time: serverPlayer.time
+ }));
+ } else {
+ await this.video.element.pause();
+ }
break;
case PLAYER_STATUS.PLAYING:
if (
@@ -136,20 +162,33 @@ export default class VideoListener {
PLAYER_SOURCE.NETFLIX === this.video.source ||
PLAYER_SOURCE.HBOMAX === this.video.source ||
PLAYER_SOURCE.HBONOW === this.video.source
- )
+ ) {
window.postMessage({ type: 'PLAY', time: serverPlayer.time }, '*');
- else
+ } else if (PLAYER_SOURCE.CRUNCHYROLL === this.video.source) {
+ window.postMessage(JSON.stringify({
+ watchPartyDirection: 'TO_SCRIPT',
+ type: 'WATCH_PARTY_STATUS',
+ status: serverPlayer.status,
+ time: serverPlayer.time
+ }));
+ } else {
await this.video.element
.play()
.catch((err) => console.error('WatchParty erred when syncing play.'));
+ }
break;
default:
break;
}
await this.timeout(500);
// RE-ADD LISTENERS FOR FUTURE SYNC
- this.video.element.addEventListener('playing', this.onplay);
- this.video.element.addEventListener('pause', this.onpause);
+ switch(this.video.source) {
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ window.addEventListener('watchParty', this.crunchyRollListener);
+ default:
+ this.video.element.addEventListener('playing', this.onplay);
+ this.video.element.addEventListener('pause', this.onpause);
+ }
}
this.isUpdating = false;
}
@@ -218,6 +257,18 @@ export default class VideoListener {
return peacockshow === '' && peacockepisode === ''
? null
: peacockshow + '\n' + peacockepisode;
+ case PLAYER_SOURCE.GOOGLEPLAY:
+ const playerIframe = document.querySelector('#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe');
+ var googleplayshow = '';
+ if (playerIframe) {
+ const titleElement = playerIframe.contentWindow.document.querySelector('.playing-title');
+ googleplayshow = titleElement ? titleElement.innerText : '';
+ }
+ return googleplayshow === '' ? null : googleplayshow;
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ const crunchyrollTitle = document.querySelector('#template_body > div:nth-child(4) > div.showmedia-trail.cf > div > h1');
+ const crunchyrollshow = crunchyrollTitle ? crunchyrollTitle.innerText : '';
+ return crunchyrollshow === '' ? null : crunchyrollshow;
default:
break;
}
@@ -236,6 +287,15 @@ export default class VideoListener {
showName: await this.getShowName(),
});
}
+ async crunchyRollListener(ev) {
+ if (ev.detail.watchPartyDirection === 'TO_APP' && ev.detail.type === 'WATCH_PARTY_PLAYER_UPDATE') {
+ this.updateStatus({
+ status: ev.detail.status,
+ time: ev.detail.time,
+ showName: await this.getShowName()
+ });
+ }
+ }
async firstTimeSync(videoElement) {
this.updateStatus({
element: videoElement,
@@ -249,8 +309,18 @@ export default class VideoListener {
this.updateStatus({
showName: await this.getShowName(),
});
- videoElement.addEventListener('playing', this.onplay);
- videoElement.addEventListener('pause', this.onpause);
+ switch (this.video.source) {
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ window.postMessage(JSON.stringify({
+ watchPartyDirection: 'TO_SCRIPT',
+ type: 'WATCH_PARTY_ADD_LISTENERS'
+ }));
+ window.addEventListener('watchParty', this.crunchyRollListener);
+ break;
+ default:
+ videoElement.addEventListener('playing', this.onplay);
+ videoElement.addEventListener('pause', this.onpause);
+ }
}
async start() {
this.updateStatus({ source: getPlayerSource(), showName: await this.getShowName() });
@@ -454,6 +524,51 @@ export default class VideoListener {
);
}
break;
+ case PLAYER_SOURCE.GOOGLEPLAY:
+ const playerIframe = document.querySelector('#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe');
+ if (playerIframe) {
+ const googlePlayVideoElement = playerIframe.contentWindow.document.querySelector('video');
+ if (googlePlayVideoElement) {
+ this.firstTimeSync(googlePlayVideoElement);
+ }
+ } else {
+ setInterval(
+ this.video.element
+ ? null
+ : () => {
+ const pif = document.querySelector('#yDmH0d > div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe');
+ if (pif) {
+ const gpve = pif.contentWindow.document.querySelector('video');
+ if (gpve) {
+ this.firstTimeSync(gpve);
+ }
+ }
+ },
+ 1000
+ );
+ }
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ const crunchyRollInject = `var sendToWatchPartyApp = function(pS, pT) { var customWatchPartyEvent = new CustomEvent('watchParty', { detail: { watchPartyDirection: 'TO_APP', type: 'WATCH_PARTY_PLAYER_UPDATE', status: pS, time: pT } }); window.dispatchEvent(customWatchPartyEvent); }, watchPartyGetTimeAndSend = function(pS) { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.getCurrentTime(function(pT) { sendToWatchPartyApp(pS, pT); }); } }, watchPartySendPlaying = function() { watchPartyGetTimeAndSend('PLAYING'); }, watchPartySendPaused = function() { watchPartyGetTimeAndSend('PAUSED'); }, watchPartyInteraction = function(e) { if (e.source === window) { const data = JSON.parse(e.data); if (data.watchPartyDirection === 'TO_SCRIPT') { if (data.type === 'WATCH_PARTY_ADD_LISTENERS') { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.on('play', watchPartySendPlaying, 'player.js'); window.VILOS_PLAYERJS.on('pause', watchPartySendPaused, 'player.js'); } } else if (data.type === 'WATCH_PARTY_SEEK') { if (window.VILOS_PLAYERJS) { window.VILOS_PLAYERJS.setCurrentTime(data.time); } } else if (data.type === 'WATCH_PARTY_STATUS') { if (window.VILOS_PLAYERJS) { if (data.status === 'PAUSED') window.VILOS_PLAYERJS.pause(); if (data.status === 'PLAYING') window.VILOS_PLAYERJS.play(); } } } } }; window.addEventListener("message", watchPartyInteraction);`;
+ const crunchyRollInjectScript = document.createElement('script');
+ crunchyRollInjectScript.text = crunchyRollInject;
+ document.head.appendChild(crunchyRollInjectScript);
+ const crunchyrollIframe = document.getElementById('vilos-player');
+ if (crunchyrollIframe) {
+ this.firstTimeSync(crunchyrollIframe);
+ } else {
+ setInterval(
+ this.video.element
+ ? null
+ : () => {
+ const cif = document.getElementById('vilos-player');
+ if (cif) {
+ this.firstTimeSync(cif);
+ }
+ },
+ 1000
+ );
+ }
+ break;
case PLAYER_SOURCE.LOCAL:
const localVideoElement = document.getElementsByTagName('video')[0];
if (localVideoElement) {
diff --git a/app/src/index.css b/app/src/index.css
index 870de2a..5373a24 100644
--- a/app/src/index.css
+++ b/app/src/index.css
@@ -73,7 +73,8 @@ body.hulu.visible .Player__container,/* HULU */
body.amazon-prime.visible #dv-web-player.dv-player-fullscreen, /* AMAZON PRIME */
body.disney.visible #hudson-wrapper, /* DISNEY */
body.youtube.visible #content.ytd-app, /* YOUTUBE */
-body.youtube.visible #masthead-container /* YOUTUBE HEADER */ {
+body.youtube.visible #masthead-container, /* YOUTUBE HEADER */
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td /* GOOGLE PLAY MOVIES */ {
max-width: var(--body-width) !important;
}
/* HULU MINIMIZED VIDEO */
@@ -151,6 +152,48 @@ body.peacock.visible .playback-header__container {
position: relative;
max-width: 100%;
}
+/* GOOGLE PLAY STYLING */
+body.googleplay.visible {
+ overflow: hidden;
+}
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div.mjANdc.eEPege {
+ padding: 0 !important;
+}
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div.mjANdc.eEPege > div.LhXUod.XL54pf.J9Nfi.iWO5td,
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div > iframe {
+ min-height: 100%;
+ width: 100% !important;
+ height: 100% !important;
+}
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div,
+body.googleplay.visible div.llhEMd.bYEzqc.LAhoGb.iWO5td > div > div.LhXUod.XL54pf.J9Nfi.iWO5td > span > div > div {
+ height: 100%;
+}
+/* CRUNCHYROLL STYLING */
+body.crunchyroll.visible #showmedia_video {
+ position: fixed;
+ z-index: 99999;
+ width: calc(100% - var(--party-width));
+ background: black;
+ top: 0;
+ left: 0;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+body.crunchyroll.visible #showmedia_video_box {
+ width: 100% !important;
+ height: 100% !important;
+}
+body.crunchyroll.visible #showmedia_video_player {
+ display: flex !important;
+ align-items: center !important;
+}
+body.crunchyroll.visible #vilos-player {
+ max-height: 42vw !important;
+}
+
/*
FULL SCREEN BUTTON REMOVER
diff --git a/app/src/index.js b/app/src/index.js
index c68d050..59c1905 100644
--- a/app/src/index.js
+++ b/app/src/index.js
@@ -95,6 +95,12 @@ switch (getPlayerSource()) {
case PLAYER_SOURCE.PEACOCK:
document.body.classList.add('peacock');
break;
+ case PLAYER_SOURCE.GOOGLEPLAY:
+ document.body.classList.add('googleplay');
+ break;
+ case PLAYER_SOURCE.CRUNCHYROLL:
+ document.body.classList.add('crunchyroll');
+ break;
case PLAYER_SOURCE.LOCAL:
document.body.classList.add('local');
break;
diff --git a/functions/app.js b/functions/app.js
index c4a460f..efabb82 100644
--- a/functions/app.js
+++ b/functions/app.js
@@ -7,7 +7,8 @@ const cors = require('cors');
// WEBHOOKS
const webhooks = express();
webhooks.use(cors({ origin: true }));
-webhooks.all('/conferenceEnded',Webhooks.conferenceEnd);
+webhooks.all('/conferenceEnded', Webhooks.conferenceEnd);
+webhooks.all('/scheduledCleanup', Webhooks.scheduledCleanUp);
// VERSION 1
const app = express();
diff --git a/functions/index.js b/functions/index.js
index 605dd62..0fae717 100644
--- a/functions/index.js
+++ b/functions/index.js
@@ -1,4 +1,3 @@
-const FunctionModules = require('./modules/index');
const functions = require('firebase-functions');
const secret = require('./FirebaseSecret.json');
const admin = require('firebase-admin');
@@ -15,9 +14,5 @@ admin.initializeApp(FIREBASE_OPTIONS);
exports.widgets = functions.https.onRequest(REST.v1);
// VERSION 2
exports.widgets2 = functions.https.onRequest(REST.v2);
-
-exports.scheduledCleanUp = functions.pubsub
- .schedule('every 60 minutes')
- .onRun(FunctionModules.scheduledCleanUp);
-
+// WEBHOOKS
exports.webhooks = functions.https.onRequest(REST.webhooks);
diff --git a/functions/modules/index.js b/functions/modules/index.js
index b3888b1..5433a8c 100644
--- a/functions/modules/index.js
+++ b/functions/modules/index.js
@@ -1,6 +1,4 @@
const authenticator = require('./authenticator');
-const cleanUpRoom = require('./cleanUpRoom');
-const scheduledCleanUp = require('./scheduledCleanUp');
const createRoom = require('./createRoom');
const joinRoom = require('./joinRoom');
const sendMessage = require('./sendMessage');
@@ -21,8 +19,6 @@ module.exports = {
updatePlayer,
leaveRoom,
deleteRoom,
- scheduledCleanUp,
- cleanUpRoom,
timeDiff,
tokenValidator,
tokenRefresher,
diff --git a/functions/modules/updatePlayer.js b/functions/modules/updatePlayer.js
index 7ae9253..2ccd39c 100644
--- a/functions/modules/updatePlayer.js
+++ b/functions/modules/updatePlayer.js
@@ -2,18 +2,6 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => {
const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body);
- // const statusString =
- // player.status === 'PLAYING'
- // ? 'started playing from'
- // : player.status === 'PAUSED'
- // ? 'paused at'
- // : player.status === 'NOT_PLAYING'
- // ? 'stopped at'
- // : 'is at';
- // const timeString =
- // player.time > 3600
- // ? new Date(player.time * 1000).toISOString().substr(11, 8)
- // : new Date(player.time * 1000).toISOString().substr(14, 5);
if (!roomId) return res.status(204).send({ message: 'Party name not sent.' });
const [roomExists, roomData] = await admin
.firestore()
@@ -32,17 +20,19 @@ module.exports = async (req, res) => {
.then((userSnap) => userSnap.exists);
if (userExists) {
if (showChanged === true && player.showName) {
- await admin
- .firestore()
- .collection('rooms')
- .doc(roomId)
- .collection('messages')
- .add({
- uid: '',
- name: '',
- text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
- time: new Date(),
- });
+ if (roomData.player.showName !== player.showName) {
+ await admin
+ .firestore()
+ .collection('rooms')
+ .doc(roomId)
+ .collection('messages')
+ .add({
+ uid: '',
+ name: '',
+ text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
+ time: new Date(),
+ });
+ }
}
if (roomData.status !== player.status) {
await admin
@@ -56,23 +46,6 @@ module.exports = async (req, res) => {
console.error(`Failed to update player : \n\t Room: ${roomId} \n\t ${uid}`, err);
res.status(500).send({ message: "Uh oh party could't be synced", err });
});
- // await admin
- // .firestore()
- // .collection('rooms')
- // .doc(roomId)
- // .collection('messages')
- // .add({
- // uid: '',
- // name: '',
- // text: `${userName} ${statusString} ${timeString}`,
- // time: new Date(),
- // })
- // .catch((err) => {
- // console.error(
- // `Failed to add system update player message : \n\t Room: ${roomId} \n\t ${uid}`,
- // err
- // );
- // });
return res.status(200).send({ message: 'Synced party player' });
} else {
return res.status(208).send({ message: 'Player already up to date' });
diff --git a/functions/modules2/updatePlayer.js b/functions/modules2/updatePlayer.js
index 7ae9253..9783f51 100644
--- a/functions/modules2/updatePlayer.js
+++ b/functions/modules2/updatePlayer.js
@@ -32,7 +32,8 @@ module.exports = async (req, res) => {
.then((userSnap) => userSnap.exists);
if (userExists) {
if (showChanged === true && player.showName) {
- await admin
+ if (player.showChanged !== roomData.player.showName) {
+ await admin
.firestore()
.collection('rooms')
.doc(roomId)
@@ -43,6 +44,7 @@ module.exports = async (req, res) => {
text: `${userName} changed \n${player.showName ? 'to ' + player.showName : ''}`,
time: new Date(),
});
+ }
}
if (roomData.status !== player.status) {
await admin
diff --git a/functions/modules/cleanUpRoom.js b/functions/webhooks/cleanUpRoom.js
similarity index 100%
rename from functions/modules/cleanUpRoom.js
rename to functions/webhooks/cleanUpRoom.js
diff --git a/functions/webhooks/index.js b/functions/webhooks/index.js
index 5cf0ebb..b6655a4 100644
--- a/functions/webhooks/index.js
+++ b/functions/webhooks/index.js
@@ -1,5 +1,7 @@
const conferenceEnd = require('./conferenceEnd');
+const scheduledCleanUp = require('./scheduledCleanUp');
module.exports = {
conferenceEnd,
+ scheduledCleanUp
};
diff --git a/functions/modules/scheduledCleanUp.js b/functions/webhooks/scheduledCleanUp.js
similarity index 92%
rename from functions/modules/scheduledCleanUp.js
rename to functions/webhooks/scheduledCleanUp.js
index 2fc91a5..1c3caa0 100644
--- a/functions/modules/scheduledCleanUp.js
+++ b/functions/webhooks/scheduledCleanUp.js
@@ -9,8 +9,9 @@ const timeDiff = (ds1, ds2) => {
return Math.abs(Math.round(diff));
};
-module.exports = async () => {
+module.exports = async (req, res) => {
const startedTime = new Date();
+ res.status(200).send({message:'Started processing'});
const roomList = await admin
.firestore()
.collection('rooms')
@@ -29,7 +30,7 @@ module.exports = async () => {
if (
// MORE THAN 24 HOURS AND NOT IN USE
(timeDiff(roomSnap.createTime.toDate(), startedTime) >= 24 &&
- timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12) ||
+ timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 4) ||
// LESS THAN 24 HOURS BUT NOT IN USE
timeDiff(roomSnap.updateTime.toDate(), startedTime) >= 12
) {
@@ -45,4 +46,5 @@ module.exports = async () => {
);
});
});
+ return;
};
From 9ef8184ccc8619332de9bce24cdd0710e7dd7d09 Mon Sep 17 00:00:00 2001
From: Ayushya Amitabh
Date: Sun, 2 Aug 2020 21:11:42 -0400
Subject: [PATCH 13/48] v1.6.0/document added support for sites
---
app/src/Chat.js | 6 +++++-
website/changes/index.html | 2 ++
website/changes/v1.5.2/index.html | 2 ++
website/changes/v1.6.0/index.html | 4 +++-
website/copyright/index.html | 6 +++++-
website/index.html | 4 ++++
website/media/logos/Crunchyroll.png | Bin 0 -> 29198 bytes
website/media/logos/GooglePlay.png | Bin 0 -> 26800 bytes
website/styles/variables.css | 6 ++++++
website/welcome/index.html | 6 +++++-
10 files changed, 32 insertions(+), 4 deletions(-)
create mode 100644 website/media/logos/Crunchyroll.png
create mode 100644 website/media/logos/GooglePlay.png
diff --git a/app/src/Chat.js b/app/src/Chat.js
index 616e966..106937d 100644
--- a/app/src/Chat.js
+++ b/app/src/Chat.js
@@ -402,7 +402,11 @@ export default class Chat extends Component {
message: 'Functionality may be buggy, HBO has a horrible design.',
},
{
- condition: () => getPlayerSource() === PLAYER_SOURCE.PEACOCK,
+ condition: () => getPlayerSource() === PLAYER_SOURCE.GOOGLEPLAY,
+ message: 'Functionality may be buggy, support on this site is in testing.',
+ },
+ {
+ condition: () => getPlayerSource() === PLAYER_SOURCE.CRUNCHYROLL,
message: 'Functionality may be buggy, support on this site is in testing.',
},
{
diff --git a/website/changes/index.html b/website/changes/index.html
index 0a533ac..d9e59de 100644
--- a/website/changes/index.html
+++ b/website/changes/index.html
@@ -16,6 +16,8 @@ YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
------------------------------------------------------------------------->
diff --git a/website/changes/v1.5.2/index.html b/website/changes/v1.5.2/index.html
index f259a1c..8e4ce16 100644
--- a/website/changes/v1.5.2/index.html
+++ b/website/changes/v1.5.2/index.html
@@ -16,6 +16,8 @@ YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
------------------------------------------------------------------------->
diff --git a/website/changes/v1.6.0/index.html b/website/changes/v1.6.0/index.html
index 5c229bb..424205f 100644
--- a/website/changes/v1.6.0/index.html
+++ b/website/changes/v1.6.0/index.html
@@ -16,6 +16,8 @@ YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
------------------------------------------------------------------------->
@@ -55,7 +57,7 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
- Add support for Crunchyroll
+ Add support for Crunchyroll & Google Play Movies & TV
diff --git a/website/copyright/index.html b/website/copyright/index.html
index c2d780f..d1ac6a9 100644
--- a/website/copyright/index.html
+++ b/website/copyright/index.html
@@ -16,6 +16,8 @@ YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
------------------------------------------------------------------------->
@@ -68,7 +70,9 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,
LLC.
- Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+ Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+ Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
diff --git a/website/index.html b/website/index.html
index 8d96e92..3f0a830 100644
--- a/website/index.html
+++ b/website/index.html
@@ -16,6 +16,8 @@ YouTube logo is a trademark and intellectual property of Google, Inc.
Hulu logo is a trademark and intellectual property of Hulu, LLC.
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Universal.
+Google Play Movies & TV logo is a trademark and intellectual property of Google, Inc.
+Crunchyroll logo is a trademark and intellectual property of Crunchyroll and Ellation, Inc.
------------------------------------------------------------------------->
@@ -59,6 +61,8 @@ Peacock TV logo is a trademark and intellectual property of Peacock TV & NBC Uni
+
+
9P(>2_&C!C3GOw5UG+qP}n)?{Maw#|v1bTCmTnb>d6bD#5lf4ckEzIN|g
zyH?ezbww)3i6g<|zykmPBuNPoB>(^%^!Ym$7W(t^&;AD{06+wg6cJSM_;sEQ^9w`t
zezdZ}+l;-AZtd&1B^-&Nfv66upooY