diff --git a/README.md b/README.md index fb6fe43e..89aa4c8e 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,6 @@ None at the moment # Credit +The awesome [Invidious API](https://github.com/omarroth/invidious/wiki/API) is used to grab the time the video was published. + Some icons made by Gregor Cresnar from www.flaticon.com and are licensed by CC 3.0 BY diff --git a/background.js b/background.js index c619bcbb..07743435 100644 --- a/background.js +++ b/background.js @@ -21,11 +21,10 @@ chrome.tabs.onUpdated.addListener( // On tab update chrome.runtime.onMessage.addListener(function (request, sender, callback) { if (request.message == "submitTimes") { - submitTimes(request.videoID); + submitTimes(request.videoID, callback); - callback({ - success: true - }); + //this allows the callback to be called later by the submitTimes function + return true; } else if (request.message == "ytvideoid") { if (previousVideoID != request.videoID) { videoIDChange(request.videoID); @@ -42,7 +41,10 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { //this allows the callback to be called later return true; } else if (request.message == "submitVote") { - submitVote(request.type, request.UUID) + submitVote(request.type, request.UUID, callback); + + //this allows the callback to be called later + return true; } }); @@ -51,7 +53,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { function getSponsorTimes(videoID, callback) { let sponsorTimes = []; let sponsorTimeKey = "sponsorTimes" + videoID; - chrome.storage.local.get([sponsorTimeKey], function(result) { + chrome.storage.sync.get([sponsorTimeKey], function(result) { let sponsorTimesStorage = result[sponsorTimeKey]; if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) { sponsorTimes = sponsorTimesStorage; @@ -77,41 +79,70 @@ function addSponsorTime(time) { //save this info let sponsorTimeKey = "sponsorTimes" + previousVideoID; - chrome.storage.local.set({[sponsorTimeKey]: sponsorTimes}); + chrome.storage.sync.set({[sponsorTimeKey]: sponsorTimes}); }); } -function submitVote(type, UUID) { - let xmlhttp = new XMLHttpRequest(); - +function submitVote(type, UUID, callback) { getUserID(function(userID) { //publish this vote - console.log(serverAddress + "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type); - xmlhttp.open('GET', serverAddress + "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, true); - - //submit this vote - xmlhttp.send(); + sendRequestToServer('GET', "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + callback({ + successType: 1 + }); + } else if (xmlhttp.readyState == 4 && xmlhttp.status == 405) { + //duplicate vote + callback({ + successType: 0 + }); + } else if (error) { + //error while connect + callback({ + successType: -1 + }); + } + }) }) } -function submitTimes(videoID) { +function submitTimes(videoID, callback) { //get the video times from storage let sponsorTimeKey = 'sponsorTimes' + videoID; - chrome.storage.local.get([sponsorTimeKey], function(result) { + chrome.storage.sync.get([sponsorTimeKey], function(result) { let sponsorTimes = result[sponsorTimeKey]; if (sponsorTimes != undefined && sponsorTimes.length > 0) { //submit these times for (let i = 0; i < sponsorTimes.length; i++) { - let xmlhttp = new XMLHttpRequest(); - - let userIDStorage = getUserID(function(userIDStorage) { + getUserID(function(userIDStorage) { //submit the sponsorTime - xmlhttp.open('GET', serverAddress + "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1] - + "&userID=" + userIDStorage, true); - xmlhttp.send(); + sendRequestToServer('GET', "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1] + + "&userID=" + userIDStorage, function(xmlhttp, error) { + if (xmlhttp.readyState == 4 && !error) { + callback({ + statusCode: xmlhttp.status + }); + } else if (error) { + callback({ + statusCode: -1 + }); + } + }); }); } + + //add these to the storage log + chrome.storage.sync.get(["sponsorTimesContributed"], function(result) { + let currentContributionAmount = 0; + if (result.sponsorTimesContributed != undefined) { + //current contribution amount is known + currentContributionAmount = result.sponsorTimesContributed; + } + + //save the amount contributed + chrome.storage.sync.set({"sponsorTimesContributed": currentContributionAmount + sponsorTimes.length}); + }); } }); } @@ -121,7 +152,7 @@ function videoIDChange(currentVideoID) { if (previousVideoID != null) { //get the sponsor times from storage let sponsorTimeKey = 'sponsorTimes' + previousVideoID; - chrome.storage.local.get([sponsorTimeKey], function(result) { + chrome.storage.sync.get([sponsorTimeKey], function(result) { let sponsorTimes = result[sponsorTimeKey]; if (sponsorTimes != undefined && sponsorTimes.length > 0) { @@ -149,7 +180,7 @@ function getUserID(callback) { } //if it is not cached yet, grab it from storage - chrome.storage.local.get(["userID"], function(result) { + chrome.storage.sync.get(["userID"], function(result) { let userIDStorage = result.userID; if (userIDStorage != undefined) { userID = userIDStorage; @@ -159,13 +190,32 @@ function getUserID(callback) { userID = generateUUID(); //save this UUID - chrome.storage.local.set({"userID": userID}); + chrome.storage.sync.set({"userID": userID}); callback(userID); } }); } +function sendRequestToServer(type, address, callback) { + let xmlhttp = new XMLHttpRequest(); + + xmlhttp.open(type, serverAddress + address, true); + + if (callback != undefined) { + xmlhttp.onreadystatechange = function () { + callback(xmlhttp, false); + }; + + xmlhttp.onerror = function(ev) { + callback(xmlhttp, true); + }; + } + + //submit this request + xmlhttp.send(); +} + function getYouTubeVideoID(url) { // Return video id or false var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/; var match = url.match(regExp); diff --git a/content.css b/content.css index 1a85c3ac..6126768a 100644 --- a/content.css +++ b/content.css @@ -1,8 +1,16 @@ +.playerButton { + height: 60%; + top: 0; + bottom: 0; + display: block; + margin: auto; +} + .sponsorSkipObject { font-family: 'Source Sans Pro', sans-serif; } -#sponsorSkipLogo { +.sponsorSkipLogo { height: 64px; position: absolute; top: 0; @@ -11,7 +19,12 @@ margin-left: 10px; } -#sponsorSkipNotice { +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.sponsorSkipNotice { min-height: 165px; min-width: 400px; background-color: rgba(255, 217, 217, 0.8); @@ -19,9 +32,18 @@ z-index: 1; border: 3px solid rgba(0, 0, 0, 0.8); margin-top: -50px; + + animation: fadeIn 0.5s; } -#sponsorSkipMessage { +/* if two are very close to eachother */ +.secondSkipNotice { + margin-left: 500px; + + transition: margin-left 0.2s; +} + +.sponsorSkipMessage { font-size: 18px; color: #000000; text-align: center; @@ -30,7 +52,7 @@ margin-top: 4px; } -#sponsorSkipInfo { +.sponsorSkipInfo { font-size: 10px; color: #000000; text-align: center; @@ -42,6 +64,8 @@ font-weight: bold; color: #000000; text-align: center; + margin-top: 0px; + margin-bottom: 0px; } #sponsorTimesThanksForVotingInfoText { @@ -49,6 +73,14 @@ font-weight: bold; color: #000000; text-align: center; + margin-top: 0px; +} + +.sponsorTimesInfoMessage { + font-size: 15px; + font-weight: bold; + color: #000000; + text-align: center; } .voteButton { @@ -60,6 +92,42 @@ filter: brightness(80%); } +.submitButton { + background-color:#ec1c1c; + -moz-border-radius:28px; + -webkit-border-radius:28px; + border-radius:28px; + border:1px solid #d31919; + display:inline-block; + cursor:pointer; + color:#ffffff; + font-size:14px; + padding:4px 15px; + text-decoration:none; + text-shadow:0px 0px 0px #662727; + + margin-top: 5px; + margin-right: 15px; +} +.submitButton:hover { + background-color:#bf2a2a; +} + +.submitButton:focus { + outline: none; + background-color:#bf2a2a; +} + +.submitButton:active { + position:relative; + top:1px; +} + +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + .sponsorSkipButton { background-color:#ec1c1c; -moz-border-radius:28px; diff --git a/content.js b/content.js index 91e217f6..1fdccecc 100644 --- a/content.js +++ b/content.js @@ -36,7 +36,7 @@ var hideVideoPlayerControls = false; //if the notice should not be shown //happens when the user click's the "Don't show notice again" button var dontShowNotice = false; -chrome.storage.local.get(["dontShowNoticeAgain"], function(result) { +chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) { let dontShowNoticeAgain = result.dontShowNoticeAgain; if (dontShowNoticeAgain != undefined) { dontShowNotice = dontShowNoticeAgain; @@ -59,7 +59,8 @@ chrome.runtime.onMessage.addListener( // Detect URL Changes //send the sponsor times along with if it's found sendResponse({ found: sponsorDataFound, - sponsorTimes: sponsorTimes + sponsorTimes: sponsorTimes, + UUIDs: UUIDs }) } @@ -73,8 +74,8 @@ chrome.runtime.onMessage.addListener( // Detect URL Changes dontShowNotice = false; } - if (request.message == "toggleStartSponsorButton") { - toggleStartSponsorButton(); + if (request.message == "changeStartSponsorButton") { + changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible); } if (request.message == "changeVideoPlayerControlsVisibility") { @@ -96,14 +97,16 @@ function videoIDChange(id) { }, function(response) { if (response != undefined) { let sponsorTimes = response.sponsorTimes; - if (sponsorTimes != undefined && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) { + if (sponsorTimes != undefined && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length >= 2) { + document.getElementById("submitButton").style.display = "unset"; + } else if (sponsorTimes != undefined && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) { toggleStartSponsorButton(); } } }); //see if video control buttons should be added - chrome.storage.local.get(["hideVideoPlayerControls"], function(result) { + chrome.storage.sync.get(["hideVideoPlayerControls"], function(result) { if (result.hideVideoPlayerControls != undefined) { hideVideoPlayerControls = result.hideVideoPlayerControls; } @@ -117,24 +120,34 @@ function sponsorsLookup(id) { let xmlhttp = new XMLHttpRequest(); //check database for sponsor times - xmlhttp.open('GET', serverAddress + "/api/getVideoSponsorTimes?videoID=" + id, true); + sendRequestToServer('GET', "/api/getVideoSponsorTimes?videoID=" + id, function(xmlhttp) { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + sponsorDataFound = true; - xmlhttp.onreadystatechange = function () { - if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { - sponsorDataFound = true; + sponsorTimes = JSON.parse(xmlhttp.responseText).sponsorTimes; + UUIDs = JSON.parse(xmlhttp.responseText).UUIDs; - sponsorTimes = JSON.parse(xmlhttp.responseText).sponsorTimes; - UUIDs = JSON.parse(xmlhttp.responseText).UUIDs; + // If the sponsor data exists, add the event to run on the videos "ontimeupdate" + v.ontimeupdate = function () { + sponsorCheck(sponsorTimes); + }; + } else if (xmlhttp.readyState == 4) { + sponsorDataFound = false; - // If the sponsor data exists, add the event to run on the videos "ontimeupdate" - v.ontimeupdate = function () { - sponsorCheck(sponsorTimes); - }; - } else { - sponsorDataFound = false; - } - }; - xmlhttp.send(null); + //check if this video was uploaded recently + //use the invidious api to get the time published + sendRequestToCustomServer('GET', "https://invidio.us/api/v1/videos/" + id, function(xmlhttp, error) { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + let unixTimePublished = JSON.parse(xmlhttp.responseText).published; + + //if less than 3 days old + if ((Date.now() / 1000) - unixTimePublished < 259200) { + setTimeout(() => sponsorsLookup(id), 10000); + } + } + }); + } + }); } function sponsorCheck(sponsorTimes) { // Video skipping @@ -148,24 +161,26 @@ function sponsorCheck(sponsorTimes) { // Video skipping v.currentTime = sponsorTimes[i][1]; lastSponsorTimeSkipped = sponsorTimes[i][0]; - lastSponsorTimeSkippedUUID = UUIDs[i]; + + let currentUUID = UUIDs[i]; + lastSponsorTimeSkippedUUID = currentUUID; //send out the message saying that a sponsor message was skipped openSkipNotice(); - setTimeout(closeSkipNotice, 7000); + setTimeout(() => closeSkipNotice(currentUUID), 7000); } lastTime = v.currentTime; } } -function goBackToPreviousTime() { - if (lastSponsorTimeSkipped != null) { +function goBackToPreviousTime(UUID) { + if (sponsorTimes != undefined) { //add a tiny bit of time to make sure it is not skipped again - v.currentTime = lastSponsorTimeSkipped + 0.001; + v.currentTime = sponsorTimes[UUIDs.indexOf(UUID)][0] + 0.001; - closeSkipNotice(); + closeSkipNotice(UUID); } } @@ -184,11 +199,7 @@ function addPlayerControlsButton() { let startSponsorImage = document.createElement("img"); startSponsorImage.id = "startSponsorImage"; - startSponsorImage.style.height = "60%"; - startSponsorImage.style.top = "0"; - startSponsorImage.style.bottom = "0"; - startSponsorImage.style.display = "block"; - startSponsorImage.style.margin = "auto"; + startSponsorImage.className = "playerButton"; startSponsorImage.src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png"); //add the image to the button @@ -201,6 +212,7 @@ function addPlayerControlsButton() { function removePlayerControlsButton() { document.getElementById("startSponsorButton").style.display = "none"; + document.getElementById("submitButton").style.display = "none"; } //adds or removes the player controls button to what it should be @@ -209,6 +221,7 @@ function updateVisibilityOfPlayerControlsButton() { removePlayerControlsButton(); } else { addPlayerControlsButton(); + addSubmitButton(); } } @@ -222,16 +235,58 @@ function startSponsorClicked() { }); } -function toggleStartSponsorButton() { - if (showingStartSponsor) { - showingStartSponsor = false; - document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png"); - } else { +function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) { + if (showStartSponsor) { showingStartSponsor = true; document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png"); + + if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible) { + document.getElementById("submitButton").style.display = "unset"; + } else if (!uploadButtonVisible) { + //disable submit button + document.getElementById("submitButton").style.display = "none"; + } + } else { + showingStartSponsor = false; + document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png"); + + //disable submit button + document.getElementById("submitButton").style.display = "none"; } } +function toggleStartSponsorButton() { + changeStartSponsorButton(!showingStartSponsor, true); +} + +//shows the submit button on the video player +function addSubmitButton() { + if (document.getElementById("submitButton") != null) { + //it's already added + return; + } + + //make a submit button + let submitButton = document.createElement("button"); + submitButton.id = "submitButton"; + submitButton.className = "ytp-button"; + submitButton.setAttribute("title", "Submit Sponsor Times"); + submitButton.addEventListener("click", submitSponsorTimes); + //hide it at the start + submitButton.style.display = "none"; + + let submitImage = document.createElement("img"); + submitImage.id = "submitButtonImage"; + submitImage.className = "playerButton"; + submitImage.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png"); + + //add the image to the button + submitButton.appendChild(submitImage); + + let referenceNode = document.getElementsByClassName("ytp-right-controls")[0]; + referenceNode.prepend(submitButton); +} + //Opens the notice that tells the user that a sponsor was just skipped function openSkipNotice(){ if (dontShowNotice) { @@ -239,40 +294,57 @@ function openSkipNotice(){ return; } + let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length; + + if (amountOfPreviousNotices > 0) { + //already exists + + let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0]; + previousNotice.classList.add("secondSkipNotice") + } + + let UUID = lastSponsorTimeSkippedUUID; + let noticeElement = document.createElement("div"); - noticeElement.id = "sponsorSkipNotice"; - noticeElement.className = "sponsorSkipObject"; + //what sponsor time this is about + noticeElement.id = "sponsorSkipNotice" + lastSponsorTimeSkippedUUID; + noticeElement.classList.add("sponsorSkipObject"); + noticeElement.classList.add("sponsorSkipNotice"); + noticeElement.style.zIndex = 1 + amountOfPreviousNotices; let logoElement = document.createElement("img"); - logoElement.id = "sponsorSkipLogo"; + logoElement.id = "sponsorSkipLogo" + lastSponsorTimeSkippedUUID; + logoElement.className = "sponsorSkipLogo"; logoElement.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png"); let noticeMessage = document.createElement("div"); - noticeMessage.id = "sponsorSkipMessage"; - noticeMessage.className = "sponsorSkipObject"; + noticeMessage.id = "sponsorSkipMessage" + lastSponsorTimeSkippedUUID; + noticeMessage.classList.add("sponsorSkipMessage"); + noticeMessage.classList.add("sponsorSkipObject"); noticeMessage.innerText = "Hey, you just skipped a sponsor!"; let noticeInfo = document.createElement("p"); - noticeInfo.id = "sponsorSkipInfo"; - noticeInfo.className = "sponsorSkipObject"; + noticeInfo.id = "sponsorSkipInfo" + lastSponsorTimeSkippedUUID; + noticeInfo.classList.add("sponsorSkipInfo"); + noticeInfo.classList.add("sponsorSkipObject"); noticeInfo.innerText = "This message will disapear in 7 seconds"; //thumbs up and down buttons let voteButtonsContainer = document.createElement("div"); - voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer"; + voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + lastSponsorTimeSkippedUUID; voteButtonsContainer.setAttribute("align", "center"); let upvoteButton = document.createElement("img"); - upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + lastSponsorTimeSkippedUUID; upvoteButton.className = "sponsorSkipObject voteButton"; upvoteButton.src = chrome.extension.getURL("icons/upvote.png"); - upvoteButton.addEventListener("click", upvote); + upvoteButton.addEventListener("click", () => vote(1, UUID)); let downvoteButton = document.createElement("img"); - downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + lastSponsorTimeSkippedUUID; downvoteButton.className = "sponsorSkipObject voteButton"; downvoteButton.src = chrome.extension.getURL("icons/downvote.png"); - downvoteButton.addEventListener("click", downvote); + downvoteButton.addEventListener("click", () => vote(0, UUID)); //add thumbs up and down buttons to the container voteButtonsContainer.appendChild(upvoteButton); @@ -284,12 +356,12 @@ function openSkipNotice(){ let goBackButton = document.createElement("button"); goBackButton.innerText = "Go back"; goBackButton.className = "sponsorSkipButton"; - goBackButton.addEventListener("click", goBackToPreviousTime); + goBackButton.addEventListener("click", () => goBackToPreviousTime(UUID)); let hideButton = document.createElement("button"); hideButton.innerText = "Dismiss"; hideButton.className = "sponsorSkipButton"; - hideButton.addEventListener("click", closeSkipNotice); + hideButton.addEventListener("click", () => closeSkipNotice(UUID)); let dontShowAgainButton = document.createElement("button"); dontShowAgainButton.innerText = "Don't Show This Again"; @@ -316,19 +388,23 @@ function openSkipNotice(){ referenceNode.prepend(noticeElement); } -function upvote() { - vote(1); - - closeSkipNotice(); -} - -function downvote() { - vote(0); - +function afterDownvote(UUID) { //change text to say thanks for voting //remove buttons - document.getElementById("sponsorTimesVoteButtonsContainer").removeChild(document.getElementById("sponsorTimesUpvoteButtonsContainer")); - document.getElementById("sponsorTimesVoteButtonsContainer").removeChild(document.getElementById("sponsorTimesDownvoteButtonsContainer")); + let upvoteButton = document.getElementById("sponsorTimesUpvoteButtonsContainer" + UUID); + let downvoteButton = document.getElementById("sponsorTimesDownvoteButtonsContainer" + UUID); + if (upvoteButton != null) { + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(upvoteButton); + } + if (downvoteButton != null) { + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(downvoteButton); + } + + let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + UUID); + if (previousInfoMessage != null) { + //remove it + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(previousInfoMessage); + } //add thanks for voting text let thanksForVotingText = document.createElement("p"); @@ -341,32 +417,89 @@ function downvote() { thanksForVotingInfoText.innerText = "Hit go back to get to where you came from." //add element to div - document.getElementById("sponsorTimesVoteButtonsContainer").appendChild(thanksForVotingText); - document.getElementById("sponsorTimesVoteButtonsContainer").appendChild(thanksForVotingInfoText); + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).appendChild(thanksForVotingText); + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).appendChild(thanksForVotingInfoText); } -function vote(type) { +function addLoadingInfo(message, UUID) { + //change text to say thanks for message + //remove buttons + let upvoteButton = document.getElementById("sponsorTimesUpvoteButtonsContainer" + UUID); + let downvoteButton = document.getElementById("sponsorTimesDownvoteButtonsContainer" + UUID); + if (upvoteButton != null) { + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(upvoteButton); + } + if (downvoteButton != null) { + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(downvoteButton); + } + + let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + UUID); + if (previousInfoMessage != null) { + //remove it + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).removeChild(previousInfoMessage); + } + + //add thanks for voting text + let thanksForVotingText = document.createElement("p"); + thanksForVotingText.id = "sponsorTimesInfoMessage" + UUID; + thanksForVotingText.className = "sponsorTimesInfoMessage"; + thanksForVotingText.innerText = message; + + //add element to div + document.getElementById("sponsorTimesVoteButtonsContainer" + UUID).appendChild(thanksForVotingText); +} + +function vote(type, UUID) { + //add loading info + addLoadingInfo("Loading...", UUID) + chrome.runtime.sendMessage({ message: "submitVote", type: type, - UUID: lastSponsorTimeSkippedUUID + UUID: UUID + }, function(response) { + if (response != undefined) { + //see if it was a success or failure + if (response.successType == 1) { + //success + if (type == 0) { + afterDownvote(UUID); + } else if (type == 1) { + closeSkipNotice(UUID); + } + } else if (response.successType == 0) { + //failure: duplicate vote + addLoadingInfo("It seems you've already voted before", UUID) + } else if (response.successType == -1) { + //failure: duplicate vote + addLoadingInfo("A connection error has occured.", UUID) + } + } }); } -//Closes the notice that tells the user that a sponsor was just skipped -function closeSkipNotice(){ - let notice = document.getElementById("sponsorSkipNotice"); +//Closes the notice that tells the user that a sponsor was just skipped for this UUID +function closeSkipNotice(UUID){ + let notice = document.getElementById("sponsorSkipNotice" + UUID); if (notice != null) { notice.remove(); } } +//Closes all notices that tell the user that a sponsor was just skipped +function closeAllSkipNotices(){ + let notices = document.getElementsByClassName("sponsorSkipNotice"); + for (let i = 0; i < notices.length; i++) { + notices[i].remove(); + } +} + function dontShowNoticeAgain() { - chrome.storage.local.set({"dontShowNoticeAgain": true}); + chrome.storage.sync.set({"dontShowNoticeAgain": true}); dontShowNotice = true; - closeSkipNotice(); + closeAllSkipNotices(); } function sponsorMessageStarted() { @@ -382,6 +515,80 @@ function sponsorMessageStarted() { toggleStartSponsorButton(); } +function submitSponsorTimes() { + //add loading animation + document.getElementById("submitButtonImage").src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png"); + document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite"; + + let currentVideoID = getYouTubeVideoID(document.URL); + + chrome.runtime.sendMessage({ + message: "submitTimes", + videoID: currentVideoID + }, function(response) { + if (response != undefined) { + if (response.statusCode == 200) { + //hide loading message + let submitButton = document.getElementById("submitButton"); + //finish this animation + submitButton.style.animation = "rotate 1s"; + //when the animation is over, hide the button + submitButton.addEventListener("animationend", function() { + submitButton.style.animation = "unset"; + submitButton.style.display = "none"; + }); + + //clear the sponsor times + let sponsorTimeKey = "sponsorTimes" + currentVideoID; + chrome.storage.sync.set({[sponsorTimeKey]: []}); + } else { + //for a more detailed error message, they should check the popup + //show that the upload failed + document.getElementById("submitButton").style.animation = "unset"; + document.getElementById("submitButtonImage").src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png"); + } + } + }); +} + +function sendRequestToServer(type, address, callback) { + let xmlhttp = new XMLHttpRequest(); + + xmlhttp.open(type, serverAddress + address, true); + + if (callback != undefined) { + xmlhttp.onreadystatechange = function () { + callback(xmlhttp, false); + }; + + xmlhttp.onerror = function(ev) { + callback(xmlhttp, true); + }; + } + + //submit this request + xmlhttp.send(); +} + +function sendRequestToCustomServer(type, fullAddress, callback) { + let xmlhttp = new XMLHttpRequest(); + + xmlhttp.open(type, fullAddress, true); + + if (callback != undefined) { + xmlhttp.onreadystatechange = function () { + callback(xmlhttp, false); + }; + + xmlhttp.onerror = function(ev) { + callback(xmlhttp, true); + }; + } + + //submit this request + xmlhttp.send(); +} + function getYouTubeVideoID(url) { // Returns with video id else returns false var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/; var match = url.match(regExp); diff --git a/icons/PlayerUploadFailedIconSponsorBlocker256px.png b/icons/PlayerUploadFailedIconSponsorBlocker256px.png new file mode 100644 index 00000000..57c80838 Binary files /dev/null and b/icons/PlayerUploadFailedIconSponsorBlocker256px.png differ diff --git a/icons/PlayerUploadIconSponsorBlocker256px.png b/icons/PlayerUploadIconSponsorBlocker256px.png new file mode 100644 index 00000000..5be42678 Binary files /dev/null and b/icons/PlayerUploadIconSponsorBlocker256px.png differ diff --git a/manifest.json b/manifest.json index c742d16d..f0a4ba7b 100644 --- a/manifest.json +++ b/manifest.json @@ -22,6 +22,8 @@ "icons/IconSponsorBlocker256px.png", "icons/PlayerStartIconSponsorBlocker256px.png", "icons/PlayerStopIconSponsorBlocker256px.png", + "icons/PlayerUploadIconSponsorBlocker256px.png", + "icons/PlayerUploadFailedIconSponsorBlocker256px.png", "icons/upvote.png", "icons/downvote.png" ], diff --git a/popup.css b/popup.css index 64ae0a17..ec2f1d59 100644 --- a/popup.css +++ b/popup.css @@ -12,6 +12,19 @@ body { background-color: #ffd9d9; } +.recordingSubtitle { + margin-bottom: 10px; +} + +.voteButton { + height: 32px; + margin-right: 15px; + cursor: pointer; +} +.voteButton:hover { + filter: brightness(80%); +} + .greenButton { background-color:#ec1c1c; -moz-border-radius:28px; diff --git a/popup.html b/popup.html index 79b7adde..b180d788 100644 --- a/popup.html +++ b/popup.html @@ -24,8 +24,26 @@
- -

Record the times of a sponsorship

+ +
+ + + + + +

Record the times of a sponsorship

+ +

Click the button below when the sponsorship starts and ends to record and @@ -48,8 +66,18 @@

+ +

-
diff --git a/popup.js b/popup.js index d5b984f0..36f6e2d2 100644 --- a/popup.js +++ b/popup.js @@ -6,6 +6,7 @@ document.getElementById("showNoticeAgain").addEventListener("click", showNoticeA document.getElementById("hideVideoPlayerControls").addEventListener("click", hideVideoPlayerControls); document.getElementById("showVideoPlayerControls").addEventListener("click", showVideoPlayerControls); document.getElementById("optionsButton").addEventListener("click", openOptions); +document.getElementById("reportAnIssue").addEventListener("click", reportAnIssue); //if true, the button now selects the end time var startTimeChosen = false; @@ -21,7 +22,7 @@ var isYouTubeTab = false; //if the don't show notice again variable is true, an option to // disable should be available -chrome.storage.local.get(["dontShowNoticeAgain"], function(result) { +chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) { let dontShowNoticeAgain = result.dontShowNoticeAgain; if (dontShowNoticeAgain != undefined && dontShowNoticeAgain) { document.getElementById("showNoticeAgain").style.display = "unset"; @@ -29,7 +30,7 @@ chrome.storage.local.get(["dontShowNoticeAgain"], function(result) { }); //show proper video player controls option -chrome.storage.local.get(["hideVideoPlayerControls"], function(result) { +chrome.storage.sync.get(["hideVideoPlayerControls"], function(result) { let hideVideoPlayerControls = result.hideVideoPlayerControls; if (hideVideoPlayerControls != undefined && hideVideoPlayerControls) { document.getElementById("hideVideoPlayerControls").style.display = "none"; @@ -37,6 +38,20 @@ chrome.storage.local.get(["hideVideoPlayerControls"], function(result) { } }); +//get the amount of times this user has contributed and display it to thank them +chrome.storage.sync.get(["sponsorTimesContributed"], function(result) { + if (result.sponsorTimesContributed != undefined) { + let sponsorTimesContributionsDisplay = document.getElementById("sponsorTimesContributionsDisplay"); + + if (result.sponsorTimesContributed > 1) { + sponsorTimesContributionsDisplay.innerText = "So far, you've submitted " + result.sponsorTimesContributed + " sponsor times."; + } else { + sponsorTimesContributionsDisplay.innerText = "So far, you've submitted " + result.sponsorTimesContributed + " sponsor time."; + } + sponsorTimesContributionsDisplay.style.display = "unset"; + } +}); + chrome.tabs.query({ active: true, currentWindow: true @@ -54,7 +69,7 @@ function loadTabData(tabs) { //load video times for this video let sponsorTimeKey = "sponsorTimes" + currentVideoID; - chrome.storage.local.get([sponsorTimeKey], function(result) { + chrome.storage.sync.get([sponsorTimeKey], function(result) { let sponsorTimesStorage = result[sponsorTimeKey]; if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) { if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) { @@ -139,7 +154,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { sponsorTimes[sponsorTimesIndex][startTimeChosen ? 1 : 0] = request.time; let sponsorTimeKey = "sponsorTimes" + currentVideoID; - chrome.storage.local.set({[sponsorTimeKey]: sponsorTimes}); + chrome.storage.sync.set({[sponsorTimeKey]: sponsorTimes}); updateStartTimeChosen(); @@ -164,6 +179,56 @@ function displayDownloadedSponsorTimes(request) { if (request.sponsorTimes != undefined) { //set it to the message document.getElementById("downloadedSponsorMessageTimes").innerHTML = getSponsorTimesMessage(request.sponsorTimes); + + //add them as buttons to the issue reporting container + let container = document.getElementById("issueReporterTimeButtons"); + for (let i = 0; i < request.sponsorTimes.length; i++) { + let sponsorTimeButton = document.createElement("button"); + sponsorTimeButton.className = "warningButton"; + sponsorTimeButton.innerText = getFormattedTime(request.sponsorTimes[i][0]) + " to " + getFormattedTime(request.sponsorTimes[i][1]); + + let votingButtons = document.createElement("div"); + + let UUID = request.UUIDs[i]; + + //thumbs up and down buttons + let voteButtonsContainer = document.createElement("div"); + voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID; + voteButtonsContainer.setAttribute("align", "center"); + voteButtonsContainer.style.display = "none" + + let upvoteButton = document.createElement("img"); + upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID; + upvoteButton.className = "voteButton"; + upvoteButton.src = chrome.extension.getURL("icons/upvote.png"); + upvoteButton.addEventListener("click", () => vote(1, UUID)); + + let downvoteButton = document.createElement("img"); + downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID; + downvoteButton.className = "voteButton"; + downvoteButton.src = chrome.extension.getURL("icons/downvote.png"); + downvoteButton.addEventListener("click", () => vote(0, UUID)); + + //add thumbs up and down buttons to the container + voteButtonsContainer.appendChild(document.createElement("br")); + voteButtonsContainer.appendChild(document.createElement("br")); + voteButtonsContainer.appendChild(upvoteButton); + voteButtonsContainer.appendChild(downvoteButton); + + //add click listener to open up vote panel + sponsorTimeButton.addEventListener("click", function() { + voteButtonsContainer.style.display = "unset"; + }); + + container.appendChild(sponsorTimeButton); + container.appendChild(voteButtonsContainer); + + //if it is not the last iteration + if (i != request.sponsorTimes.length - 1) { + container.appendChild(document.createElement("br")); + container.appendChild(document.createElement("br")); + } + } } } @@ -190,14 +255,16 @@ function getSponsorTimesMessage(sponsorTimes) { } function clearTimes() { - //check if the player controls should be toggled - if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) { + //send new sponsor time state to tab + if (sponsorTimes.length > 0) { chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { chrome.tabs.sendMessage(tabs[0].id, { - message: "toggleStartSponsorButton" + message: "changeStartSponsorButton", + showStartSponsor: true, + uploadButtonVisible: false }); }); } @@ -206,7 +273,7 @@ function clearTimes() { sponsorTimes = []; let sponsorTimeKey = "sponsorTimes" + currentVideoID; - chrome.storage.local.set({[sponsorTimeKey]: sponsorTimes}); + chrome.storage.sync.set({[sponsorTimeKey]: sponsorTimes}); displaySponsorTimes(); @@ -217,18 +284,41 @@ function clearTimes() { } function submitTimes() { + //make info message say loading + document.getElementById("submitTimesInfoMessage").innerText = "Loading..."; + document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; + if (sponsorTimes.length > 0) { chrome.runtime.sendMessage({ message: "submitTimes", videoID: currentVideoID - }, function(request) { - clearTimes(); + }, function(response) { + if (response != undefined) { + if (response.statusCode == 200) { + //hide loading message + document.getElementById("submitTimesInfoMessageContainer").style.display = "none"; + + clearTimes(); + } else if(response.statusCode == 400) { + document.getElementById("submitTimesInfoMessage").innerText = "Server said this request was invalid"; + document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; + } else if(response.statusCode == 429) { + document.getElementById("submitTimesInfoMessage").innerText = "You have submitted too many sponsor times for this one video, are you sure there are this many?"; + document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; + } else if(response.statusCode == 409) { + document.getElementById("submitTimesInfoMessage").innerText = "This has already been submitted before"; + document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; + } else { + document.getElementById("submitTimesInfoMessage").innerText = "There was an error submitting your sponsor times, please try again later"; + document.getElementById("submitTimesInfoMessageContainer").style.display = "unset"; + } + } }); } } function showNoticeAgain() { - chrome.storage.local.set({"dontShowNoticeAgain": false}); + chrome.storage.sync.set({"dontShowNoticeAgain": false}); chrome.tabs.query({ active: true, @@ -243,7 +333,7 @@ function showNoticeAgain() { } function hideVideoPlayerControls() { - chrome.storage.local.set({"hideVideoPlayerControls": true}); + chrome.storage.sync.set({"hideVideoPlayerControls": true}); chrome.tabs.query({ active: true, @@ -260,7 +350,7 @@ function hideVideoPlayerControls() { } function showVideoPlayerControls() { - chrome.storage.local.set({"hideVideoPlayerControls": false}); + chrome.storage.sync.set({"hideVideoPlayerControls": false}); chrome.tabs.query({ active: true, @@ -292,13 +382,15 @@ function resetStartTimeChosen() { document.getElementById("sponsorStart").innerHTML = "Sponsorship Starts Now"; } +//hides and shows the submit times button when needed function showSubmitTimesIfNecessary() { //check if an end time has been specified for the latest sponsor time if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length > 1) { //show submit times button - document.getElementById("submitTimes").style.display = "unset"; + document.getElementById("submitTimesContainer").style.display = "unset"; } else { - document.getElementById("submitTimes").style.display = "none"; + //hide submit times button + document.getElementById("submitTimesContainer").style.display = "none"; } } @@ -314,6 +406,52 @@ function displayNoVideo() { "If you know this is a YouTube tab, close this popup and open it again."; } +function reportAnIssue() { + document.getElementById("issueReporterContainer").style.display = "unset"; + document.getElementById("reportAnIssue").style.display = "none"; +} + +function addVoteMessage(message, UUID) { + let container = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID); + //remove all children + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + let thanksForVotingText = document.createElement("h2"); + thanksForVotingText.innerText = message; + //there are already breaks there + thanksForVotingText.style.marginBottom = "0px"; + + container.appendChild(thanksForVotingText); +} + +function vote(type, UUID) { + //add loading info + addVoteMessage("Loading...", UUID) + + //send the vote message to the tab + chrome.runtime.sendMessage({ + message: "submitVote", + type: type, + UUID: UUID + }, function(response) { + if (response != undefined) { + //see if it was a success or failure + if (response.successType == 1) { + //success + addVoteMessage("Thanks for voting!", UUID) + } else if (response.successType == 0) { + //failure: duplicate vote + addVoteMessage("You have already voted this way before.", UUID) + } else if (response.successType == -1) { + //failure: duplicate vote + addVoteMessage("A connection error has occured.", UUID) + } + } + }); +} + //converts time in seconds to minutes:seconds function getFormattedTime(seconds) { let minutes = Math.floor(seconds / 60);