From 60aa77157247ee01a5d1f14133a7c526f3183c43 Mon Sep 17 00:00:00 2001 From: Simon Huang Date: Wed, 30 Sep 2020 19:17:55 -0400 Subject: [PATCH] added netflix syncing in browser extension --- extension/background.js | 40 ++++++++++++++++++++++++++++++++++++---- extension/manifest.json | 11 ++++++++++- extension/netflix.js | 39 +++++++++++++++++++++++++++++++++++++++ extension/player.js | 14 ++++++++++++++ 4 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 extension/netflix.js create mode 100644 extension/player.js diff --git a/extension/background.js b/extension/background.js index 34b8d47..283e4ac 100644 --- a/extension/background.js +++ b/extension/background.js @@ -2,9 +2,41 @@ const IFRAME_HEADERS = ['content-security-policy', 'x-frame-options']; const isFirefox = navigator.userAgent.includes('Firefox'); chrome.webRequest.onHeadersReceived.addListener( - (details) => ({ - responseHeaders: details.responseHeaders.filter((header) => !IFRAME_HEADERS.includes(header.name.toLowerCase())), - }), + (details) => { + details.responseHeaders.forEach((header, index, headers) => { + // Modify samesite property for cookies + if (header.name.toLowerCase() === 'set-cookie') { + let cookies = header.value.split('\n'); + cookies.forEach((cookie, i, allCookies) => { + cookie = cookie.replace(/SameSite=Lax/i, 'SameSite=None'); + if (cookie.search(/SameSite/i) === -1) { + cookie += '; SameSite=None'; + } + if (cookie.search(/Secure/i) === -1) { + cookie += '; Secure'; + } + + allCookies[i] = cookie; + }); + + header.value = ''; + for (let i = 0; i < cookies.length; i++) { + header.value += cookies[i]; + if (i !== cookies.length - 1) { + header.value += '\n'; + } + } + //console.log(header.value); + + headers[index].value = header.value; + } + }); + + // Return stripped headers + return { + responseHeaders: details.responseHeaders.filter((header) => !IFRAME_HEADERS.includes(header.name.toLowerCase())), + }; + }, { urls: [''] }, - ['blocking', 'responseHeaders'] + isFirefox ? ['blocking', 'responseHeaders'] : ['blocking', 'responseHeaders', 'extraHeaders'] ); diff --git a/extension/manifest.json b/extension/manifest.json index 1b29a47..0ed19a8 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -3,8 +3,17 @@ "name": "Turtle", "description": "Allows syncing shows/movies of subscription services", "background": { - "scripts": ["background.js"] + "scripts": ["background.js"], + "persistent": true }, + "content_scripts": [ + { + "matches": ["https://www.netflix.com/*"], + "js": ["player.js"], + "all_frames": true + } + ], + "web_accessible_resources": ["netflix.js"], "permissions": ["webRequest", "webRequestBlocking", ""], "version": "1.0", "icons": { diff --git a/extension/netflix.js b/extension/netflix.js new file mode 100644 index 0000000..4df6ef2 --- /dev/null +++ b/extension/netflix.js @@ -0,0 +1,39 @@ +const videoPlayer = netflix.appContext.state.playerApp.getAPI().videoPlayer; +const playerSessionId = videoPlayer.getAllPlayerSessionIds()[0]; +const netflixPlayer = videoPlayer.getVideoPlayerBySessionId(playerSessionId); +const player = document.getElementsByTagName('video')[0]; +window.parent.postMessage('video ready', '*'); + +// Subscribe listeners on client-side actions +player.addEventListener('play', (ev) => { + window.parent.postMessage({ type: 'play', time: player.currentTime }, '*'); +}); + +player.addEventListener('pause', (ev) => { + window.parent.postMessage({ type: 'pause', time: player.currentTime }, '*'); +}); + +// Subscribe listener for syncing requests +window.addEventListener('message', (ev) => { + let type = ev.data.type; + + // Handle status request event + if (ev.data.toString() === 'fetch current status') { + ev.ports[0].postMessage({ isPlaying: player.paused, time: player.currentTime }); + } + + // Handle seeking event + if (type === 'seek') { + netflixPlayer.seek(Number(ev.data.currentTime * 1000)); // Netflix Player object is needed to seek + } + + // Handle playing event + if (type === 'playing') { + if (ev.data.playing && player.paused) { + player.play(); + } + if (!ev.data.playing && !player.paused) { + player.pause(); + } + } +}); diff --git a/extension/player.js b/extension/player.js new file mode 100644 index 0000000..bce787d --- /dev/null +++ b/extension/player.js @@ -0,0 +1,14 @@ +var playerReady = false; +const NETFLIX_VID_URL = /https?:\/\/www\.netflix\.com\/watch\/([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g; + +document.addEventListener('click', (e) => { + if (!playerReady && window.location.toString().match(NETFLIX_VID_URL)) { + let s = document.createElement('script'); + s.src = chrome.runtime.getURL('netflix.js'); + (document.head || document.documentElement).appendChild(s); + playerReady = true; + } else if (playerReady && !window.location.toString().match(NETFLIX_VID_URL)) { + playerReady = false; + window.parent.postMessage('video not ready', '*'); + } +});