Merge branch 'experimental' into patch-18

This commit is contained in:
Official Noob 2019-08-12 15:59:25 +01:00 committed by GitHub
commit 5347b9c935
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 189 additions and 147 deletions

11
LICENSE
View file

@ -1,4 +1,8 @@
GNU GENERAL PUBLIC LICENSE
SponsorBlock Copyright (C) 2019 Ajay Ramachandran and other SponsorBlock contributors.
Please refer to the license below.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
@ -651,8 +655,9 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
SponsorBlock Copyright (C) 2019 Ajay Ramachandran and other SponsorBlock contributors
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View file

@ -3,7 +3,7 @@
# SponsorBlock
SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.
SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that lets anyone submit the start and end times of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.
# Available for Chrome and Firefox
@ -21,9 +21,13 @@ To make sure that this project doesn't die, I have made the database publicly do
Hopefully this project can be combined with projects like [this](https://github.com/Sponsoff/sponsorship_remover) and use this data to create a neural network to predict when sponsored segments happen. That project is sadly abandoned now, so I have decided to attempt to revive this idea.
# API
You can read the API docs [here](https://github.com/ajayyy/SponsorBlockServer#api-docs).
# Previous extension
This project is partially based off of [this experimental extension](https://github.com/OfficialNoob/YTSponsorSkip). That extension has the basic video skipping functionality.
This project is partially based off of [this experimental extension](https://github.com/OfficialNoob/YTSponsorSkip), which has the basic video skipping functionality.
# Build Yourself
@ -35,4 +39,4 @@ The awesome [Invidious API](https://github.com/omarroth/invidious/wiki/API) is u
Some icons made by <a href="https://www.flaticon.com/authors/gregor-cresnar" title="Gregor Cresnar">Gregor Cresnar</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> and are licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a>
Some icons made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a>
Some icons made by <a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> are licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a>

View file

@ -1,11 +1,3 @@
var previousVideoID = null
//the id of this user, randomly generated once per install
var userID = null;
//the last video id loaded, to make sure it is a video id change
var sponsorVideoID = null;
//when a new tab is highlighted
chrome.tabs.onActivated.addListener(
function(activeInfo) {
@ -62,14 +54,38 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
//add help page on install
chrome.runtime.onInstalled.addListener(function (object) {
chrome.storage.sync.get(["shownInstallPage"], function(result) {
let shownInstallPage = result.shownInstallPage;
if (shownInstallPage == undefined || !shownInstallPage) {
//open up the install page
chrome.tabs.create({url: chrome.extension.getURL("/help/index.html")});
// TODO (shownInstallPage): remove shownInstallPage logic after sufficient amount of time,
// so that people have time to upgrade and move to shownInstallPage-free code.
chrome.storage.sync.get(["userID", "shownInstallPage"], function(result) {
const userID = result.userID;
// TODO (shownInstallPage): delete row below
const shownInstallPage = result.shownInstallPage;
//save that this happened
chrome.storage.sync.set({shownInstallPage: true});
// If there is no userID, then it is the first install.
if (!userID){
// Show install page, if there is no user id
// and there is no shownInstallPage.
// TODO (shownInstallPage): remove this if statement, but leave contents
if (!shownInstallPage){
//open up the install page
chrome.tabs.create({url: chrome.extension.getURL("/help/index.html")});
}
// TODO (shownInstallPage): delete if statement and contents
// If shownInstallPage is set, remove it.
if (!!shownInstallPage){
chrome.storage.sync.remove("shownInstallPage");
}
//generate a userID
const newUserID = generateUUID();
//save this UUID
chrome.storage.sync.set({
"userID": newUserID,
//the last video id loaded, to make sure it is a video id change
"sponsorVideoID": null,
"previousVideoID": null
});
}
});
});
@ -109,9 +125,11 @@ function addSponsorTime(time, videoID, callback) {
}
function submitVote(type, UUID, callback) {
getUserID(function(userID) {
chrome.storage.sync.get(["userID"], function(result) {
let userID = result.userID;
//publish this vote
sendRequestToServer('GET', "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) {
sendRequestToServer("GET", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback({
successType: 1
@ -138,14 +156,14 @@ function submitTimes(videoID, callback) {
let sponsorTimeKey = 'sponsorTimes' + videoID;
chrome.storage.sync.get([sponsorTimeKey], function(result) {
let sponsorTimes = result[sponsorTimeKey];
let userID = result.userID;
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
//submit these times
for (let i = 0; i < sponsorTimes.length; i++) {
getUserID(function(userIDStorage) {
//submit the sponsorTime
sendRequestToServer('GET', "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1]
+ "&userID=" + userIDStorage, function(xmlhttp, error) {
sendRequestToServer("GET", "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1]
+ "&userID=" + userID, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && !error) {
callback({
statusCode: xmlhttp.status
@ -169,7 +187,6 @@ function submitTimes(videoID, callback) {
statusCode: -1
});
}
});
});
}
}
@ -183,63 +200,45 @@ function videoIDChange(currentVideoID, tabId) {
id: currentVideoID
});
//not a url change
if (sponsorVideoID == currentVideoID){
return;
}
sponsorVideoID = currentVideoID;
chrome.storage.sync.get(["sponsorVideoID", "previousVideoID"], function(result) {
const sponsorVideoID = result.sponsorVideoID;
const previousVideoID = result.previousVideoID;
//warn them if they had unsubmitted times
if (previousVideoID != null) {
//get the sponsor times from storage
let sponsorTimeKey = 'sponsorTimes' + previousVideoID;
chrome.storage.sync.get([sponsorTimeKey], function(result) {
let sponsorTimes = result[sponsorTimeKey];
//not a url change
if (sponsorVideoID == currentVideoID){
return;
}
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
//warn them that they have unsubmitted sponsor times
chrome.notifications.create("stillThere" + Math.random(), {
type: "basic",
title: "Do you want to submit the sponsor times for watch?v=" + previousVideoID + "?",
message: "You seem to have left some sponsor times unsubmitted. Go back to that page to submit them (they are not deleted).",
iconUrl: "./icons/LogoSponsorBlocker256px.png"
});
}
//set the previous video id to the currentID
previousVideoID = currentVideoID;
chrome.storage.sync.set({
"sponsorVideoID": currentVideoID
});
} else {
previousVideoID = currentVideoID;
}
}
function getUserID(callback) {
if (userID != null) {
callback(userID);
return;
}
//warn them if they had unsubmitted times
if (previousVideoID != null) {
//get the sponsor times from storage
let sponsorTimeKey = 'sponsorTimes' + previousVideoID;
chrome.storage.sync.get([sponsorTimeKey], function(result) {
let sponsorTimes = result[sponsorTimeKey];
//if it is not cached yet, grab it from storage
chrome.storage.sync.get(["userID"], function(result) {
let userIDStorage = result.userID;
if (userIDStorage != undefined) {
userID = userIDStorage;
callback(userID);
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
//warn them that they have unsubmitted sponsor times
chrome.notifications.create("stillThere" + Math.random(), {
type: "basic",
title: "Do you want to submit the sponsor times for watch?v=" + previousVideoID + "?",
message: "You seem to have left some sponsor times unsubmitted. Go back to that page to submit them (they are not deleted).",
iconUrl: "./icons/LogoSponsorBlocker256px.png"
});
}
//set the previous video id to the currentID
chrome.storage.sync.set({
"previousVideoID": currentVideoID
});
});
} else {
//double check if a UUID hasn't been created since this was first called
if (userID != null) {
callback(userID);
return;
}
//generate a userID
userID = generateUUID();
//save this UUID
chrome.storage.sync.set({"userID": userID});
callback(userID);
chrome.storage.sync.set({
"previousVideoID": currentVideoID
});
}
});
}
@ -263,11 +262,21 @@ function sendRequestToServer(type, address, callback) {
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);
return (match && match[7].length == 11) ? match[7] : false;
function generateUUID(length = 36) {
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
let isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
if (window.crypto && window.crypto.getRandomValues) {
values = new Uint32Array(length);
window.crypto.getRandomValues(values);
for (i = 0; i < length; i++) {
result += charset[values[i] % charset.length];
}
return result;
} else {
for (let i = 0; i < length; i++) {
result += charset[Math.floor(Math.random() * charset.length)];
}
return result;
}
}
//uuid generator function from https://gist.github.com/jed/982883
function generateUUID(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,generateUUID)}

View file

@ -10,10 +10,6 @@ var sponsorVideoID = null;
//the time this video is starting at when first played, if not zero
var youtubeVideoStartTime = null;
if(id = getYouTubeVideoID(document.URL)){ // Direct Links
videoIDChange(id);
}
//the video
var v;
@ -23,12 +19,20 @@ var channelURL;
//is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false;
if(id = getYouTubeVideoID(document.URL)){ // Direct Links
videoIDChange(id);
}
//the last time looked at (used to see if this time is in the interval)
var lastTime = -1;
//the actual time (not video time) that the last skip happened
var lastUnixTimeSkipped = -1;
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
var sponsorLookupRetries = 0;
//the last time in the video a sponsor was skipped
//used for the go back button
var lastSponsorTimeSkipped = null;
@ -195,6 +199,7 @@ function videoIDChange(id) {
sponsorTimes = null;
UUIDs = null;
sponsorVideoID = id;
sponsorLookupRetries = 0;
//see if there is a video start time
youtubeVideoStartTime = getYouTubeVideoStartTime(document.URL);
@ -275,7 +280,8 @@ function sponsorsLookup(id) {
getChannelID();
} else if (xmlhttp.readyState == 4) {
sponsorLookupRetries = 0;
} else if (xmlhttp.readyState == 4 && xmlhttp.status == 404) {
sponsorDataFound = false;
//check if this video was uploaded recently
@ -290,6 +296,13 @@ function sponsorsLookup(id) {
}
}
});
sponsorLookupRetries = 0;
} else if (xmlhttp.readyState == 4 && sponsorLookupRetries < 15) {
//some error occurred, try again in a second
setTimeout(() => sponsorsLookup(id), 1000);
sponsorLookupRetries++;
}
});
@ -1048,6 +1061,9 @@ function sendSubmitMessage(){
//clear the sponsor times
let sponsorTimeKey = "sponsorTimes" + currentVideoID;
chrome.storage.sync.set({[sponsorTimeKey]: []});
//request the sponsors from the server again
sponsorsLookup(currentVideoID);
} else {
//for a more detailed error message, they should check the popup
//show that the upload failed
@ -1143,26 +1159,3 @@ function sendRequestToCustomServer(type, fullAddress, callback) {
//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);
var id = new URL(url).searchParams.get("v");
if (url.includes("/embed/")) {
//it is an embed, don't search for v
id = match[7];
}
return (match && match[7].length == 11) ? id : false;
}
//returns the start time of the video if there was one specified (ex. ?t=5s)
function getYouTubeVideoStartTime(url) {
let searchParams = new URL(url).searchParams;
var startTime = searchParams.get("t");
if (startTime == null) {
startTime = searchParams.get("time_continue");
}
return startTime;
}

View file

@ -1,7 +1,7 @@
{
"name": "SponsorBlock for YouTube - Skip Sponsorships",
"short_name": "SponsorBlock",
"version": "1.0.29",
"version": "1.0.31",
"description": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
"content_scripts": [
{
@ -12,6 +12,7 @@
"js": [
"LANG.js",
"config.js",
"utils.js",
"content.js",
"popup.js"
],
@ -33,9 +34,7 @@
"icons/downvote.png",
"icons/PlayerInfoIconSponsorBlocker256px.png",
"icons/PlayerDeleteIconSponsorBlocker256px.png",
"popup.html",
"help/index.html",
"help/style.css"
"popup.html"
],
"permissions": [
"tabs",
@ -49,10 +48,11 @@
},
"background": {
"scripts":[
"LANG.js",
"utils.js",
"config.js",
"background.js"
]
],
"persistent": false
},
"icons": {
"16": "icons/IconSponsorBlocker16px.png",

View file

@ -193,5 +193,6 @@
<!-- Scripts that need to load after the html -->
<script src="config.js"></script>
<script src="utils.js"></script>
<script src="popup.js"></script>
</html>
</html>

View file

@ -114,26 +114,6 @@ function runThePopup() {
}
});
//see if whitelist button should be swapped
chrome.tabs.query({
active: true,
currentWindow: true
}, tabs => {
chrome.tabs.sendMessage(
tabs[0].id,
{message: 'isChannelWhitelisted'},
function(response) {
if (response.value) {
SB.whitelistChannel.style.display = "none";
SB.unwhitelistChannel.style.display = "unset";
SB.downloadedSponsorMessageTimes.innerText = MSG("CWL");
SB.downloadedSponsorMessageTimes.style.fontWeight = "bold";
}
});
}
);
//if the don't show notice again letiable is true, an option to
// disable should be available
chrome.storage.sync.get(["dontShowNoticeAgain"], function(result) {
@ -283,6 +263,26 @@ function runThePopup() {
SB.videoFound.innerHTML = MSG("SP_NONE");
}
}
//see if whitelist button should be swapped
chrome.tabs.query({
active: true,
currentWindow: true
}, tabs => {
chrome.tabs.sendMessage(
tabs[0].id,
{message: 'isChannelWhitelisted'},
function(response) {
if (response.value) {
SB.whitelistChannel.style.display = "none";
SB.unwhitelistChannel.style.display = "unset";
SB.downloadedSponsorMessageTimes.innerText = "Channel Whitelisted!";
SB.downloadedSponsorMessageTimes.style.fontWeight = "bold";
}
});
}
);
}
function setVideoID(request) {
@ -1112,13 +1112,6 @@ function runThePopup() {
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);
var id = new URL(url).searchParams.get("v");
return (match && match[7].length == 11) ? id : false;
}
//end of function
}

37
utils.js Normal file
View file

@ -0,0 +1,37 @@
function getYouTubeVideoID(url) {
//Attempt to parse url
let urlObject = null;
try {
urlObject = new URL(url);
} catch (e) {
console.error("[SB] Unable to parse URL: " + url);
return false
}
//Check if valid hostname
if(!["www.youtube.com","www.youtube-nocookie.com"].includes(urlObject.host)) return false;
//Get ID from searchParam
if ((urlObject.pathname == "/watch" || urlObject.pathname == "/watch/") && urlObject.searchParams.has("v")) {
id = urlObject.searchParams.get("v");
return id.length == 11 ? id : false;
} else if (urlObject.pathname.startsWith("/embed/")) {
try {
return urlObject.pathname.substr(7, 11);
} catch (e) {
console.error("[SB] Video ID not valid for " + url);
return false;
}
}
}
//returns the start time of the video if there was one specified (ex. ?t=5s)
function getYouTubeVideoStartTime(url) {
let searchParams = new URL(url).searchParams;
let startTime = searchParams.get("t");
if (startTime == null) {
startTime = searchParams.get("time_continue");
}
return startTime;
}