mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-07 12:07:11 +03:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb3c67629 | ||
|
|
622b2ea158 | ||
|
|
7a9af6cbcf | ||
|
|
51a389284d | ||
|
|
2ff70f1511 | ||
|
|
ff9badf2e9 | ||
|
|
aefc880486 | ||
|
|
e749456719 | ||
|
|
a2312a10be | ||
|
|
b0090a3334 | ||
|
|
7100831956 | ||
|
|
30c1b2d919 | ||
|
|
3ec6caee9d | ||
|
|
7f505a52ca | ||
|
|
84b8e59b39 | ||
|
|
808dfe42ce | ||
|
|
600365d998 | ||
|
|
569699cbc1 | ||
|
|
08cb324125 | ||
|
|
504f0b0ede | ||
|
|
00877fb087 | ||
|
|
d654d8934b | ||
|
|
97e202dbd0 | ||
|
|
64be41b008 | ||
|
|
b3684a8730 | ||
|
|
45f0d65f69 | ||
|
|
5436a1651e | ||
|
|
33fe63084b | ||
|
|
8f47513c98 | ||
|
|
7a17cc2b0a | ||
|
|
c7cb845d8a | ||
|
|
250c75a78f | ||
|
|
a86e55ac67 | ||
|
|
30f2f638fe | ||
|
|
725ab783e3 | ||
|
|
30e3222177 |
@@ -3,34 +3,40 @@ var previousVideoID = null
|
|||||||
//the id of this user, randomly generated once per install
|
//the id of this user, randomly generated once per install
|
||||||
var userID = null;
|
var userID = null;
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener( // On tab update
|
//when a new tab is highlighted
|
||||||
function(tabId, changeInfo, tab) {
|
chrome.tabs.onActivated.addListener(
|
||||||
if (changeInfo != undefined && changeInfo.url != undefined) {
|
function(activeInfo) {
|
||||||
let id = getYouTubeVideoID(changeInfo.url);
|
chrome.tabs.get(activeInfo.tabId, function(tab) {
|
||||||
if (changeInfo.url && id) { // If URL changed and is youtube video message contentScript the video id
|
let id = getYouTubeVideoID(tab.url);
|
||||||
videoIDChange(id);
|
|
||||||
|
|
||||||
chrome.tabs.sendMessage( tabId, {
|
//if this even is a YouTube tab
|
||||||
message: 'ytvideoid',
|
if (id) {
|
||||||
id: id
|
videoIDChange(id, activeInfo.tabId);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//when a tab changes URLs
|
||||||
|
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
|
||||||
|
if (changeInfo != undefined && changeInfo.url != undefined) {
|
||||||
|
let id = getYouTubeVideoID(changeInfo.url);
|
||||||
|
|
||||||
|
//if URL changed and is youtube video message contentScript the video id
|
||||||
|
if (changeInfo.url && id) {
|
||||||
|
videoIDChange(id, tabId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
||||||
if (request.message == "submitTimes") {
|
if (request.message == "submitTimes") {
|
||||||
submitTimes(request.videoID, callback);
|
submitTimes(request.videoID, callback);
|
||||||
|
|
||||||
//this allows the callback to be called later by the submitTimes function
|
//this allows the callback to be called later by the submitTimes function
|
||||||
return true;
|
return true;
|
||||||
} else if (request.message == "ytvideoid") {
|
|
||||||
if (previousVideoID != request.videoID) {
|
|
||||||
videoIDChange(request.videoID);
|
|
||||||
}
|
|
||||||
} else if (request.message == "addSponsorTime") {
|
} else if (request.message == "addSponsorTime") {
|
||||||
addSponsorTime(request.time);
|
addSponsorTime(request.time, request.videoID);
|
||||||
} else if (request.message == "getSponsorTimes") {
|
} else if (request.message == "getSponsorTimes") {
|
||||||
getSponsorTimes(request.videoID, function(sponsorTimes) {
|
getSponsorTimes(request.videoID, function(sponsorTimes) {
|
||||||
callback({
|
callback({
|
||||||
@@ -63,22 +69,22 @@ function getSponsorTimes(videoID, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSponsorTime(time) {
|
function addSponsorTime(time, videoID) {
|
||||||
getSponsorTimes(previousVideoID, function(sponsorTimes) {
|
getSponsorTimes(videoID, function(sponsorTimes) {
|
||||||
//add to sponsorTimes
|
//add to sponsorTimes
|
||||||
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
|
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
|
||||||
//it is an end time
|
//it is an end time
|
||||||
sponsorTimes[sponsorTimes.length - 1][1] = parseInt(time);
|
sponsorTimes[sponsorTimes.length - 1][1] = time;
|
||||||
} else {
|
} else {
|
||||||
//it is a start time
|
//it is a start time
|
||||||
let sponsorTimesIndex = sponsorTimes.length;
|
let sponsorTimesIndex = sponsorTimes.length;
|
||||||
sponsorTimes[sponsorTimesIndex] = [];
|
sponsorTimes[sponsorTimesIndex] = [];
|
||||||
|
|
||||||
sponsorTimes[sponsorTimesIndex][0] = parseInt(time);
|
sponsorTimes[sponsorTimesIndex][0] = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
//save this info
|
//save this info
|
||||||
let sponsorTimeKey = "sponsorTimes" + previousVideoID;
|
let sponsorTimeKey = "sponsorTimes" + videoID;
|
||||||
chrome.storage.sync.set({[sponsorTimeKey]: sponsorTimes});
|
chrome.storage.sync.set({[sponsorTimeKey]: sponsorTimes});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -147,7 +153,13 @@ function submitTimes(videoID, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoIDChange(currentVideoID) {
|
function videoIDChange(currentVideoID, tabId) {
|
||||||
|
//send a message to the content script
|
||||||
|
chrome.tabs.sendMessage(tabId, {
|
||||||
|
message: 'ytvideoid',
|
||||||
|
id: currentVideoID
|
||||||
|
});
|
||||||
|
|
||||||
//warn them if they had unsubmitted times
|
//warn them if they had unsubmitted times
|
||||||
if (previousVideoID != null) {
|
if (previousVideoID != null) {
|
||||||
//get the sponsor times from storage
|
//get the sponsor times from storage
|
||||||
@@ -186,6 +198,12 @@ function getUserID(callback) {
|
|||||||
userID = userIDStorage;
|
userID = userIDStorage;
|
||||||
callback(userID);
|
callback(userID);
|
||||||
} else {
|
} else {
|
||||||
|
//double check if a UUID hasn't been created since this was first called
|
||||||
|
if (userID != null) {
|
||||||
|
callback(userID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//generate a userID
|
//generate a userID
|
||||||
userID = generateUUID();
|
userID = generateUUID();
|
||||||
|
|
||||||
|
|||||||
166
content.js
166
content.js
@@ -1,28 +1,24 @@
|
|||||||
if(id = getYouTubeVideoID(document.URL)){ // Direct Links
|
|
||||||
videoIDChange(id);
|
|
||||||
|
|
||||||
//tell background.js about this
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
message: "ytvideoid",
|
|
||||||
videoID: id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//was sponsor data found when doing SponsorsLookup
|
//was sponsor data found when doing SponsorsLookup
|
||||||
var sponsorDataFound = false;
|
var sponsorDataFound = false;
|
||||||
|
|
||||||
//the actual sponsorTimes if loaded and UUIDs associated with them
|
//the actual sponsorTimes if loaded and UUIDs associated with them
|
||||||
var sponsorTimes = undefined;
|
var sponsorTimes = null;
|
||||||
var UUIDs = undefined;
|
var UUIDs = null;
|
||||||
|
//what video id are these sponsors for
|
||||||
|
var sponsorVideoID = null;
|
||||||
|
|
||||||
|
if(id = getYouTubeVideoID(document.URL)){ // Direct Links
|
||||||
|
videoIDChange(id);
|
||||||
|
}
|
||||||
|
|
||||||
//the video
|
//the video
|
||||||
var v;
|
var v;
|
||||||
|
|
||||||
//the last time looked at (used to see if this time is in the interval)
|
//the last time looked at (used to see if this time is in the interval)
|
||||||
var lastTime;
|
var lastTime = -1;
|
||||||
|
|
||||||
//the last time skipped to
|
//the actual time (not video time) that the last skip happened
|
||||||
var lastTimeSkippedTo = -1;
|
var lastUnixTimeSkipped = -1;
|
||||||
|
|
||||||
//the last time in the video a sponsor was skipped
|
//the last time in the video a sponsor was skipped
|
||||||
//used for the go back button
|
//used for the go back button
|
||||||
@@ -59,6 +55,7 @@ chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) {
|
|||||||
|
|
||||||
chrome.runtime.onMessage.addListener( // Detect URL Changes
|
chrome.runtime.onMessage.addListener( // Detect URL Changes
|
||||||
function(request, sender, sendResponse) {
|
function(request, sender, sendResponse) {
|
||||||
|
console.log(request.message)
|
||||||
//message from background script
|
//message from background script
|
||||||
if (request.message == "ytvideoid") {
|
if (request.message == "ytvideoid") {
|
||||||
videoIDChange(request.id);
|
videoIDChange(request.id);
|
||||||
@@ -103,7 +100,40 @@ chrome.runtime.onMessage.addListener( // Detect URL Changes
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//check for hotkey pressed
|
||||||
|
document.onkeydown = function(e){
|
||||||
|
e = e || window.event;
|
||||||
|
var key = e.which || e.keyCode;
|
||||||
|
|
||||||
|
let video = document.getElementById("movie_player");
|
||||||
|
|
||||||
|
//is the video in focus, otherwise they could be typing a comment
|
||||||
|
if (document.activeElement === video) {
|
||||||
|
if(key == 186){
|
||||||
|
//semicolon
|
||||||
|
startSponsorClicked();
|
||||||
|
} else if (key == 222) {
|
||||||
|
//single quote
|
||||||
|
submitSponsorTimes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function videoIDChange(id) {
|
function videoIDChange(id) {
|
||||||
|
//not a url change
|
||||||
|
if (sponsorVideoID == id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset last sponsor times
|
||||||
|
lastTime = -1;
|
||||||
|
lastUnixTimeSkipped = -1;
|
||||||
|
|
||||||
|
//reset sponsor times
|
||||||
|
sponsorTimes = null;
|
||||||
|
UUIDs = null;
|
||||||
|
sponsorVideoID = id;
|
||||||
|
|
||||||
//reset sponsor data found check
|
//reset sponsor data found check
|
||||||
sponsorDataFound = false;
|
sponsorDataFound = false;
|
||||||
sponsorsLookup(id);
|
sponsorsLookup(id);
|
||||||
@@ -115,10 +145,13 @@ function videoIDChange(id) {
|
|||||||
}, function(response) {
|
}, function(response) {
|
||||||
if (response != undefined) {
|
if (response != undefined) {
|
||||||
let sponsorTimes = response.sponsorTimes;
|
let sponsorTimes = response.sponsorTimes;
|
||||||
if (sponsorTimes != undefined && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length >= 2) {
|
if (sponsorTimes != null && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length >= 2) {
|
||||||
document.getElementById("submitButton").style.display = "unset";
|
changeStartSponsorButton(true, true);
|
||||||
} else if (sponsorTimes != undefined && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
|
} else if (sponsorTimes != null && sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
|
||||||
toggleStartSponsorButton();
|
changeStartSponsorButton(false, true);
|
||||||
|
} else {
|
||||||
|
changeStartSponsorButton(true, true);
|
||||||
|
document.getElementById("submitButton").style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,16 +203,22 @@ function sponsorsLookup(id) {
|
|||||||
function sponsorCheck(sponsorTimes) { // Video skipping
|
function sponsorCheck(sponsorTimes) { // Video skipping
|
||||||
//see if any sponsor start time was just passed
|
//see if any sponsor start time was just passed
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
//the sponsor time is in between these times, skip it
|
//this means part of the video was just skipped
|
||||||
//if the time difference is more than 1 second, than the there was probably a skip in time,
|
if (Math.abs(v.currentTime - lastTime) > 1 && lastTime != -1) {
|
||||||
// and it's not due to playback
|
//make lastTime as if the video was playing normally
|
||||||
//also check if the last time skipped to is not too close to now, to make sure not to get too many
|
lastTime = v.currentTime - 0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentTime = Date.now();
|
||||||
|
|
||||||
|
//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)
|
// sponsor times in a row (from one troll)
|
||||||
if (Math.abs(v.currentTime - lastTime) < 1 && sponsorTimes[i][0] >= lastTime && sponsorTimes[i][0] <= v.currentTime &&
|
//the last term makes 0 second start times possible
|
||||||
(lastTimeSkippedTo == -1 || Math.abs(v.currentTime - lastTimeSkippedTo) > 1)) {
|
if ((Math.abs(v.currentTime - sponsorTimes[i][0]) < 0.3 && sponsorTimes[i][0] >= lastTime && sponsorTimes[i][0] <= v.currentTime
|
||||||
|
&& (lastUnixTimeSkipped == -1 || currentTime - lastUnixTimeSkipped > 500)) || (lastTime == -1 && sponsorTimes[i][0] == 0)) {
|
||||||
//skip it
|
//skip it
|
||||||
v.currentTime = sponsorTimes[i][1];
|
v.currentTime = sponsorTimes[i][1];
|
||||||
lastTimeSkippedTo = sponsorTimes[i][1];
|
|
||||||
|
|
||||||
lastSponsorTimeSkipped = sponsorTimes[i][0];
|
lastSponsorTimeSkipped = sponsorTimes[i][0];
|
||||||
|
|
||||||
@@ -187,7 +226,7 @@ function sponsorCheck(sponsorTimes) { // Video skipping
|
|||||||
lastSponsorTimeSkippedUUID = currentUUID;
|
lastSponsorTimeSkippedUUID = currentUUID;
|
||||||
|
|
||||||
//send out the message saying that a sponsor message was skipped
|
//send out the message saying that a sponsor message was skipped
|
||||||
openSkipNotice();
|
openSkipNotice(currentUUID);
|
||||||
|
|
||||||
setTimeout(() => closeSkipNotice(currentUUID), 7000);
|
setTimeout(() => closeSkipNotice(currentUUID), 7000);
|
||||||
|
|
||||||
@@ -197,11 +236,15 @@ function sponsorCheck(sponsorTimes) { // Video skipping
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTime = v.currentTime;
|
|
||||||
|
//don't keep track until they are loaded in
|
||||||
|
if (sponsorTimes.length > 0) {
|
||||||
|
lastTime = v.currentTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goBackToPreviousTime(UUID) {
|
function goBackToPreviousTime(UUID) {
|
||||||
if (sponsorTimes != undefined) {
|
if (sponsorTimes != null) {
|
||||||
//add a tiny bit of time to make sure it is not skipped again
|
//add a tiny bit of time to make sure it is not skipped again
|
||||||
v.currentTime = sponsorTimes[UUIDs.indexOf(UUID)][0] + 0.001;
|
v.currentTime = sponsorTimes[UUIDs.indexOf(UUID)][0] + 0.001;
|
||||||
|
|
||||||
@@ -242,11 +285,10 @@ function removePlayerControlsButton() {
|
|||||||
|
|
||||||
//adds or removes the player controls button to what it should be
|
//adds or removes the player controls button to what it should be
|
||||||
function updateVisibilityOfPlayerControlsButton() {
|
function updateVisibilityOfPlayerControlsButton() {
|
||||||
|
addPlayerControlsButton();
|
||||||
|
addSubmitButton();
|
||||||
if (hideVideoPlayerControls) {
|
if (hideVideoPlayerControls) {
|
||||||
removePlayerControlsButton();
|
removePlayerControlsButton();
|
||||||
} else {
|
|
||||||
addPlayerControlsButton();
|
|
||||||
addSubmitButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +298,8 @@ function startSponsorClicked() {
|
|||||||
//send back current time with message
|
//send back current time with message
|
||||||
chrome.runtime.sendMessage({
|
chrome.runtime.sendMessage({
|
||||||
message: "addSponsorTime",
|
message: "addSponsorTime",
|
||||||
time: v.currentTime
|
time: v.currentTime,
|
||||||
|
videoID: getYouTubeVideoID(document.URL)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +307,7 @@ function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
|
|||||||
if (showStartSponsor) {
|
if (showStartSponsor) {
|
||||||
showingStartSponsor = true;
|
showingStartSponsor = true;
|
||||||
document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
|
document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
|
||||||
|
document.getElementById("startSponsorButton").setAttribute("title", "Sponsor Starts Now");
|
||||||
|
|
||||||
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible) {
|
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible) {
|
||||||
document.getElementById("submitButton").style.display = "unset";
|
document.getElementById("submitButton").style.display = "unset";
|
||||||
@@ -274,6 +318,7 @@ function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
|
|||||||
} else {
|
} else {
|
||||||
showingStartSponsor = false;
|
showingStartSponsor = false;
|
||||||
document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
|
document.getElementById("startSponsorImage").src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
|
||||||
|
document.getElementById("startSponsorButton").setAttribute("title", "Sponsor Ends Now");
|
||||||
|
|
||||||
//disable submit button
|
//disable submit button
|
||||||
document.getElementById("submitButton").style.display = "none";
|
document.getElementById("submitButton").style.display = "none";
|
||||||
@@ -313,12 +358,35 @@ function addSubmitButton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Opens the notice that tells the user that a sponsor was just skipped
|
//Opens the notice that tells the user that a sponsor was just skipped
|
||||||
function openSkipNotice(){
|
function openSkipNotice(UUID){
|
||||||
if (dontShowNotice) {
|
if (dontShowNotice) {
|
||||||
//don't show, return
|
//don't show, return
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check if page is loaded yet (for 0 second sponsors, the page might not be loaded yet)
|
||||||
|
//it looks for the view count div and sees if it is full yet
|
||||||
|
//querySelectorAll is being used like findElementById for multiple objects, because for
|
||||||
|
//some reason YouTube has put more than one object with one ID.
|
||||||
|
let viewCountNode = document.querySelectorAll("#count");
|
||||||
|
//check to see if the length is over zero, otherwise it's a different YouTube theme probably
|
||||||
|
if (viewCountNode.length > 0) {
|
||||||
|
//check if any of these have text
|
||||||
|
let viewCountVisible = false;
|
||||||
|
for (let i = 0; i < viewCountNode.length; i++) {
|
||||||
|
if (viewCountNode[i].innerText != null) {
|
||||||
|
viewCountVisible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!viewCountVisible) {
|
||||||
|
//this is the new YouTube layout and it is still loading
|
||||||
|
//wait a bit for opening the notice
|
||||||
|
setTimeout(() => openSkipNotice(UUID), 200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
|
||||||
|
|
||||||
if (amountOfPreviousNotices > 0) {
|
if (amountOfPreviousNotices > 0) {
|
||||||
@@ -328,45 +396,43 @@ function openSkipNotice(){
|
|||||||
previousNotice.classList.add("secondSkipNotice")
|
previousNotice.classList.add("secondSkipNotice")
|
||||||
}
|
}
|
||||||
|
|
||||||
let UUID = lastSponsorTimeSkippedUUID;
|
|
||||||
|
|
||||||
let noticeElement = document.createElement("div");
|
let noticeElement = document.createElement("div");
|
||||||
//what sponsor time this is about
|
//what sponsor time this is about
|
||||||
noticeElement.id = "sponsorSkipNotice" + lastSponsorTimeSkippedUUID;
|
noticeElement.id = "sponsorSkipNotice" + UUID;
|
||||||
noticeElement.classList.add("sponsorSkipObject");
|
noticeElement.classList.add("sponsorSkipObject");
|
||||||
noticeElement.classList.add("sponsorSkipNotice");
|
noticeElement.classList.add("sponsorSkipNotice");
|
||||||
noticeElement.style.zIndex = 1 + amountOfPreviousNotices;
|
noticeElement.style.zIndex = 5 + amountOfPreviousNotices;
|
||||||
|
|
||||||
let logoElement = document.createElement("img");
|
let logoElement = document.createElement("img");
|
||||||
logoElement.id = "sponsorSkipLogo" + lastSponsorTimeSkippedUUID;
|
logoElement.id = "sponsorSkipLogo" + UUID;
|
||||||
logoElement.className = "sponsorSkipLogo";
|
logoElement.className = "sponsorSkipLogo";
|
||||||
logoElement.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
|
logoElement.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
|
||||||
|
|
||||||
let noticeMessage = document.createElement("div");
|
let noticeMessage = document.createElement("div");
|
||||||
noticeMessage.id = "sponsorSkipMessage" + lastSponsorTimeSkippedUUID;
|
noticeMessage.id = "sponsorSkipMessage" + UUID;
|
||||||
noticeMessage.classList.add("sponsorSkipMessage");
|
noticeMessage.classList.add("sponsorSkipMessage");
|
||||||
noticeMessage.classList.add("sponsorSkipObject");
|
noticeMessage.classList.add("sponsorSkipObject");
|
||||||
noticeMessage.innerText = "Hey, you just skipped a sponsor!";
|
noticeMessage.innerText = "Hey, you just skipped a sponsor!";
|
||||||
|
|
||||||
let noticeInfo = document.createElement("p");
|
let noticeInfo = document.createElement("p");
|
||||||
noticeInfo.id = "sponsorSkipInfo" + lastSponsorTimeSkippedUUID;
|
noticeInfo.id = "sponsorSkipInfo" + UUID;
|
||||||
noticeInfo.classList.add("sponsorSkipInfo");
|
noticeInfo.classList.add("sponsorSkipInfo");
|
||||||
noticeInfo.classList.add("sponsorSkipObject");
|
noticeInfo.classList.add("sponsorSkipObject");
|
||||||
noticeInfo.innerText = "This message will disapear in 7 seconds";
|
noticeInfo.innerText = "This message will disapear in 7 seconds";
|
||||||
|
|
||||||
//thumbs up and down buttons
|
//thumbs up and down buttons
|
||||||
let voteButtonsContainer = document.createElement("div");
|
let voteButtonsContainer = document.createElement("div");
|
||||||
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + lastSponsorTimeSkippedUUID;
|
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
|
||||||
voteButtonsContainer.setAttribute("align", "center");
|
voteButtonsContainer.setAttribute("align", "center");
|
||||||
|
|
||||||
let upvoteButton = document.createElement("img");
|
let upvoteButton = document.createElement("img");
|
||||||
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + lastSponsorTimeSkippedUUID;
|
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
|
||||||
upvoteButton.className = "sponsorSkipObject voteButton";
|
upvoteButton.className = "sponsorSkipObject voteButton";
|
||||||
upvoteButton.src = chrome.extension.getURL("icons/upvote.png");
|
upvoteButton.src = chrome.extension.getURL("icons/upvote.png");
|
||||||
upvoteButton.addEventListener("click", () => vote(1, UUID));
|
upvoteButton.addEventListener("click", () => vote(1, UUID));
|
||||||
|
|
||||||
let downvoteButton = document.createElement("img");
|
let downvoteButton = document.createElement("img");
|
||||||
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + lastSponsorTimeSkippedUUID;
|
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
|
||||||
downvoteButton.className = "sponsorSkipObject voteButton";
|
downvoteButton.className = "sponsorSkipObject voteButton";
|
||||||
downvoteButton.src = chrome.extension.getURL("icons/downvote.png");
|
downvoteButton.src = chrome.extension.getURL("icons/downvote.png");
|
||||||
downvoteButton.addEventListener("click", () => vote(0, UUID));
|
downvoteButton.addEventListener("click", () => vote(0, UUID));
|
||||||
@@ -541,6 +607,13 @@ function sponsorMessageStarted() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function submitSponsorTimes() {
|
function submitSponsorTimes() {
|
||||||
|
if(!confirm("Are you sure you want to submit this?")) return;
|
||||||
|
|
||||||
|
if (document.getElementById("submitButton").style.display == "none") {
|
||||||
|
//don't submit, not ready
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//add loading animation
|
//add loading animation
|
||||||
document.getElementById("submitButtonImage").src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
|
document.getElementById("submitButtonImage").src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
|
||||||
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
|
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
|
||||||
@@ -617,5 +690,6 @@ function sendRequestToCustomServer(type, fullAddress, callback) {
|
|||||||
function getYouTubeVideoID(url) { // Returns with video id else returns false
|
function getYouTubeVideoID(url) { // Returns with video id else returns false
|
||||||
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
|
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
|
||||||
var match = url.match(regExp);
|
var match = url.match(regExp);
|
||||||
return (match && match[7].length == 11) ? match[7] : false;
|
var id = new URL(url).searchParams.get("v");
|
||||||
}
|
return (match && match[7].length == 11) ? id : false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "SponsorBlock - YouTube Sponsorship Blocker",
|
"name": "SponsorBlock - YouTube Sponsorship Blocker",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "1.0.2",
|
"version": "1.0.12",
|
||||||
"description": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
|
"description": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "SponsorBlock - YouTube Sponsorship Blocker",
|
"name": "SponsorBlock - YouTube Sponsorship Blocker",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "1.0.2",
|
"version": "1.0.13",
|
||||||
"description": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
|
"description": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,7 +71,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<button id="sponsorStart" class="greenButton">Sponsorship Starts Now</button>
|
<button id="sponsorStart" class="greenButton">Sponsorship Starts Now</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<sub>Hint: Press the semicolon key while focused on a video report the start/end of a sponsor and quote to submit.</sub>
|
||||||
|
|
||||||
<div id="submissionSection" style="display: none">
|
<div id="submissionSection" style="display: none">
|
||||||
<h3>Latest Sponsor Message Times Chosen</h3>
|
<h3>Latest Sponsor Message Times Chosen</h3>
|
||||||
<b>
|
<b>
|
||||||
|
|||||||
106
popup.js
106
popup.js
@@ -1,14 +1,37 @@
|
|||||||
|
|
||||||
|
// References
|
||||||
|
var SB = {};
|
||||||
|
|
||||||
|
SB.sponsorStart = document.getElementById("sponsorStart");
|
||||||
|
SB.clearTimes = document.getElementById("clearTimes");
|
||||||
|
SB.submitTimes = document.getElementById("submitTimes");
|
||||||
|
SB.showNoticeAgain = document.getElementById("showNoticeAgain");
|
||||||
|
SB.hideVideoPlayerControls = document.getElementById("hideVideoPlayerControls");
|
||||||
|
SB.showVideoPlayerControls = document.getElementById("showVideoPlayerControls");
|
||||||
|
SB.disableSponsorViewTracking = document.getElementById("disableSponsorViewTracking");
|
||||||
|
SB.enableSponsorViewTracking = document.getElementById("enableSponsorViewTracking");
|
||||||
|
SB.optionsButton = document.getElementById("optionsButton");
|
||||||
|
SB.reportAnIssue = document.getElementById("reportAnIssue");
|
||||||
|
// sponsorTimesContributions
|
||||||
|
SB.sponsorTimesContributionsContainer = document.getElementById("sponsorTimesContributionsContainer");
|
||||||
|
SB.sponsorTimesContributionsDisplay = document.getElementById("sponsorTimesContributionsDisplay");
|
||||||
|
SB.sponsorTimesContributionsDisplayEndWord = document.getElementById("sponsorTimesContributionsDisplayEndWord");
|
||||||
|
// sponsorTimesViewsDisplay
|
||||||
|
SB.sponsorTimesViewsContainer = document.getElementById("sponsorTimesViewsDisplayContainer");
|
||||||
|
SB.sponsorTimesViewsDisplay = document.getElementById("sponsorTimesViewsDisplayDisplay");
|
||||||
|
SB.sponsorTimesViewsDisplayEndWord = document.getElementById("sponsorTimesViewsDisplayDisplayEndWord");
|
||||||
|
|
||||||
//setup click listeners
|
//setup click listeners
|
||||||
document.getElementById("sponsorStart").addEventListener("click", sendSponsorStartMessage);
|
SB.sponsorStart.addEventListener("click", sendSponsorStartMessage);
|
||||||
document.getElementById("clearTimes").addEventListener("click", clearTimes);
|
SB.clearTimes.addEventListener("click", clearTimes);
|
||||||
document.getElementById("submitTimes").addEventListener("click", submitTimes);
|
SB.submitTimes.addEventListener("click", submitTimes);
|
||||||
document.getElementById("showNoticeAgain").addEventListener("click", showNoticeAgain);
|
SB.showNoticeAgain.addEventListener("click", showNoticeAgain);
|
||||||
document.getElementById("hideVideoPlayerControls").addEventListener("click", hideVideoPlayerControls);
|
SB.hideVideoPlayerControls.addEventListener("click", hideVideoPlayerControls);
|
||||||
document.getElementById("showVideoPlayerControls").addEventListener("click", showVideoPlayerControls);
|
SB.showVideoPlayerControls.addEventListener("click", showVideoPlayerControls);
|
||||||
document.getElementById("disableSponsorViewTracking").addEventListener("click", disableSponsorViewTracking);
|
SB.disableSponsorViewTracking.addEventListener("click", disableSponsorViewTracking);
|
||||||
document.getElementById("enableSponsorViewTracking").addEventListener("click", enableSponsorViewTracking);
|
SB.enableSponsorViewTracking.addEventListener("click", enableSponsorViewTracking);
|
||||||
document.getElementById("optionsButton").addEventListener("click", openOptions);
|
SB.optionsButton.addEventListener("click", openOptions);
|
||||||
document.getElementById("reportAnIssue").addEventListener("click", reportAnIssue);
|
SB.reportAnIssue.addEventListener("click", reportAnIssue);
|
||||||
|
|
||||||
//if true, the button now selects the end time
|
//if true, the button now selects the end time
|
||||||
var startTimeChosen = false;
|
var startTimeChosen = false;
|
||||||
@@ -27,7 +50,7 @@ var isYouTubeTab = false;
|
|||||||
chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) {
|
chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) {
|
||||||
let dontShowNoticeAgain = result.dontShowNoticeAgain;
|
let dontShowNoticeAgain = result.dontShowNoticeAgain;
|
||||||
if (dontShowNoticeAgain != undefined && dontShowNoticeAgain) {
|
if (dontShowNoticeAgain != undefined && dontShowNoticeAgain) {
|
||||||
document.getElementById("showNoticeAgain").style.display = "unset";
|
SB.showNoticeAgain.style.display = "unset";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,8 +58,8 @@ chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) {
|
|||||||
chrome.storage.sync.get(["hideVideoPlayerControls"], function(result) {
|
chrome.storage.sync.get(["hideVideoPlayerControls"], function(result) {
|
||||||
let hideVideoPlayerControls = result.hideVideoPlayerControls;
|
let hideVideoPlayerControls = result.hideVideoPlayerControls;
|
||||||
if (hideVideoPlayerControls != undefined && hideVideoPlayerControls) {
|
if (hideVideoPlayerControls != undefined && hideVideoPlayerControls) {
|
||||||
document.getElementById("hideVideoPlayerControls").style.display = "none";
|
SB.hideVideoPlayerControls.style.display = "none";
|
||||||
document.getElementById("showVideoPlayerControls").style.display = "unset";
|
SB.showVideoPlayerControls.style.display = "unset";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,25 +67,21 @@ chrome.storage.sync.get(["hideVideoPlayerControls"], function(result) {
|
|||||||
chrome.storage.sync.get(["trackViewCount"], function(result) {
|
chrome.storage.sync.get(["trackViewCount"], function(result) {
|
||||||
let trackViewCount = result.trackViewCount;
|
let trackViewCount = result.trackViewCount;
|
||||||
if (trackViewCount != undefined && !trackViewCount) {
|
if (trackViewCount != undefined && !trackViewCount) {
|
||||||
document.getElementById("disableSponsorViewTracking").style.display = "none";
|
SB.disableSponsorViewTracking.style.display = "none";
|
||||||
document.getElementById("enableSponsorViewTracking").style.display = "unset";
|
SB.enableSponsorViewTracking.style.display = "unset";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//get the amount of times this user has contributed and display it to thank them
|
//get the amount of times this user has contributed and display it to thank them
|
||||||
chrome.storage.sync.get(["sponsorTimesContributed"], function(result) {
|
chrome.storage.sync.get(["sponsorTimesContributed"], function(result) {
|
||||||
if (result.sponsorTimesContributed != undefined) {
|
if (result.sponsorTimesContributed != undefined) {
|
||||||
let sponsorTimesContributionsContainer = document.getElementById("sponsorTimesContributionsContainer");
|
|
||||||
let sponsorTimesContributionsDisplay = document.getElementById("sponsorTimesContributionsDisplay");
|
|
||||||
let sponsorTimesContributionsDisplayEndWord = document.getElementById("sponsorTimesContributionsDisplayEndWord");
|
|
||||||
|
|
||||||
if (result.sponsorTimesContributed > 1) {
|
if (result.sponsorTimesContributed > 1) {
|
||||||
sponsorTimesContributionsDisplayEndWord.innerText = "sponsors."
|
SB.sponsorTimesContributionsDisplayEndWord.innerText = "sponsors."
|
||||||
} else {
|
} else {
|
||||||
sponsorTimesContributionsDisplayEndWord.innerText = "sponsor."
|
SB.sponsorTimesContributionsDisplayEndWord.innerText = "sponsor."
|
||||||
}
|
}
|
||||||
sponsorTimesContributionsDisplay.innerText = result.sponsorTimesContributed;
|
SB.sponsorTimesContributionsDisplay.innerText = result.sponsorTimesContributed;
|
||||||
sponsorTimesContributionsContainer.style.display = "unset";
|
SB.sponsorTimesContributionsContainer.style.display = "unset";
|
||||||
|
|
||||||
//get the userID
|
//get the userID
|
||||||
chrome.storage.sync.get(["userID"], function(result) {
|
chrome.storage.sync.get(["userID"], function(result) {
|
||||||
@@ -73,19 +92,14 @@ chrome.storage.sync.get(["sponsorTimesContributed"], function(result) {
|
|||||||
sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(xmlhttp) {
|
sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(xmlhttp) {
|
||||||
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
|
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
|
||||||
let viewCount = JSON.parse(xmlhttp.responseText).viewCount;
|
let viewCount = JSON.parse(xmlhttp.responseText).viewCount;
|
||||||
|
|
||||||
if (viewCount != 0) {
|
if (viewCount != 0) {
|
||||||
let sponsorTimesViewsContainer = document.getElementById("sponsorTimesViewsContainer");
|
|
||||||
let sponsorTimesViewsDisplay = document.getElementById("sponsorTimesViewsDisplay");
|
|
||||||
let sponsorTimesViewsDisplayEndWord = document.getElementById("sponsorTimesViewsDisplayEndWord");
|
|
||||||
|
|
||||||
if (viewCount > 1) {
|
if (viewCount > 1) {
|
||||||
sponsorTimesViewsDisplayEndWord.innerText = "sponsor segments."
|
SB.sponsorTimesViewsDisplayEndWord.innerText = "sponsor segments."
|
||||||
} else {
|
} else {
|
||||||
sponsorTimesViewsDisplayEndWord.innerText = "sponsor segment."
|
SB.sponsorTimesViewsDisplayEndWord.innerText = "sponsor segment."
|
||||||
}
|
}
|
||||||
sponsorTimesViewsDisplay.innerText = viewCount;
|
SB.sponsorTimesViewsDisplay.innerText = viewCount;
|
||||||
sponsorTimesViewsContainer.style.display = "unset";
|
SB.sponsorTimesViewsContainer.style.display = "unset";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -117,7 +131,7 @@ function loadTabData(tabs) {
|
|||||||
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
||||||
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) {
|
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) {
|
||||||
startTimeChosen = true;
|
startTimeChosen = true;
|
||||||
document.getElementById("sponsorStart").innerHTML = "Sponsorship Ends Now";
|
SB.sponsorStart.innerHTML = "Sponsorship Ends Now";
|
||||||
}
|
}
|
||||||
|
|
||||||
sponsorTimes = sponsorTimesStorage;
|
sponsorTimes = sponsorTimesStorage;
|
||||||
@@ -372,7 +386,7 @@ function showNoticeAgain() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("showNoticeAgain").style.display = "none";
|
SB.showNoticeAgain.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideVideoPlayerControls() {
|
function hideVideoPlayerControls() {
|
||||||
@@ -388,8 +402,8 @@ function hideVideoPlayerControls() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("hideVideoPlayerControls").style.display = "none";
|
SB.hideVideoPlayerControls.style.display = "none";
|
||||||
document.getElementById("showVideoPlayerControls").style.display = "unset";
|
SB.showVideoPlayerControls.style.display = "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
function showVideoPlayerControls() {
|
function showVideoPlayerControls() {
|
||||||
@@ -405,8 +419,8 @@ function showVideoPlayerControls() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("hideVideoPlayerControls").style.display = "unset";
|
SB.hideVideoPlayerControls.style.display = "unset";
|
||||||
document.getElementById("showVideoPlayerControls").style.display = "none";
|
SB.showVideoPlayerControls.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableSponsorViewTracking() {
|
function disableSponsorViewTracking() {
|
||||||
@@ -422,8 +436,8 @@ function disableSponsorViewTracking() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("disableSponsorViewTracking").style.display = "none";
|
SB.disableSponsorViewTracking.style.display = "none";
|
||||||
document.getElementById("enableSponsorViewTracking").style.display = "unset";
|
SB.enableSponsorViewTracking.style.display = "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableSponsorViewTracking() {
|
function enableSponsorViewTracking() {
|
||||||
@@ -439,15 +453,15 @@ function enableSponsorViewTracking() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("enableSponsorViewTracking").style.display = "none";
|
SB.enableSponsorViewTracking.style.display = "none";
|
||||||
document.getElementById("disableSponsorViewTracking").style.display = "unset";
|
SB.disableSponsorViewTracking.style.display = "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStartTimeChosen() {
|
function updateStartTimeChosen() {
|
||||||
//update startTimeChosen variable
|
//update startTimeChosen variable
|
||||||
if (!startTimeChosen) {
|
if (!startTimeChosen) {
|
||||||
startTimeChosen = true;
|
startTimeChosen = true;
|
||||||
document.getElementById("sponsorStart").innerHTML = "Sponsorship Ends Now";
|
SB.sponsorStart.innerHTML = "Sponsorship Ends Now";
|
||||||
} else {
|
} else {
|
||||||
resetStartTimeChosen();
|
resetStartTimeChosen();
|
||||||
}
|
}
|
||||||
@@ -456,7 +470,7 @@ function updateStartTimeChosen() {
|
|||||||
//set it to false
|
//set it to false
|
||||||
function resetStartTimeChosen() {
|
function resetStartTimeChosen() {
|
||||||
startTimeChosen = false;
|
startTimeChosen = false;
|
||||||
document.getElementById("sponsorStart").innerHTML = "Sponsorship Starts Now";
|
SB.sponsorStart.innerHTML = "Sponsorship Starts Now";
|
||||||
}
|
}
|
||||||
|
|
||||||
//hides and shows the submit times button when needed
|
//hides and shows the submit times button when needed
|
||||||
@@ -485,7 +499,7 @@ function displayNoVideo() {
|
|||||||
|
|
||||||
function reportAnIssue() {
|
function reportAnIssue() {
|
||||||
document.getElementById("issueReporterContainer").style.display = "unset";
|
document.getElementById("issueReporterContainer").style.display = "unset";
|
||||||
document.getElementById("reportAnIssue").style.display = "none";
|
SB.reportAnIssue.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function addVoteMessage(message, UUID) {
|
function addVoteMessage(message, UUID) {
|
||||||
@@ -566,4 +580,4 @@ function getYouTubeVideoID(url) { // Return video id or false
|
|||||||
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
|
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
|
||||||
var match = url.match(regExp);
|
var match = url.match(regExp);
|
||||||
return (match && match[7].length == 11) ? match[7] : false;
|
return (match && match[7].length == 11) ? match[7] : false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user