diff --git a/background.js b/background.js index bf2d652..beb2a5e 100644 --- a/background.js +++ b/background.js @@ -8,42 +8,65 @@ * channel to communicate between scripts */ const getMessenger = contentMessenger => { - let currentTab = contentMessenger.sender.tab; - chrome.webNavigation.onHistoryStateUpdated.addListener( details => { - if (contentMessenger.name === "uvpc-b") { - sendToContentScript(contentMessenger, { listeningTo: currentTab.id, title: currentTab.title }); - } else { - chrome.runtime.connect({ name: "uvpc-b" }); - } - contentMessenger.onDisconnect.addListener(() => { - console.info("disconnected from contentMessenger"); - }); - contentMessenger.onMessage.addListener((message, sender) => { + if ((contentMessenger.name = "uvpc-b")) { + const checkForOTTs = (tabId, changeInfo, tab) => { + if (changeInfo.url || changeInfo.audible || changeInfo.status === "complete") { + let currentUrl = changeInfo.url || tab.url; + if (currentUrl && tab.status === "complete") { + if (currentUrl.match(netflixRegex)) { + sendToContentScript(contentMessenger, { + domain: "netflix", + audible: tab.audible, + }, tabId); + return; + } + if (currentUrl.match(apvRegex)) { + sendToContentScript(contentMessenger, { + domain: "primevideo", + audible: tab.audible, + }, tabId); + return; + } + } + } + }; + contentMessenger.onMessage.addListener(message => { + console.log(message); + if (message.initiated) { + console.log(`connected to ${contentMessenger.sender.tab.title}`); + chrome.tabs.onCreated.addListener(checkForOTTs); + chrome.tabs.onUpdated.addListener(checkForOTTs); + } if (message.badgeText) { chrome.browserAction.setBadgeText({ text: message.badgeText + "x", - tabId: sender.sender.tab.id, + tabId: contentMessenger.sender.tab.id, }); chrome.browserAction.setBadgeBackgroundColor({ color: "#000000", - tabId: sender.sender.tab.id, - }); - contentMessenger.postMessage({ - newSpeed: 1.5, + tabId: contentMessenger.sender.tab.id, }); } }); - }) + contentMessenger.onDisconnect.addListener(() => { + console.info("disconnected from backgroundjs"); + chrome.tabs.onCreated.removeListener(checkForOTTs); + chrome.tabs.onUpdated.removeListener(checkForOTTs); + contentMessenger = null; + }); + } +}; + +const sendToContentScript = (port, message, tabId) => { + if (port) { + console.log(`SENDING ${JSON.stringify(message, null, 2)} to Tab #${tabId}`); + chrome.tabs.sendMessage(tabId, message); + } else { + console.log("PORT GONE!"); + } }; -const sendToContentScript = (port, message) => { - port.postMessage(message); -} +const netflixRegex = /(http(s)?:\/\/.)?(www\.)?(netflix.com\/watch)/; +const apvRegex = /(http(s)?:\/\/.)?(www\.)?(primevideo.com\/detail)/; -chrome.runtime.onStartup.addListener(() => { - chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { - if (tab.status === 'complete') { - chrome.runtime.onConnect.addListener(getMessenger); - } - }) -}); +chrome.runtime.onConnect.addListener(getMessenger); diff --git a/content.css b/content.css new file mode 100644 index 0000000..435716f --- /dev/null +++ b/content.css @@ -0,0 +1,78 @@ +div#playbackController div#uvpc-div { + opacity: 0; + background: #282828 !important; + height: 100px !important; + width: 200px !important; + padding: 5px !important; + position: absolute !important; + bottom: calc(100vh - 90vh) !important; + right: calc(100vw - 95vw) !important; + border-radius: 5px !important; + direction: ltr !important; + text-align: center; +} + +div#playbackController div#uvpc-div div.playbackSpeeds h3.playback-header { + margin: 5px auto !important; + font-size: 2em; +} + +div#playbackController div#uvpc-div.active { + opacity: 1; + -o-transition: opacity 160ms ease; + -moz-transition: opacity 160ms ease; + transition: opacity 160ms ease; + will-change: opacity; +} + +div#playbackController div#uvpc-div.active div.playbackSpeeds div.range-stuff { + margin: 50px 15px 0px 15px !important; +} + + +div#playbackController div#uvpc-div.active div.playbackSpeeds input[type=range] { + display: block; + width: 100%; + margin: 0; + -webkit-appearance: none; + outline: none; +} + +div#playbackController div#uvpc-div.active div.playbackSpeeds input[type=range]::-webkit-slider-runnable-track { + position: relative; + height: 12px; + border: 1px solid #b2b2b2; + border-radius: 5px; + background-color: #e2e2e2; + box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.1); +} + +div#playbackController div#uvpc-div.active div.playbackSpeeds input[type=range]::-webkit-slider-thumb { + position: relative; + top: -5px; + width: 20px; + height: 20px; + border: 1px solid #999; + -webkit-appearance: none; + background-color: #fff; + box-shadow: inset 0 -1px 2px 0 rgba(0, 0, 0, 0.25); + border-radius: 100%; + cursor: pointer; +} + +div#playbackController div#uvpc-div.active div.playbackSpeeds output { + position: absolute; + top: 40px !important; + right: 80px !important; + display: block; + font-weight: bold; + width: 50px; + height: 24px; + border: 1px solid #e2e2e2; + background-color: #fff; + border-radius: 3px; + color: #777; + font-size: 1.8em; + line-height: 24px; + text-align: center; +} \ No newline at end of file diff --git a/content.js b/content.js index 43e880a..7e55385 100644 --- a/content.js +++ b/content.js @@ -4,10 +4,147 @@ */ /** - * Waits for video element to be loaded, then resolves promise with the element. - * @returns {Promise} + * Creates a communication channel + * between background.js & content.js + */ +const connectToServices = () => { + const initContent = chrome.runtime.connect({ name: "uvpc-b" }); + console.log("connecting from contentjs", initContent); + initContent.postMessage({ initiated: true }); + chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { + deployer(msg, initContent); + }); +}; + +window.addEventListener("load", connectToServices); + +/** + * Based on the domain passed, deploy the controller element onto DOM + * @param {string} msg + */ +const deployer = (msg, port) => { + switch (msg.domain) { + case "netflix": + return deployNetflix(port); + case "primevideo": + console.log("APV Deploy!"); + // return deployAPV(port); + default: + console.error("Don't really need this!"); + } +}; + +const deployNetflix = async port => { + var videoElement = await fetchVideoElement(); + if (!videoElement) return false; + updateBadgeText(port, videoElement); + var injectedFa = await injectStyles(); + if (!injectedFa.success) return false; + var injectedController = await injectControllerToNetflix(); + var controllers = document.querySelectorAll("#playbackController"); + if (controllers.length > 1) { + controllers.forEach((element, index) => { + if (index > 0) element.remove(); + }); + } + if (!injectedController.success) return false; + document + .getElementById("uvpc-btn") + .addEventListener("click", playBackToggler); + document + .getElementById("uvpc-value") + .addEventListener("input", event => + valueChanged(port, event, videoElement) + ); + document.getElementById("uvpc-value").value = videoElement.playbackRate; +}; + +/** + * Builds the icon for the cog controller */ +const injectControllerToNetflix = () => { + // OTT specific + let playerDiv = document.querySelector( + ".PlayerControlsNeo__button-control-row" + ); + if (!document.contains(playerDiv)) return false; + // cog + return new Promise((resolve, reject) => { + let main = document.createElement("div"); + main.id = "playbackController"; + let videoControllerDiv = document.createElement("div"); + videoControllerDiv.id = "uvpc-div"; + videoControllerDiv.innerHTML = `