SponsorBlock/content.js

1094 lines
36 KiB
JavaScript
Raw Normal View History

2019-07-09 20:50:19 +02:00
//was sponsor data found when doing SponsorsLookup
var sponsorDataFound = false;
2019-08-08 21:33:05 +02:00
var previousVideoID = null;
//the actual sponsorTimes if loaded and UUIDs associated with them
var sponsorTimes = null;
var UUIDs = null;
//what video id are these sponsors for
var sponsorVideoID = null;
//these are sponsors that have been downvoted
var hiddenSponsorTimes = [];
/** @type {Array[boolean]} Has the sponsor been skipped */
var sponsorSkipped = [];
//the video
var v;
var listenerAdded;
//the video id of the last preview bar update
var lastPreviewBarUpdate;
//whether the duration listener listening for the duration changes of the video has been setup yet
var durationListenerSetUp = false;
//the channel this video is about
var channelURL;
//the title of the last video loaded. Used to make sure the channel URL has been updated yet.
var title;
//is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false;
2019-08-13 01:34:44 +02:00
// create preview bar
var previewBar = null;
2019-08-13 01:34:44 +02:00
//the player controls on the YouTube player
var controls = null;
2019-08-20 23:59:34 +02:00
// Direct Links
videoIDChange(getYouTubeVideoID(document.URL));
//the last time looked at (used to see if this time is in the interval)
var lastTime = -1;
2019-01-18 20:49:43 +01:00
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
var sponsorLookupRetries = 0;
2019-07-10 03:44:41 +02:00
//the last time in the video a sponsor was skipped
//used for the go back button
var lastSponsorTimeSkipped = null;
//used for ratings
var lastSponsorTimeSkippedUUID = null;
2019-07-10 03:44:41 +02:00
//if showing the start sponsor button or the end sponsor button on the player
var showingStartSponsor = true;
//should the video controls buttons be added
//TODO: If invidious gets video controls, change the code where this is set from chrome.sync as well.
var hideVideoPlayerControls = onInvidious;
var hideInfoButtonPlayerControls = onInvidious;
var hideDeleteButtonPlayerControls = onInvidious;
//the sponsor times being prepared to be submitted
var sponsorTimesSubmitting = [];
//becomes true when isInfoFound is called
//this is used to close the popup on YouTube when the other popup opens
var popupInitialised = false;
//get messages from the background script and the popup
chrome.runtime.onMessage.addListener(messageListener);
2019-08-08 20:26:26 +02:00
function messageListener(request, sender, sendResponse) {
2019-08-20 01:21:19 +02:00
//messages from popup script
2019-08-24 13:55:42 +02:00
switch(request.message){
2019-08-28 03:17:21 +02:00
case "update":
videoIDChange(getYouTubeVideoID(document.URL));
break;
case "sponsorStart":
sponsorMessageStarted(sendResponse);
2019-08-20 01:21:19 +02:00
2019-08-28 03:17:21 +02:00
break;
case "sponsorDataChanged":
updateSponsorTimesSubmitting();
2019-08-20 01:21:19 +02:00
2019-08-28 03:17:21 +02:00
break;
case "isInfoFound":
//send the sponsor times along with if it's found
sendResponse({
found: sponsorDataFound,
sponsorTimes: sponsorTimes,
hiddenSponsorTimes: hiddenSponsorTimes,
UUIDs: UUIDs
});
if (popupInitialised && document.getElementById("sponsorBlockPopupContainer") != null) {
//the popup should be closed now that another is opening
closeInfoMenu();
}
2019-07-09 21:55:33 +02:00
2019-08-28 03:17:21 +02:00
popupInitialised = true;
break;
case "getVideoID":
sendResponse({
videoID: sponsorVideoID
});
2019-08-28 03:17:21 +02:00
break;
case "getVideoDuration":
sendResponse({
2019-08-23 05:12:30 +02:00
duration: v.duration
2019-08-28 03:17:21 +02:00
});
2019-08-23 05:12:30 +02:00
2019-08-28 03:17:21 +02:00
break;
case "skipToTime":
v.currentTime = request.time;
return
case "getCurrentTime":
sendResponse({
currentTime: v.currentTime
});
2019-08-28 03:17:21 +02:00
break;
case "getChannelURL":
sendResponse({
2019-08-20 01:21:19 +02:00
channelURL: channelURL
2019-08-28 03:17:21 +02:00
});
2019-08-28 03:17:21 +02:00
break;
case "isChannelWhitelisted":
sendResponse({
value: channelWhitelisted
});
2019-08-28 03:17:21 +02:00
break;
case "whitelistChange":
channelWhitelisted = request.value;
sponsorsLookup(sponsorVideoID);
2019-08-28 03:17:21 +02:00
break;
case "dontShowNotice":
2020-01-01 14:01:42 +01:00
SB.config.dontShowNotice = true;
2019-08-28 03:17:21 +02:00
break;
case "changeStartSponsorButton":
changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible);
2019-08-28 03:17:21 +02:00
break;
2020-01-01 14:01:42 +01:00
2019-08-28 03:17:21 +02:00
case "showNoticeAgain":
2020-01-01 14:01:42 +01:00
SB.config.dontShowNotice = true;
2019-08-28 03:17:21 +02:00
break;
2020-01-01 14:46:09 +01:00
2019-08-28 03:17:21 +02:00
case "changeVideoPlayerControlsVisibility":
2020-01-01 14:46:09 +01:00
SB.config.hideVideoPlayerControls = request.value;
2019-08-28 03:17:21 +02:00
updateVisibilityOfPlayerControlsButton();
2019-08-28 03:17:21 +02:00
break;
case "changeInfoButtonPlayerControlsVisibility":
2020-01-01 14:46:09 +01:00
SB.config.hideInfoButtonPlayerControls = request.value;
2019-08-28 03:17:21 +02:00
updateVisibilityOfPlayerControlsButton();
2019-08-28 03:17:21 +02:00
break;
case "changeDeleteButtonPlayerControlsVisibility":
2020-01-01 14:46:09 +01:00
SB.config.hideDeleteButtonPlayerControls = request.value;
2019-08-28 03:17:21 +02:00
updateVisibilityOfPlayerControlsButton();
2019-08-28 03:17:21 +02:00
break;
case "trackViewCount":
2019-12-31 23:04:40 +01:00
SB.config.trackViewCount = request.value;
2019-08-28 03:17:21 +02:00
break;
2019-08-20 01:21:19 +02:00
}
}
//check for hotkey pressed
document.onkeydown = async function(e){
2019-08-20 01:21:19 +02:00
e = e || window.event;
2019-08-29 00:11:29 +02:00
var key = e.key;
2019-08-20 01:21:19 +02:00
let video = document.getElementById("movie_player");
2019-12-31 21:56:30 +01:00
let startSponsorKey = SB.config.startSponsorKeybind;
2019-12-31 21:56:30 +01:00
let submitKey = SB.config.submitKeybind;
2019-08-20 01:21:19 +02:00
//is the video in focus, otherwise they could be typing a comment
if (document.activeElement === video) {
if(key == startSponsorKey.startSponsorKeybind){
2019-08-20 01:21:19 +02:00
//semicolon
startSponsorClicked();
} else if (key == submitKey.submitKeybind) {
2019-08-20 01:21:19 +02:00
//single quote
submitSponsorTimes();
}
}
}
function resetValues() {
2019-08-20 01:21:19 +02:00
//reset last sponsor times
lastTime = -1;
2019-08-20 01:21:19 +02:00
//reset sponsor times
sponsorTimes = null;
UUIDs = null;
sponsorLookupRetries = 0;
2019-08-20 01:21:19 +02:00
//empty the preview bar
if (previewBar !== null) {
previewBar.set([], [], 0);
}
2019-08-20 01:21:19 +02:00
//reset sponsor data found check
sponsorDataFound = false;
}
function videoIDChange(id) {
//if the id has not changed return
if (sponsorVideoID === id) return;
//set the global videoID
sponsorVideoID = id;
resetValues();
//id is not valid
if (!id) return;
let channelIDPromise = wait(getChannelID);
channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true);
//setup the preview bar
2019-08-20 01:21:19 +02:00
if (previewBar == null) {
//create it
wait(getControls).then(result => {
2019-12-05 21:35:25 +01:00
const progressElementSelectors = [
// For YouTube
"ytp-progress-bar-container",
"no-model cue-range-markers",
// For Invidious/VideoJS
"vjs-progress-holder"
];
for (const selector of progressElementSelectors) {
const el = document.getElementsByClassName(selector);
if (el && el.length && el[0]) {
previewBar = new PreviewBar(el[0]);
break;
}
}
});
}
2019-08-20 01:21:19 +02:00
//warn them if they had unsubmitted times
if (previousVideoID != null) {
//get the sponsor times from storage
2020-01-08 04:59:50 +01:00
let sponsorTimes = SB.config.sponsorTimes.get(previousVideoID);
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
//warn them that they have unsubmitted sponsor times
chrome.runtime.sendMessage({
message: "alertPrevious",
previousVideoID: previousVideoID
})
}
2019-08-20 01:21:19 +02:00
2020-01-08 04:59:50 +01:00
//set the previous video id to the currentID
previousVideoID = id;
2019-08-20 01:21:19 +02:00
} else {
//set the previous id now, don't wait for chrome.storage.get
previousVideoID = id;
}
2019-08-20 01:21:19 +02:00
//close popup
closeInfoMenu();
sponsorsLookup(id, channelIDPromise);
2019-08-20 01:21:19 +02:00
//make sure everything is properly added
updateVisibilityOfPlayerControlsButton();
2019-08-20 01:21:19 +02:00
//reset sponsor times submitting
sponsorTimesSubmitting = [];
//see if the onvideo control image needs to be changed
2019-08-21 19:17:30 +02:00
wait(getControls).then(result => {
chrome.runtime.sendMessage({
message: "getSponsorTimes",
videoID: id
}, function(response) {
if (response != undefined) {
let sponsorTimes = response.sponsorTimes;
if (sponsorTimes != null && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length >= 2) {
changeStartSponsorButton(true, true);
} else if (sponsorTimes != null && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
2019-08-24 03:01:31 +02:00
changeStartSponsorButton(false, true);
2019-08-21 19:17:30 +02:00
} else {
changeStartSponsorButton(true, false);
2019-08-24 03:01:31 +02:00
}
2019-08-21 19:17:30 +02:00
//see if this data should be saved in the sponsorTimesSubmitting variable
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
sponsorTimesSubmitting = sponsorTimes;
2019-08-24 03:01:31 +02:00
updatePreviewBar();
2019-08-21 19:17:30 +02:00
}
}
});
});
2019-08-20 01:21:19 +02:00
//see if video controls buttons should be added
if (!onInvidious) {
updateVisibilityOfPlayerControlsButton();
}
}
function sponsorsLookup(id, channelIDPromise) {
2019-08-20 01:21:19 +02:00
v = document.querySelector('video') // Youtube video player
//there is no video here
if (v == null) {
setTimeout(() => sponsorsLookup(id), 100);
return;
}
2019-08-20 01:21:19 +02:00
if (!durationListenerSetUp) {
durationListenerSetUp = true;
2019-08-20 01:21:19 +02:00
//wait until it is loaded
v.addEventListener('durationchange', updatePreviewBar);
}
2019-11-23 23:53:04 +01:00
if (channelIDPromise != null) {
if (channelIDPromise.isFulfilled) {
whitelistCheck();
} else if (channelIDPromise.isRejected) {
//try again
wait(getChannelID).then(whitelistCheck).catch();
} else {
//add it as a then statement
channelIDPromise.then(whitelistCheck);
}
}
//check database for sponsor times
//made true once a setTimeout has been created to try again after a server error
let recheckStarted = false;
2019-08-20 01:21:19 +02:00
sendRequestToServer('GET', "/api/getVideoSponsorTimes?videoID=" + id, function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
sponsorDataFound = true;
sponsorTimes = JSON.parse(xmlhttp.responseText).sponsorTimes;
UUIDs = JSON.parse(xmlhttp.responseText).UUIDs;
// Reset skip save
sponsorSkipped = [];
2019-08-20 01:21:19 +02:00
//update the preview bar
//leave the type blank for now until categories are added
if (lastPreviewBarUpdate == id || (lastPreviewBarUpdate == null && !isNaN(v.duration))) {
//set it now
//otherwise the listener can handle it
updatePreviewBar();
}
sponsorLookupRetries = 0;
} else if (xmlhttp.readyState == 4 && xmlhttp.status == 404) {
sponsorDataFound = false;
//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 + '?fields=published', function(xmlhttp, error) {
2019-08-20 01:21:19 +02:00
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) {
//TODO lower when server becomes better
setTimeout(() => sponsorsLookup(id), 180000);
2019-08-20 01:21:19 +02:00
}
}
});
sponsorLookupRetries = 0;
} else if (xmlhttp.readyState == 4 && sponsorLookupRetries < 90 && !recheckStarted) {
recheckStarted = true;
//TODO lower when server becomes better (back to 1 second)
2019-08-20 01:21:19 +02:00
//some error occurred, try again in a second
setTimeout(() => sponsorsLookup(id), 10000);
2019-08-20 01:21:19 +02:00
sponsorLookupRetries++;
}
});
2019-08-20 01:21:19 +02:00
//add the event to run on the videos "ontimeupdate"
2019-12-31 23:04:40 +01:00
if (!SB.config.disableSkipping) {
v.ontimeupdate = function () {
sponsorCheck();
};
}
}
function updatePreviewBar() {
let localSponsorTimes = sponsorTimes;
if (localSponsorTimes == null) localSponsorTimes = [];
let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
//create an array of the sponsor types
let types = [];
for (let i = 0; i < localSponsorTimes.length; i++) {
2019-12-14 04:38:00 +01:00
if (!hiddenSponsorTimes.includes(i)) {
types.push("sponsor");
} else {
// Don't show this sponsor
types.push(null);
}
}
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
types.push("previewSponsor");
}
wait(() => previewBar !== null).then((result) => previewBar.set(allSponsorTimes, types, v.duration));
2019-08-20 01:21:19 +02:00
//update last video id
lastPreviewBarUpdate = sponsorVideoID;
}
function getChannelID() {
2019-08-20 01:21:19 +02:00
//get channel id
let channelURLContainer = null;
channelURLContainer = document.querySelector("#channel-name > #container > #text-container > #text");
if (channelURLContainer !== null) {
channelURLContainer = channelURLContainer.firstElementChild;
} else if (onInvidious) {
// Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers...
2019-12-31 08:23:23 +01:00
channelURLContainer = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a");
} else {
2019-08-20 01:21:19 +02:00
//old YouTube theme
let channelContainers = document.getElementsByClassName("yt-user-info");
2019-08-20 01:21:19 +02:00
if (channelContainers.length != 0) {
channelURLContainer = channelContainers[0].firstElementChild;
2019-12-05 21:35:25 +01:00
}
}
if (channelURLContainer === null) {
2019-08-20 01:21:19 +02:00
//try later
return false;
}
//first get the title to make sure a title change has occurred (otherwise the next video might still be loading)
let titleInfoContainer = document.getElementById("info-contents");
let currentTitle = "";
if (titleInfoContainer != null) {
currentTitle = titleInfoContainer.firstElementChild.firstElementChild.querySelector(".title").firstElementChild.innerText;
} else if (onInvidious) {
2019-12-05 21:35:25 +01:00
// Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers...
currentTitle = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a > div > span").textContent;
} else {
//old YouTube theme
currentTitle = document.getElementById("eow-title").innerText;
}
if (title == currentTitle) {
//video hasn't changed yet, wait
//try later
return false;
2019-08-20 01:21:19 +02:00
}
title = currentTitle;
2019-08-20 01:21:19 +02:00
channelURL = channelURLContainer.getAttribute("href");
//reset variables
channelWhitelisted = false;
}
//checks if this channel is whitelisted, should be done only after the channelID has been loaded
function whitelistCheck() {
2019-08-20 01:21:19 +02:00
//see if this is a whitelisted channel
2019-12-31 21:56:30 +01:00
let whitelistedChannels = SB.config.whitelistedChannels;
console.log(channelURL)
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelURL)) {
2019-08-20 01:21:19 +02:00
channelWhitelisted = true;
}
}
//video skipping
function sponsorCheck() {
2019-12-31 23:04:40 +01:00
if (SB.config.disableSkipping) {
// Make sure this isn't called again
v.ontimeupdate = null;
return;
} else if (channelWhitelisted) {
return;
}
2019-08-20 01:21:19 +02:00
let skipHappened = false;
if (sponsorTimes != null) {
//see if any sponsor start time was just passed
for (let i = 0; i < sponsorTimes.length; i++) {
//if something was skipped
if (checkSponsorTime(sponsorTimes, i, true)) {
skipHappened = true;
break;
}
}
}
2019-08-20 01:21:19 +02:00
if (!skipHappened) {
//check for the "preview" sponsors (currently edited by this user)
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
//must be a finished sponsor and be valid
if (sponsorTimesSubmitting[i].length > 1 && sponsorTimesSubmitting[i][1] > sponsorTimesSubmitting[i][0]) {
checkSponsorTime(sponsorTimesSubmitting, i, false);
}
}
}
2019-08-20 01:21:19 +02:00
//don't keep track until they are loaded in
if (sponsorTimes != null || sponsorTimesSubmitting.length > 0) {
lastTime = v.currentTime;
}
}
2019-01-18 20:49:43 +01:00
function checkSponsorTime(sponsorTimes, index, openNotice) {
2019-08-20 01:21:19 +02:00
//this means part of the video was just skipped
if (Math.abs(v.currentTime - lastTime) > 1 && lastTime != -1) {
//make lastTime as if the video was playing normally
lastTime = v.currentTime - 0.0001;
}
if (checkIfTimeToSkip(v.currentTime, sponsorTimes[index][0], sponsorTimes[index][1]) && !hiddenSponsorTimes.includes(index)) {
2019-08-20 01:21:19 +02:00
//skip it
skipToTime(v, index, sponsorTimes, openNotice);
2019-08-20 01:21:19 +02:00
//something was skipped
return true;
}
2019-08-20 01:21:19 +02:00
return false;
}
function checkIfTimeToSkip(currentVideoTime, startTime, endTime) {
2019-08-20 01:21:19 +02:00
//If the sponsor time is in between these times, skip it
//Checks if the last time skipped to is not too close to now, to make sure not to get too many
// sponsor times in a row (from one troll)
//the last term makes 0 second start times possible only if the video is not setup to start at a different time from zero
return (Math.abs(currentVideoTime - startTime) < 3 && startTime >= lastTime && startTime <= currentVideoTime) ||
(lastTime == -1 && startTime == 0 && currentVideoTime < endTime)
}
//skip fromt he start time to the end time for a certain index sponsor time
function skipToTime(v, index, sponsorTimes, openNotice) {
2019-12-31 23:04:40 +01:00
if (!SB.config.disableAutoSkip) {
v.currentTime = sponsorTimes[index][1];
}
2019-08-20 01:21:19 +02:00
lastSponsorTimeSkipped = sponsorTimes[index][0];
2019-08-20 01:21:19 +02:00
let currentUUID = UUIDs[index];
lastSponsorTimeSkippedUUID = currentUUID;
if (openNotice) {
//send out the message saying that a sponsor message was skipped
2020-01-01 14:01:42 +01:00
if (!SB.config.dontShowNotice) {
2019-12-31 23:04:40 +01:00
let skipNotice = new SkipNotice(this, currentUUID, SB.config.disableAutoSkip);
2019-08-20 01:21:19 +02:00
//auto-upvote this sponsor
2019-12-31 23:04:40 +01:00
if (SB.config.trackViewCount && !SB.config.disableAutoSkip) {
2019-08-20 01:21:19 +02:00
vote(1, currentUUID, null);
}
}
}
2019-12-29 06:10:08 +01:00
//send telemetry that a this sponsor was skipped
2019-12-31 23:04:40 +01:00
if (SB.config.trackViewCount && !sponsorSkipped[index]) {
2019-12-29 06:10:08 +01:00
sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + currentUUID);
2019-12-31 23:04:40 +01:00
if (!SB.config.disableAutoSkip) {
// Count this as a skip
2019-12-31 21:56:30 +01:00
SB.config.minutesSaved = SB.config.minutesSaved + (sponsorTimes[index][1] - sponsorTimes[index][0]) / 60;
SB.config.skipCount = SB.config.skipCount + 1;
sponsorSkipped[index] = true;
}
2019-08-20 01:21:19 +02:00
}
}
function unskipSponsorTime(UUID) {
2019-08-20 01:21:19 +02:00
if (sponsorTimes != null) {
//add a tiny bit of time to make sure it is not skipped again
v.currentTime = sponsorTimes[UUIDs.indexOf(UUID)][0] + 0.001;
}
}
2019-07-10 03:44:41 +02:00
function reskipSponsorTime(UUID) {
2019-08-20 01:21:19 +02:00
if (sponsorTimes != null) {
//add a tiny bit of time to make sure it is not skipped again
v.currentTime = sponsorTimes[UUIDs.indexOf(UUID)][1];
}
2019-07-10 03:44:41 +02:00
}
function removePlayerControlsButton() {
2019-08-21 20:10:49 +02:00
if (!sponsorVideoID) return;
2019-08-20 01:21:19 +02:00
document.getElementById("startSponsorButton").style.display = "none";
2019-08-20 01:21:19 +02:00
document.getElementById("submitButton").style.display = "none";
}
2019-08-20 01:21:19 +02:00
2019-08-22 20:39:17 +02:00
function createButton(baseID, title, callback, imageName, isDraggable=false) {
2019-08-24 03:02:17 +02:00
if (document.getElementById(baseID + "Button") != null) return;
2019-08-20 01:21:19 +02:00
// Button HTML
let newButton = document.createElement("button");
newButton.draggable = isDraggable;
2019-08-24 03:02:17 +02:00
newButton.id = baseID + "Button";
newButton.className = "ytp-button playerButton";
newButton.setAttribute("title", chrome.i18n.getMessage(title));
newButton.addEventListener("click", callback);
2019-08-20 01:21:19 +02:00
// Image HTML
let newButtonImage = document.createElement("img");
newButton.draggable = isDraggable;
2019-08-24 03:02:17 +02:00
newButtonImage.id = baseID + "Image";
newButtonImage.className = "playerButtonImage";
2019-08-24 03:02:17 +02:00
newButtonImage.src = chrome.extension.getURL("icons/" + imageName);
2019-08-20 01:21:19 +02:00
// Append image to button
newButton.appendChild(newButtonImage);
2019-08-20 01:21:19 +02:00
// Add the button to player
controls.prepend(newButton);
}
function getControls() {
let controls = document.getElementsByClassName("ytp-right-controls");
2019-12-05 21:35:25 +01:00
if (!controls || controls.length === 0) {
// The invidious video element's controls element
controls = document.getElementsByClassName("vjs-control-bar");
return (!controls || controls.length === 0) ? false : controls[controls.length - 1];
} else {
2019-12-05 21:35:25 +01:00
return controls[controls.length - 1];
}
};
//adds all the player controls buttons
async function createButtons() {
let result = await wait(getControls).catch();
//set global controls variable
controls = result;
// Add button if does not already exist in html
createButton("startSponsor", "sponsorStart", startSponsorClicked, "PlayerStartIconSponsorBlocker256px.png");
createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png")
createButton("delete", "clearTimes", clearSponsorTimes, "PlayerDeleteIconSponsorBlocker256px.png");
createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png");
}
//adds or removes the player controls button to what it should be
async function updateVisibilityOfPlayerControlsButton() {
2019-08-20 01:21:19 +02:00
//not on a proper video yet
if (!sponsorVideoID) return;
2019-08-20 01:21:19 +02:00
await createButtons();
2020-01-01 14:46:09 +01:00
if (SB.config.hideDeleteButtonPlayerControls) {
2019-08-20 01:21:19 +02:00
removePlayerControlsButton();
}
//don't show the info button on embeds
2020-01-01 14:46:09 +01:00
if (SB.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/")) {
2019-08-20 01:21:19 +02:00
document.getElementById("infoButton").style.display = "none";
}
2020-01-01 14:46:09 +01:00
if (SB.config.hideDeleteButtonPlayerControls) {
2019-08-20 01:21:19 +02:00
document.getElementById("deleteButton").style.display = "none";
}
}
2019-07-12 22:44:50 +02:00
function startSponsorClicked() {
2019-08-20 01:21:19 +02:00
//it can't update to this info yet
closeInfoMenu();
toggleStartSponsorButton();
//send back current time with message
chrome.runtime.sendMessage({
message: "addSponsorTime",
time: v.currentTime,
videoID: sponsorVideoID
2019-08-20 01:21:19 +02:00
}, function(response) {
//see if the sponsorTimesSubmitting needs to be updated
updateSponsorTimesSubmitting();
});
}
function updateSponsorTimesSubmitting() {
2019-08-20 01:21:19 +02:00
chrome.runtime.sendMessage({
message: "getSponsorTimes",
videoID: sponsorVideoID
2019-08-20 01:21:19 +02:00
}, function(response) {
if (response != undefined) {
let sponsorTimes = response.sponsorTimes;
//see if this data should be saved in the sponsorTimesSubmitting variable
if (sponsorTimes != undefined) {
sponsorTimesSubmitting = sponsorTimes;
updatePreviewBar();
2019-08-20 01:21:19 +02:00
}
}
});
}
//is the submit button on the player loaded yet
function isSubmitButtonLoaded() {
return document.getElementById("submitButton") !== null;
}
2019-08-21 19:17:30 +02:00
async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
if(!sponsorVideoID) return false;
//make sure submit button is loaded
await wait(isSubmitButtonLoaded);
2019-08-20 01:21:19 +02:00
//if it isn't visible, there is no data
2020-01-01 14:46:09 +01:00
let shouldHide = (uploadButtonVisible && !SB.config.hideDeleteButtonPlayerControls) ? "unset" : "none"
document.getElementById("deleteButton").style.display = shouldHide;
2019-08-20 01:21:19 +02:00
if (showStartSponsor) {
showingStartSponsor = true;
document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
2020-01-01 14:46:09 +01:00
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible && !SB.config.hideInfoButtonPlayerControls) {
2019-08-20 01:21:19 +02:00
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");
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorEND"));
//disable submit button
document.getElementById("submitButton").style.display = "none";
}
}
function toggleStartSponsorButton() {
2019-08-20 01:21:19 +02:00
changeStartSponsorButton(!showingStartSponsor, true);
}
function openInfoMenu() {
2019-08-20 01:21:19 +02:00
if (document.getElementById("sponsorBlockPopupContainer") != null) {
//it's already added
return;
}
popupInitialised = false;
//hide info button
document.getElementById("infoButton").style.display = "none";
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var popup = document.createElement("div");
popup.id = "sponsorBlockPopupContainer";
popup.innerHTML = xmlhttp.responseText
//close button
let closeButton = document.createElement("div");
closeButton.innerText = "Close Popup";
closeButton.classList = "smallLink";
closeButton.setAttribute("align", "center");
closeButton.addEventListener("click", closeInfoMenu);
//add the close button
popup.prepend(closeButton);
let parentNodes = document.querySelectorAll("#secondary");
let parentNode = null;
for (let i = 0; i < parentNodes.length; i++) {
if (parentNodes[i].firstElementChild !== null) {
parentNode = parentNodes[i];
}
}
2019-08-20 01:21:19 +02:00
if (parentNode == null) {
//old youtube theme
parentNode = document.getElementById("watch7-sidebar-contents");
}
2019-08-20 01:21:19 +02:00
//make the logo source not 404
//query selector must be used since getElementByID doesn't work on a node and this isn't added to the document yet
let logo = popup.querySelector("#sponsorBlockPopupLogo");
logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
2019-08-20 01:21:19 +02:00
//remove the style sheet and font that are not necessary
popup.querySelector("#sponorBlockPopupFont").remove();
popup.querySelector("#sponorBlockStyleSheet").remove();
2019-08-20 01:21:19 +02:00
parentNode.insertBefore(popup, parentNode.firstChild);
2019-08-20 01:21:19 +02:00
//run the popup init script
runThePopup();
}
});
}
function closeInfoMenu() {
2019-08-20 01:21:19 +02:00
let popup = document.getElementById("sponsorBlockPopupContainer");
if (popup != null) {
popup.remove();
//show info button if it's not an embed
if (!document.URL.includes("/embed/")) {
document.getElementById("infoButton").style.display = "unset";
}
2019-08-20 01:21:19 +02:00
}
}
2019-07-30 02:54:10 +02:00
function clearSponsorTimes() {
2019-08-20 01:21:19 +02:00
//it can't update to this info yet
closeInfoMenu();
2019-07-30 02:54:10 +02:00
let currentVideoID = sponsorVideoID;
2019-07-30 02:54:10 +02:00
2020-01-01 20:10:34 +01:00
let sponsorTimes = SB.config.sponsorTimes.get(currentVideoID);
2019-07-30 02:54:10 +02:00
2019-12-31 21:56:30 +01:00
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
let confirmMessage = chrome.i18n.getMessage("clearThis") + getSponsorTimesMessage(sponsorTimes);
confirmMessage += chrome.i18n.getMessage("confirmMSG")
if(!confirm(confirmMessage)) return;
2019-07-30 02:54:10 +02:00
2019-12-31 21:56:30 +01:00
//clear the sponsor times
2020-01-01 20:10:34 +01:00
SB.config.sponsorTimes.delete(currentVideoID);
2019-07-30 02:54:10 +02:00
2019-12-31 21:56:30 +01:00
//clear sponsor times submitting
sponsorTimesSubmitting = [];
2019-12-31 21:56:30 +01:00
updatePreviewBar();
2019-12-31 21:56:30 +01:00
//set buttons to be correct
changeStartSponsorButton(true, false);
}
2019-07-30 02:54:10 +02:00
}
//if skipNotice is null, it will not affect the UI
function vote(type, UUID, skipNotice) {
2019-08-20 01:21:19 +02:00
if (skipNotice != null) {
//add loading info
skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...")
skipNotice.resetNoticeInfoMessage.bind(skipNotice)();
}
2019-08-20 01:21:19 +02:00
let sponsorIndex = UUIDs.indexOf(UUID);
2019-12-29 06:08:57 +01:00
// See if the local time saved count and skip count should be saved
if (type == 0 && sponsorSkipped[sponsorIndex] || type == 1 && !sponsorSkipped[sponsorIndex]) {
let factor = 1;
if (type == 0) {
factor = -1;
2019-12-29 06:10:08 +01:00
sponsorSkipped[sponsorIndex] = false;
}
2019-12-31 21:56:30 +01:00
// Count this as a skip
SB.config.minutesSaved = SB.config.minutesSaved + factor * (sponsorTimes[sponsorIndex][1] - sponsorTimes[sponsorIndex][0]) / 60;
2019-12-31 23:50:52 +01:00
SB.config.skipCount = 0;
2019-12-31 21:56:30 +01:00
SB.config.skipCount = SB.config.skipCount + factor * 1;
}
2019-08-20 01:21:19 +02:00
chrome.runtime.sendMessage({
message: "submitVote",
type: type,
UUID: UUID
}, function(response) {
if (response != undefined) {
//see if it was a success or failure
if (skipNotice != null) {
2019-09-24 23:55:07 +02:00
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
//success (treat rate limits as a success)
2019-08-20 01:21:19 +02:00
if (type == 0) {
skipNotice.afterDownvote.bind(skipNotice)();
}
} else if (response.successType == 0) {
//failure: duplicate vote
skipNotice.addNoticeInfoMessage.bind(skipNotice)(chrome.i18n.getMessage("voteFail"))
skipNotice.resetVoteButtonInfo.bind(skipNotice)();
} else if (response.successType == -1) {
skipNotice.addNoticeInfoMessage.bind(skipNotice)(getErrorMessage(response.statusCode))
skipNotice.resetVoteButtonInfo.bind(skipNotice)();
2019-08-20 01:21:19 +02:00
}
}
}
});
}
//Closes all notices that tell the user that a sponsor was just skipped
function closeAllSkipNotices(){
2019-08-20 01:21:19 +02:00
let notices = document.getElementsByClassName("sponsorSkipNotice");
for (let i = 0; i < notices.length; i++) {
notices[i].remove();
}
}
function dontShowNoticeAgain() {
2019-12-31 21:56:30 +01:00
SB.config.dontShowNotice = true;
2019-08-20 01:21:19 +02:00
closeAllSkipNotices();
}
function sponsorMessageStarted(callback) {
2019-09-25 00:21:47 +02:00
v = document.querySelector('video');
2019-09-25 00:21:47 +02:00
//send back current time
callback({
time: v.currentTime
})
2019-09-25 00:21:47 +02:00
//update button
toggleStartSponsorButton();
}
function submitSponsorTimes() {
2019-08-20 01:21:19 +02:00
if (document.getElementById("submitButton").style.display == "none") {
//don't submit, not ready
return;
}
2019-08-20 01:21:19 +02:00
//it can't update to this info yet
closeInfoMenu();
let currentVideoID = sponsorVideoID;
2020-01-08 04:59:50 +01:00
let sponsorTimes = SB.config.sponsorTimes.get(currentVideoID);
2020-01-08 04:59:50 +01:00
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
//check if a sponsor exceeds the duration of the video
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i][1] > v.duration) {
sponsorTimes[i][1] = v.duration;
}
2020-01-08 04:59:50 +01:00
}
//update sponsorTimes
SB.config.sponsorTimes.set(currentVideoID, sponsorTimes);
2020-01-08 04:59:50 +01:00
//update sponsorTimesSubmitting
sponsorTimesSubmitting = sponsorTimes;
2020-01-08 04:59:50 +01:00
let confirmMessage = chrome.i18n.getMessage("submitCheck") + "\n\n" + getSponsorTimesMessage(sponsorTimes)
+ "\n\n" + chrome.i18n.getMessage("confirmMSG") + "\n\n" + chrome.i18n.getMessage("guildlinesSummary");
if(!confirm(confirmMessage)) return;
2020-01-08 04:59:50 +01:00
sendSubmitMessage();
}
}
//send the message to the background js
//called after all the checks have been made that it's okay to do so
function sendSubmitMessage(){
2019-08-20 01:21:19 +02:00
//add loading animation
2019-08-24 18:29:51 +02:00
document.getElementById("submitImage").src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
2019-08-20 01:21:19 +02:00
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
let currentVideoID = sponsorVideoID;
2019-08-20 01:21:19 +02:00
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
let animationEndListener = function() {
changeStartSponsorButton(true, false);
submitButton.style.animation = "none";
submitButton.removeEventListener("animationend", animationEndListener);
};
submitButton.addEventListener("animationend", animationEndListener);
//clear the sponsor times
2020-01-01 20:10:34 +01:00
SB.config.sponsorTimes.delete(currentVideoID);
2019-08-20 01:21:19 +02:00
//add submissions to current sponsors list
sponsorTimes = sponsorTimes.concat(sponsorTimesSubmitting);
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
// Add some random IDs
UUIDs.push(generateUserID());
}
// Empty the submitting times
sponsorTimesSubmitting = [];
updatePreviewBar();
2019-08-20 01:21:19 +02:00
} else {
//show that the upload failed
document.getElementById("submitButton").style.animation = "unset";
2019-08-24 18:29:51 +02:00
document.getElementById("submitImage").src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png");
2019-08-20 01:21:19 +02:00
alert(getErrorMessage(response.statusCode));
2019-08-20 01:21:19 +02:00
}
}
2019-08-20 01:21:19 +02:00
});
}
//get the message that visually displays the video times
function getSponsorTimesMessage(sponsorTimes) {
2019-08-20 01:21:19 +02:00
let sponsorTimesMessage = "";
for (let i = 0; i < sponsorTimes.length; i++) {
for (let s = 0; s < sponsorTimes[i].length; s++) {
let timeMessage = getFormattedTime(sponsorTimes[i][s]);
//if this is an end time
if (s == 1) {
timeMessage = " to " + timeMessage;
} else if (i > 0) {
//add commas if necessary
timeMessage = ", " + timeMessage;
}
sponsorTimesMessage += timeMessage;
}
}
2019-08-20 01:21:19 +02:00
return sponsorTimesMessage;
}
//converts time in seconds to minutes:seconds
function getFormattedTime(seconds) {
2019-08-20 01:21:19 +02:00
let minutes = Math.floor(seconds / 60);
let secondsDisplay = Math.round(seconds - minutes * 60);
if (secondsDisplay < 10) {
//add a zero
secondsDisplay = "0" + secondsDisplay;
}
2019-08-20 01:21:19 +02:00
let formatted = minutes+ ":" + secondsDisplay;
2019-08-20 01:21:19 +02:00
return formatted;
}
function sendRequestToServer(type, address, callback) {
2019-08-20 01:21:19 +02:00
let xmlhttp = new XMLHttpRequest();
2019-08-20 01:21:19 +02:00
xmlhttp.open(type, serverAddress + address, true);
2019-08-20 01:21:19 +02:00
if (callback != undefined) {
xmlhttp.onreadystatechange = function () {
callback(xmlhttp, false);
};
2019-08-20 01:21:19 +02:00
xmlhttp.onerror = function(ev) {
callback(xmlhttp, true);
};
}
2019-08-20 01:21:19 +02:00
//submit this request
xmlhttp.send();
}
function sendRequestToCustomServer(type, fullAddress, callback) {
2019-08-20 01:21:19 +02:00
let xmlhttp = new XMLHttpRequest();
2019-08-20 01:21:19 +02:00
xmlhttp.open(type, fullAddress, true);
2019-08-20 01:21:19 +02:00
if (callback != undefined) {
xmlhttp.onreadystatechange = function () {
callback(xmlhttp, false);
};
2019-08-20 01:21:19 +02:00
xmlhttp.onerror = function(ev) {
callback(xmlhttp, true);
};
}
2019-08-20 01:21:19 +02:00
//submit this request
xmlhttp.send();
}