v1.6.0/added support, cleanup webhook

This commit is contained in:
2020-08-02 20:41:52 -04:00
parent 86ee168045
commit e388dbf283
14 changed files with 247 additions and 72 deletions
+37 -5
View File
@@ -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) => {
+6 -2
View File
@@ -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"],
+4
View File
@@ -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;
};
+125 -10
View File
@@ -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) {
+44 -1
View File
@@ -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
+6
View File
@@ -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;
+2 -1
View File
@@ -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();
+1 -6
View File
@@ -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);
-4
View File
@@ -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,
+13 -40
View File
@@ -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: '<system>',
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: '<system>',
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: '<system>',
// 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' });
+3 -1
View File
@@ -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
+2
View File
@@ -1,5 +1,7 @@
const conferenceEnd = require('./conferenceEnd');
const scheduledCleanUp = require('./scheduledCleanUp');
module.exports = {
conferenceEnd,
scheduledCleanUp
};
@@ -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;
};