Changed whitelisting to use channel JSON instead of page scraping.

It also only pulls data once. Uses channelID now instead of URLs.

Resolves https://github.com/ajayyy/SponsorBlock/issues/275

TODO: Add migration method to use channelID instead of channelURL
This commit is contained in:
Ajay Ramachandran
2020-04-26 15:16:00 -04:00
parent 0f9e794428
commit 16896be97f
3 changed files with 84 additions and 124 deletions

View File

@@ -540,5 +540,8 @@
}, },
"hiddenDueToDuration": { "hiddenDueToDuration": {
"message": "hidden: too short" "message": "hidden: too short"
},
"channelDataNotFound": {
"message": "Channel ID not loaded yet"
} }
} }

View File

@@ -24,6 +24,11 @@ var sponsorTimes: SponsorTime[] = null;
//what video id are these sponsors for //what video id are these sponsors for
var sponsorVideoID: VideoID = null; var sponsorVideoID: VideoID = null;
// JSON video info
var videoInfo: any = null;
//the channel this video is about
var channelID;
// Skips are scheduled to ensure precision. // Skips are scheduled to ensure precision.
// Skips are rescheduled every seeking event. // Skips are rescheduled every seeking event.
// Skips are canceled every seeking event // Skips are canceled every seeking event
@@ -56,12 +61,6 @@ var switchingVideos = null;
var lastCheckTime = 0; var lastCheckTime = 0;
var lastCheckVideoTime = -1; var lastCheckVideoTime = -1;
//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 //is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false; var channelWhitelisted = false;
@@ -183,9 +182,9 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
}); });
break; break;
case "getChannelURL": case "getChannelID":
sendResponse({ sendResponse({
channelURL: channelURL channelID: channelID
}); });
break; break;
@@ -262,6 +261,10 @@ function resetValues() {
sponsorTimes = null; sponsorTimes = null;
sponsorLookupRetries = 0; sponsorLookupRetries = 0;
videoInfo = null;
channelWhitelisted = false;
channelID = null;
//empty the preview bar //empty the preview bar
if (previewBar !== null) { if (previewBar !== null) {
previewBar.set([], [], 0); previewBar.set([], [], 0);
@@ -293,6 +296,9 @@ async function videoIDChange(id) {
// Wait for options to be ready // Wait for options to be ready
await utils.wait(() => Config.config !== null, 5000, 1); await utils.wait(() => Config.config !== null, 5000, 1);
// Get new video info
getVideoInfo();
// If enabled, it will check if this video is private or unlisted and double check with the user if the sponsors should be looked up // If enabled, it will check if this video is private or unlisted and double check with the user if the sponsors should be looked up
if (Config.config.checkForUnlistedVideos) { if (Config.config.checkForUnlistedVideos) {
await utils.wait(isPrivacyInfoAvailable); await utils.wait(isPrivacyInfoAvailable);
@@ -303,10 +309,8 @@ async function videoIDChange(id) {
} }
} }
// TODO: Use a better method here than using type any // Update whitelist data when the video data is loaded
// This is done to be able to do channelIDPromise.isFulfilled and channelIDPromise.isRejected utils.wait(() => !!videoInfo).then(whitelistCheck);
let channelIDPromise: any = utils.wait(getChannelID);
channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true);
//setup the preview bar //setup the preview bar
if (previewBar === null) { if (previewBar === null) {
@@ -346,7 +350,7 @@ async function videoIDChange(id) {
//close popup //close popup
closeInfoMenu(); closeInfoMenu();
sponsorsLookup(id, channelIDPromise); sponsorsLookup(id);
//make sure everything is properly added //make sure everything is properly added
updateVisibilityOfPlayerControlsButton().then(() => { updateVisibilityOfPlayerControlsButton().then(() => {
@@ -523,11 +527,11 @@ function incorrectVideoIDCheck(): boolean {
} }
} }
function sponsorsLookup(id: string, channelIDPromise?) { function sponsorsLookup(id: string) {
video = document.querySelector('video') // Youtube video player video = document.querySelector('video') // Youtube video player
//there is no video here //there is no video here
if (video == null) { if (video == null) {
setTimeout(() => sponsorsLookup(id, channelIDPromise), 100); setTimeout(() => sponsorsLookup(id), 100);
return; return;
} }
@@ -586,18 +590,6 @@ function sponsorsLookup(id: string, channelIDPromise?) {
startSponsorSchedule(); startSponsorSchedule();
} }
if (channelIDPromise !== undefined) {
if (channelIDPromise.isFulfilled) {
whitelistCheck();
} else if (channelIDPromise.isRejected) {
//try again
utils.wait(getChannelID).then(whitelistCheck).catch();
} else {
//add it as a then statement
channelIDPromise.then(whitelistCheck);
}
}
//check database for sponsor times //check database for sponsor times
//made true once a setTimeout has been created to try again after a server error //made true once a setTimeout has been created to try again after a server error
let recheckStarted = false; let recheckStarted = false;
@@ -682,23 +674,13 @@ function sponsorsLookup(id: string, channelIDPromise?) {
sponsorDataFound = false; sponsorDataFound = false;
//check if this video was uploaded recently //check if this video was uploaded recently
//use the invidious api to get the time published utils.wait(() => !!videoInfo).then(() => {
sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + id, function(xmlhttp, error) { let dateUploaded = videoInfo.microformat.playerMicroformatRenderer.uploadDate;
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
if (decodedData === undefined) { //if less than 3 days old
console.error("[SB] Failed at getting video upload date info from YouTube."); if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
return; //TODO lower when server becomes better
} setTimeout(() => sponsorsLookup(id), 180000);
let dateUploaded = JSON.parse(decodedData).microformat.playerMicroformatRenderer.uploadDate;
//if less than 3 days old
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
//TODO lower when server becomes better
setTimeout(() => sponsorsLookup(id, channelIDPromise), 180000);
}
} }
}); });
@@ -708,13 +690,30 @@ function sponsorsLookup(id: string, channelIDPromise?) {
//TODO lower when server becomes better (back to 1 second) //TODO lower when server becomes better (back to 1 second)
//some error occurred, try again in a second //some error occurred, try again in a second
setTimeout(() => sponsorsLookup(id, channelIDPromise), 10000); setTimeout(() => sponsorsLookup(id), 10000);
sponsorLookupRetries++; sponsorLookupRetries++;
} }
}); });
} }
/**
* Get the video info for the current tab from YouTube
*/
function getVideoInfo() {
sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
if (!decodedData) {
console.error("[SB] Failed at getting video info from YouTube.");
return;
}
videoInfo = JSON.parse(decodedData);
}
});
}
function getYouTubeVideoID(url: string) { function getYouTubeVideoID(url: string) {
// For YouTube TV support // For YouTube TV support
if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
@@ -757,55 +756,6 @@ function getYouTubeVideoID(url: string) {
return false; return false;
} }
function getChannelID() {
//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...
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 {
//old YouTube theme
let channelContainers = document.getElementsByClassName("yt-user-info");
if (channelContainers.length != 0) {
channelURLContainer = channelContainers[0].firstElementChild;
}
}
if (channelURLContainer === null) {
//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 = (<HTMLElement> titleInfoContainer.firstElementChild.firstElementChild.querySelector(".title").firstElementChild).innerText;
} else if (onInvidious) {
// 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;
}
title = currentTitle;
channelURL = channelURLContainer.getAttribute("href");
//reset variables
channelWhitelisted = false;
}
/** /**
* This function is required on mobile YouTube and will keep getting called whenever the preview bar disapears * This function is required on mobile YouTube and will keep getting called whenever the preview bar disapears
*/ */
@@ -845,10 +795,12 @@ function updatePreviewBar() {
//checks if this channel is whitelisted, should be done only after the channelID has been loaded //checks if this channel is whitelisted, should be done only after the channelID has been loaded
function whitelistCheck() { function whitelistCheck() {
channelID = videoInfo.videoDetails.channelId;
//see if this is a whitelisted channel //see if this is a whitelisted channel
let whitelistedChannels = Config.config.whitelistedChannels; let whitelistedChannels = Config.config.whitelistedChannels;
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelURL)) { if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
channelWhitelisted = true; channelWhitelisted = true;
} }
} }

View File

@@ -919,39 +919,44 @@ async function runThePopup(messageListener?: MessageListener) {
}, tabs => { }, tabs => {
messageHandler.sendMessage( messageHandler.sendMessage(
tabs[0].id, tabs[0].id,
{message: 'getChannelURL'}, {message: 'getChannelID'},
function(response) { function(response) {
if (!response.channelID) {
alert(chrome.i18n.getMessage("channelDataNotFound"));
return;
}
//get whitelisted channels //get whitelisted channels
let whitelistedChannels = Config.config.whitelistedChannels; let whitelistedChannels = Config.config.whitelistedChannels;
if (whitelistedChannels == undefined) { if (whitelistedChannels == undefined) {
whitelistedChannels = []; whitelistedChannels = [];
}
//add on this channel
whitelistedChannels.push(response.channelID);
//change button
PageElements.whitelistChannel.style.display = "none";
PageElements.unwhitelistChannel.style.display = "unset";
PageElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
PageElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
//save this
Config.config.whitelistedChannels = whitelistedChannels;
//send a message to the client
messageHandler.query({
active: true,
currentWindow: true
}, tabs => {
messageHandler.sendMessage(
tabs[0].id, {
message: 'whitelistChange',
value: true
});
} }
);
//add on this channel
whitelistedChannels.push(response.channelURL);
//change button
PageElements.whitelistChannel.style.display = "none";
PageElements.unwhitelistChannel.style.display = "unset";
PageElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
PageElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
//save this
Config.config.whitelistedChannels = whitelistedChannels;
//send a message to the client
messageHandler.query({
active: true,
currentWindow: true
}, tabs => {
messageHandler.sendMessage(
tabs[0].id, {
message: 'whitelistChange',
value: true
});
}
);
} }
); );
}); });