2020-01-10 04:13:00 +01:00
|
|
|
var isBackgroundScript = false;
|
2019-12-05 21:35:25 +01:00
|
|
|
var onInvidious = false;
|
|
|
|
|
2019-08-20 18:46:05 +02:00
|
|
|
// Function that can be used to wait for a condition before returning
|
|
|
|
async function wait(condition, timeout = 5000, check = 100) {
|
2019-08-24 03:10:28 +02:00
|
|
|
return await new Promise((resolve, reject) => {
|
|
|
|
setTimeout(() => reject("TIMEOUT"), timeout);
|
|
|
|
|
|
|
|
let intervalCheck = () => {
|
|
|
|
let result = condition();
|
|
|
|
if (result !== false) {
|
|
|
|
resolve(result);
|
|
|
|
clearInterval(interval);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let interval = setInterval(intervalCheck, check);
|
|
|
|
|
|
|
|
//run the check once first, this speeds it up a lot
|
|
|
|
intervalCheck();
|
|
|
|
});
|
2019-08-20 18:46:05 +02:00
|
|
|
}
|
|
|
|
|
2019-08-06 23:06:23 +02:00
|
|
|
function getYouTubeVideoID(url) {
|
2019-12-29 22:48:10 +01:00
|
|
|
// For YouTube TV support
|
2020-01-10 03:23:25 +01:00
|
|
|
if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
|
2019-12-29 22:39:19 +01:00
|
|
|
|
2019-08-11 01:51:08 +02:00
|
|
|
//Attempt to parse url
|
2019-08-11 02:53:43 +02:00
|
|
|
let urlObject = null;
|
2019-08-11 01:51:08 +02:00
|
|
|
try {
|
2019-12-29 22:48:10 +01:00
|
|
|
urlObject = new URL(url);
|
2019-08-07 16:46:07 +02:00
|
|
|
} catch (e) {
|
2019-12-29 22:48:10 +01:00
|
|
|
console.error("[SB] Unable to parse URL: " + url);
|
|
|
|
return false;
|
2019-08-07 16:34:21 +02:00
|
|
|
}
|
2019-08-24 03:10:28 +02:00
|
|
|
|
2019-08-11 01:51:08 +02:00
|
|
|
//Check if valid hostname
|
2020-01-10 03:23:25 +01:00
|
|
|
if (SB.config && SB.config.invidiousInstances.includes(urlObject.host)) {
|
2019-12-05 21:35:25 +01:00
|
|
|
onInvidious = true;
|
2019-12-19 06:36:35 +01:00
|
|
|
} else if (!["www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) {
|
2020-01-10 03:23:25 +01:00
|
|
|
if (!SB.config) {
|
|
|
|
// Call this later, in case this is an Invidious tab
|
|
|
|
wait(() => SB.config !== undefined).then(() => videoIDChange(getYouTubeVideoID(url)));
|
|
|
|
}
|
|
|
|
|
2019-12-19 06:36:35 +01:00
|
|
|
return false
|
2019-12-05 21:35:25 +01:00
|
|
|
}
|
2019-08-24 03:10:28 +02:00
|
|
|
|
2019-08-11 01:55:39 +02:00
|
|
|
//Get ID from searchParam
|
2019-12-29 22:51:11 +01:00
|
|
|
if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) {
|
|
|
|
id = urlObject.searchParams.get("v");
|
2019-08-24 03:10:28 +02:00
|
|
|
return id.length == 11 ? id : false;
|
2019-08-11 01:55:39 +02:00
|
|
|
} else if (urlObject.pathname.startsWith("/embed/")) {
|
2019-08-24 03:10:28 +02:00
|
|
|
try {
|
|
|
|
return urlObject.pathname.substr(7, 11);
|
|
|
|
} catch (e) {
|
|
|
|
console.error("[SB] Video ID not valid for " + url);
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-29 22:39:19 +01:00
|
|
|
}
|
2019-08-23 18:19:44 +02:00
|
|
|
return false;
|
2019-08-24 19:06:25 +02:00
|
|
|
}
|
|
|
|
|
2020-01-10 04:13:00 +01:00
|
|
|
/**
|
|
|
|
* Asks for the optional permissions required for all extra sites.
|
|
|
|
* It also starts the content script registrations.
|
|
|
|
*
|
|
|
|
* For now, it is just SB.config.invidiousInstances.
|
|
|
|
*
|
|
|
|
* @param {CallableFunction} callback
|
|
|
|
*/
|
|
|
|
function setupExtraSitePermissions(callback) {
|
|
|
|
// Request permission
|
|
|
|
let permissions = ["declarativeContent"];
|
|
|
|
if (isFirefox()) permissions = [];
|
|
|
|
|
|
|
|
chrome.permissions.request({
|
|
|
|
origins: getInvidiousInstancesRegex(),
|
|
|
|
permissions: permissions
|
|
|
|
}, async function (granted) {
|
|
|
|
if (granted) {
|
|
|
|
setupExtraSiteContentScripts();
|
|
|
|
} else {
|
2020-01-10 04:32:20 +01:00
|
|
|
removeExtraSiteRegistration();
|
2020-01-10 04:13:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
callback(granted);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers the content scripts for the extra sites.
|
|
|
|
* Will use a different method depending on the browser.
|
|
|
|
* This is called by setupExtraSitePermissions().
|
|
|
|
*
|
|
|
|
* For now, it is just SB.config.invidiousInstances.
|
|
|
|
*/
|
|
|
|
function setupExtraSiteContentScripts() {
|
|
|
|
let js = [
|
|
|
|
"config.js",
|
|
|
|
"SB.js",
|
|
|
|
"utils/previewBar.js",
|
|
|
|
"utils/skipNotice.js",
|
|
|
|
"utils.js",
|
|
|
|
"content.js",
|
|
|
|
"popup.js"
|
|
|
|
];
|
|
|
|
let css = [
|
|
|
|
"content.css",
|
|
|
|
"./libs/Source+Sans+Pro.css",
|
|
|
|
"popup.css"
|
|
|
|
];
|
|
|
|
|
|
|
|
if (isFirefox()) {
|
|
|
|
let firefoxJS = [];
|
|
|
|
for (const file of js) {
|
|
|
|
firefoxJS.push({file});
|
|
|
|
}
|
|
|
|
let firefoxCSS = [];
|
|
|
|
for (const file of css) {
|
|
|
|
firefoxCSS.push({file});
|
|
|
|
}
|
|
|
|
|
|
|
|
let registration = {
|
|
|
|
message: "registerContentScript",
|
|
|
|
id: "invidious",
|
|
|
|
allFrames: true,
|
|
|
|
js: firefoxJS,
|
|
|
|
css: firefoxCSS,
|
|
|
|
matches: getInvidiousInstancesRegex()
|
|
|
|
};
|
|
|
|
|
|
|
|
if (isBackgroundScript) {
|
|
|
|
registerFirefoxContentScript(registration);
|
|
|
|
} else {
|
|
|
|
chrome.runtime.sendMessage(registration);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chrome.declarativeContent.onPageChanged.removeRules(["invidious"], function() {
|
|
|
|
let conditions = [];
|
|
|
|
for (const regex of getInvidiousInstancesRegex()) {
|
|
|
|
conditions.push(new chrome.declarativeContent.PageStateMatcher({
|
|
|
|
pageUrl: { urlMatches: regex }
|
|
|
|
}));
|
|
|
|
}
|
2020-01-10 04:32:20 +01:00
|
|
|
|
2020-01-10 04:13:00 +01:00
|
|
|
// Add page rule
|
|
|
|
let rule = {
|
|
|
|
id: "invidious",
|
|
|
|
conditions,
|
|
|
|
actions: [new chrome.declarativeContent.RequestContentScript({
|
|
|
|
allFrames: true,
|
|
|
|
js,
|
|
|
|
css
|
|
|
|
})]
|
|
|
|
};
|
|
|
|
|
|
|
|
chrome.declarativeContent.onPageChanged.addRules([rule]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 04:32:20 +01:00
|
|
|
/**
|
|
|
|
* Removes the permission and content script registration.
|
|
|
|
*/
|
|
|
|
function removeExtraSiteRegistration() {
|
|
|
|
if (isFirefox()) {
|
|
|
|
let id = "invidious";
|
|
|
|
|
|
|
|
if (isBackgroundScript) {
|
|
|
|
if (contentScriptRegistrations[id]) {
|
|
|
|
contentScriptRegistrations[id].unregister();
|
|
|
|
delete contentScriptRegistrations[id];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chrome.runtime.sendMessage({
|
|
|
|
message: "unregisterContentScript",
|
|
|
|
id: id
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chrome.declarativeContent.onPageChanged.removeRules(["invidious"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
chrome.permissions.remove({
|
|
|
|
origins: getInvidiousInstancesRegex()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-08-24 19:59:53 +02:00
|
|
|
function localizeHtmlPage() {
|
2019-08-24 19:06:25 +02:00
|
|
|
//Localize by replacing __MSG_***__ meta tags
|
2019-12-30 05:18:18 +01:00
|
|
|
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
|
2019-08-28 03:10:35 +02:00
|
|
|
for (var j = 0; j < objects.length; j++) {
|
2019-08-24 19:06:25 +02:00
|
|
|
var obj = objects[j];
|
2019-12-30 05:18:18 +01:00
|
|
|
|
|
|
|
let localizedMessage = getLocalizedMessage(obj.innerHTML.toString());
|
|
|
|
if (localizedMessage) obj.innerHTML = localizedMessage;
|
2019-08-24 19:06:25 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-24 03:10:28 +02:00
|
|
|
|
2019-12-30 05:18:18 +01:00
|
|
|
function getLocalizedMessage(text) {
|
|
|
|
var valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
|
|
|
|
return v1 ? chrome.i18n.getMessage(v1) : "";
|
|
|
|
});
|
|
|
|
|
|
|
|
if(valNewH != text) {
|
|
|
|
return valNewH;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 04:13:00 +01:00
|
|
|
/**
|
|
|
|
* @returns {String[]} Invidious Instances in regex form
|
|
|
|
*/
|
|
|
|
function getInvidiousInstancesRegex() {
|
|
|
|
var invidiousInstancesRegex = [];
|
|
|
|
for (const url of SB.config.invidiousInstances) {
|
|
|
|
invidiousInstancesRegex.push("https://*." + url + "/*");
|
|
|
|
invidiousInstancesRegex.push("http://*." + url + "/*");
|
|
|
|
}
|
|
|
|
|
|
|
|
return invidiousInstancesRegex;
|
|
|
|
}
|
|
|
|
|
2019-12-14 03:55:43 +01:00
|
|
|
function generateUserID(length = 36) {
|
|
|
|
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
|
|
let result = "";
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-14 03:32:57 +01:00
|
|
|
/**
|
|
|
|
* Gets the error message in a nice string
|
|
|
|
*
|
|
|
|
* @param {int} statusCode
|
|
|
|
* @returns {string} errorMessage
|
|
|
|
*/
|
|
|
|
function getErrorMessage(statusCode) {
|
|
|
|
let errorMessage = "";
|
|
|
|
|
|
|
|
if([400, 429, 409, 502, 0].includes(statusCode)) {
|
|
|
|
//treat them the same
|
|
|
|
if (statusCode == 503) statusCode = 502;
|
|
|
|
|
|
|
|
errorMessage = chrome.i18n.getMessage(statusCode + "") + " " + chrome.i18n.getMessage("errorCode") + statusCode
|
|
|
|
+ "\n\n" + chrome.i18n.getMessage("statusReminder");
|
|
|
|
} else {
|
|
|
|
errorMessage = chrome.i18n.getMessage("connectionError") + statusCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return errorMessage;
|
2019-12-29 22:39:19 +01:00
|
|
|
}
|
2019-12-31 23:48:43 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Is this Firefox (web-extensions)
|
|
|
|
*/
|
|
|
|
function isFirefox() {
|
|
|
|
return typeof(browser) !== "undefined";
|
|
|
|
}
|