Merge pull request #181 from afrmtbl/add-invidious

Initial Invidious support
This commit is contained in:
Ajay Ramachandran
2020-01-09 22:41:08 -05:00
committed by GitHub
12 changed files with 596 additions and 118 deletions

3
SB.js
View File

@@ -159,7 +159,8 @@ SB.defaults = {
"hideInfoButtonPlayerControls": false, "hideInfoButtonPlayerControls": false,
"hideDeleteButtonPlayerControls": false, "hideDeleteButtonPlayerControls": false,
"hideDiscordLaunches": 0, "hideDiscordLaunches": 0,
"hideDiscordLink": false "hideDiscordLink": false,
"invidiousInstances": ["invidio.us", "invidiou.sh"]
} }
// Reset config // Reset config

View File

@@ -344,5 +344,35 @@
}, },
"keybindCurrentlySet": { "keybindCurrentlySet": {
"message": ". It is currently set to:" "message": ". It is currently set to:"
},
"supportInvidious": {
"message": "Support Invidious"
},
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) is a third party YouTube client. To enable support, you must accept the extra permissions. This does NOT work in incongnito on chrome and other chromium variants."
},
"optionsInfo": {
"message": "Enable Invidious support, disable autoskip, hide buttons and more."
},
"addInvidiousInstance": {
"message": "Add Invidious Instance"
},
"addInvidiousInstanceDescription": {
"message": "Add a custom instance of Invidious. This must be formatted with JUST the domain. Example: invidious.ajay.app"
},
"add": {
"message": "Add"
},
"addInvidiousInstanceError": {
"message": "This is an invalid domain. This should JUST include the domain part. Example: invidious.ajay.app"
},
"resetInvidiousInstance": {
"message": "Reset Invidious Instance List"
},
"resetInvidiousInstanceAlert": {
"message": "You are about to reset the Invidious instance list"
},
"currentInstances": {
"message": "Current Instances:"
} }
} }

View File

@@ -1,3 +1,15 @@
isBackgroundScript = true;
// Used only on Firefox, which does not support non persistent background pages.
var contentScriptRegistrations = {};
// Register content script if needed
if (isFirefox()) {
wait(() => SB.config !== undefined).then(function() {
if (SB.config.supportInvidious) setupExtraSiteContentScripts();
});
}
chrome.tabs.onUpdated.addListener(function(tabId) { chrome.tabs.onUpdated.addListener(function(tabId) {
chrome.tabs.sendMessage(tabId, { chrome.tabs.sendMessage(tabId, {
message: 'update', message: 'update',
@@ -40,6 +52,14 @@ chrome.runtime.onMessage.addListener(async function (request, sender, callback)
message: chrome.i18n.getMessage("leftTimes"), message: chrome.i18n.getMessage("leftTimes"),
iconUrl: "./icons/LogoSponsorBlocker256px.png" iconUrl: "./icons/LogoSponsorBlocker256px.png"
}); });
case "registerContentScript":
registerFirefoxContentScript(request);
return false;
case "unregisterContentScript":
contentScriptRegistrations[request.id].unregister();
delete contentScriptRegistrations[request.id];
return false;
} }
}); });
@@ -61,6 +81,24 @@ chrome.runtime.onInstalled.addListener(function (object) {
}, 1500); }, 1500);
}); });
/**
* Only works on Firefox.
* Firefox requires that it be applied after every extension restart.
*
* @param {JSON} options
*/
function registerFirefoxContentScript(options) {
let oldRegistration = contentScriptRegistrations[options.id];
if (oldRegistration) oldRegistration.unregister();
browser.contentScripts.register({
allFrames: options.allFrames,
js: options.js,
css: options.css,
matches: options.matches
}).then(() => void (contentScriptRegistrations[options.id] = registration));
}
//gets the sponsor times from memory //gets the sponsor times from memory
function getSponsorTimes(videoID, callback) { function getSponsorTimes(videoID, callback) {
let sponsorTimes = []; let sponsorTimes = [];

View File

@@ -148,7 +148,7 @@ function messageListener(request, sender, sendResponse) {
* *
* @param {String} changes * @param {String} changes
*/ */
function configUpdateListener(changes) { function contentConfigUpdateListener(changes) {
for (const key in changes) { for (const key in changes) {
switch(key) { switch(key) {
case "hideVideoPlayerControls": case "hideVideoPlayerControls":
@@ -160,8 +160,8 @@ function configUpdateListener(changes) {
} }
} }
if (!SB.configListeners.includes(configUpdateListener)) { if (!SB.configListeners.includes(contentConfigUpdateListener)) {
SB.configListeners.push(configUpdateListener); SB.configListeners.push(contentConfigUpdateListener);
} }
//check for hotkey pressed //check for hotkey pressed
@@ -224,8 +224,22 @@ function videoIDChange(id) {
if (previewBar == null) { if (previewBar == null) {
//create it //create it
wait(getControls).then(result => { wait(getControls).then(result => {
let progressBar = document.getElementsByClassName("ytp-progress-bar-container")[0] || document.getElementsByClassName("no-model cue-range-markers")[0]; const progressElementSelectors = [
previewBar = new PreviewBar(progressBar); // 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;
}
}
}); });
} }
@@ -284,8 +298,10 @@ function videoIDChange(id) {
} }
}); });
}); });
//see if video controls buttons should be added
if (!onInvidious) {
updateVisibilityOfPlayerControlsButton(); updateVisibilityOfPlayerControlsButton();
updateVisibilityOfPlayerControlsButton(false); }
} }
function sponsorsLookup(id, channelIDPromise) { function sponsorsLookup(id, channelIDPromise) {
@@ -407,6 +423,9 @@ function getChannelID() {
channelURLContainer = document.querySelector("#channel-name > #container > #text-container > #text"); channelURLContainer = document.querySelector("#channel-name > #container > #text-container > #text");
if (channelURLContainer !== null) { if (channelURLContainer !== null) {
channelURLContainer = channelURLContainer.firstElementChild; 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 { } else {
//old YouTube theme //old YouTube theme
let channelContainers = document.getElementsByClassName("yt-user-info"); let channelContainers = document.getElementsByClassName("yt-user-info");
@@ -425,6 +444,9 @@ function getChannelID() {
let currentTitle = ""; let currentTitle = "";
if (titleInfoContainer != null) { if (titleInfoContainer != null) {
currentTitle = titleInfoContainer.firstElementChild.firstElementChild.querySelector(".title").firstElementChild.innerText; currentTitle = 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 { } else {
//old YouTube theme //old YouTube theme
currentTitle = document.getElementById("eow-title").innerText; currentTitle = document.getElementById("eow-title").innerText;
@@ -595,7 +617,14 @@ function createButton(baseID, title, callback, imageName, isDraggable=false) {
function getControls() { function getControls() {
let controls = document.getElementsByClassName("ytp-right-controls"); let controls = document.getElementsByClassName("ytp-right-controls");
return (!controls || controls.length === 0) ? false : controls[controls.length - 1]
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 {
return controls[controls.length - 1];
}
}; };
//adds all the player controls buttons //adds all the player controls buttons
@@ -618,7 +647,7 @@ async function updateVisibilityOfPlayerControlsButton() {
await createButtons(); await createButtons();
if (SB.config.hideVideoPlayerControls) { if (SB.config.hideVideoPlayerControls || onInvidious) {
document.getElementById("startSponsorButton").style.display = "none"; document.getElementById("startSponsorButton").style.display = "none";
document.getElementById("submitButton").style.display = "none"; document.getElementById("submitButton").style.display = "none";
} else { } else {
@@ -626,13 +655,13 @@ async function updateVisibilityOfPlayerControlsButton() {
} }
//don't show the info button on embeds //don't show the info button on embeds
if (SB.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/")) { if (SB.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious) {
document.getElementById("infoButton").style.display = "none"; document.getElementById("infoButton").style.display = "none";
} else { } else {
document.getElementById("infoButton").style.removeProperty("display"); document.getElementById("infoButton").style.removeProperty("display");
} }
if (SB.config.hideDeleteButtonPlayerControls) { if (SB.config.hideDeleteButtonPlayerControls || onInvidious) {
document.getElementById("deleteButton").style.display = "none"; document.getElementById("deleteButton").style.display = "none";
} }
} }

View File

@@ -48,15 +48,19 @@
"notifications", "notifications",
"https://sponsor.ajay.app/*" "https://sponsor.ajay.app/*"
], ],
"optional_permissions": [
"*://*/*",
"declarativeContent"
],
"browser_action": { "browser_action": {
"default_title": "__MSG_Name__", "default_title": "__MSG_Name__",
"default_popup": "popup.html" "default_popup": "popup.html"
}, },
"background": { "background": {
"scripts":[ "scripts":[
"config.js",
"SB.js", "SB.js",
"utils.js", "utils.js",
"config.js",
"background.js" "background.js"
], ],
"persistent": false "persistent": false
@@ -68,6 +72,9 @@
"128": "icons/LogoSponsorBlocker128px.png", "128": "icons/LogoSponsorBlocker128px.png",
"256": "icons/LogoSponsorBlocker256px.png" "256": "icons/LogoSponsorBlocker256px.png"
}, },
"options_page": "options/options.html", "options_ui": {
"page": "options/options.html",
"open_in_tab": true
},
"manifest_version": 2 "manifest_version": 2
} }

View File

@@ -7,12 +7,16 @@ body {
text-align: center; text-align: center;
} }
.inline {
display: inline-block;
}
.bold { .bold {
font-weight: bold; font-weight: bold;
} }
.hidden { .hidden {
display: none; display: none !important;
} }
.keybind-status { .keybind-status {
@@ -42,7 +46,7 @@ body {
border-radius: 5px; border-radius: 5px;
font-size: 14px; font-size: 14px;
width: fit-content; width: max-content;
} }
.option-button:hover { .option-button:hover {

View File

@@ -22,6 +22,58 @@
<div id="options" class="hidden"> <div id="options" class="hidden">
<div id="support-invidious" option-type="toggle" sync-option="supportInvidious">
<label class="switch-container" label-name="__MSG_supportInvidious__">
<label class="switch">
<input type="checkbox">
<span class="slider round"></span>
</label>
</label>
<br/>
<br/>
<div class="small-description">__MSG_supportInvidiousDescription__</div>
</div>
<br/>
<br/>
<div option-type="text-change" sync-option="invidiousInstances">
<div class="option-button trigger-button">
__MSG_addInvidiousInstance__
</div>
<br/>
<div class="small-description">__MSG_addInvidiousInstanceDescription__</div>
<div class="option-hidden-section hidden">
<br/>
<input class="option-text-box" type="text">
<br/>
<br/>
<div class="option-button text-change-set inline">
__MSG_add__
</div>
<div class="option-button invidious-instance-reset inline">
__MSG_resetInvidiousInstance__
</div>
<br/>
<br/>
<span class="small-description">__MSG_currentInstances__</span>
<span class="small-description" option-type="display" sync-option="invidiousInstances"></span>
</div>
</div>
<br/>
<br/>
<div option-type="toggle" toggle-type="reverse" sync-option="disableAutoSkip"> <div option-type="toggle" toggle-type="reverse" sync-option="disableAutoSkip">
<label class="switch-container" label-name="__MSG_autoSkip__"> <label class="switch-container" label-name="__MSG_autoSkip__">

View File

@@ -3,17 +3,20 @@ window.addEventListener('DOMContentLoaded', init);
async function init() { async function init() {
localizeHtmlPage(); localizeHtmlPage();
if (!SB.configListeners.includes(optionsConfigUpdateListener)) {
SB.configListeners.push(optionsConfigUpdateListener);
}
await wait(() => SB.config !== undefined); await wait(() => SB.config !== undefined);
// Set all of the toggle options to the correct option // Set all of the toggle options to the correct option
let optionsContainer = document.getElementById("options"); let optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.children; let optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) { for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) { switch (optionsElements[i].getAttribute("option-type")) {
case "toggle": case "toggle":
let option = optionsElements[i].getAttribute("sync-option"); let option = optionsElements[i].getAttribute("sync-option");
let optionResult = SB.config[option]; let optionResult = SB.config[option];
let checkbox = optionsElements[i].querySelector("input"); let checkbox = optionsElements[i].querySelector("input");
@@ -27,20 +30,44 @@ async function init() {
} }
} }
// See if anything extra should be run first time
switch (option) {
case "supportInvidious":
invidiousInit(checkbox, option);
break;
}
// Add click listener
checkbox.addEventListener("click", () => { checkbox.addEventListener("click", () => {
SB.config[option] = reverse ? !checkbox.checked : checkbox.checked; SB.config[option] = reverse ? !checkbox.checked : checkbox.checked;
// See if anything extra must be run
switch (option) {
case "supportInvidious":
invidiousOnClick(checkbox, option);
break;
}
}); });
break; break;
case "text-change": case "text-change":
let button = optionsElements[i].querySelector(".trigger-button"); let button = optionsElements[i].querySelector(".trigger-button");
button.addEventListener("click", () => activateTextChange(optionsElements[i])); button.addEventListener("click", () => activateTextChange(optionsElements[i]));
let textChangeOption = optionsElements[i].getAttribute("sync-option");
// See if anything extra must be done
switch (textChangeOption) {
case "invidiousInstances":
invidiousInstanceAddInit(optionsElements[i], textChangeOption);
}
break; break;
case "keybind-change": case "keybind-change":
let keybindButton = optionsElements[i].querySelector(".trigger-button"); let keybindButton = optionsElements[i].querySelector(".trigger-button");
keybindButton.addEventListener("click", () => activateKeybindChange(optionsElements[i])); keybindButton.addEventListener("click", () => activateKeybindChange(optionsElements[i]));
break; break;
case "display":
updateDisplayElement(optionsElements[i])
} }
} }
@@ -48,6 +75,127 @@ async function init() {
optionsContainer.classList.add("animated"); optionsContainer.classList.add("animated");
} }
/**
* Called when the config is updated
*
* @param {String} element
*/
function optionsConfigUpdateListener(changes) {
let optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) {
case "display":
updateDisplayElement(optionsElements[i])
}
}
}
/**
* Will set display elements to the proper text
*
* @param {HTMLElement} element
*/
function updateDisplayElement(element) {
let displayOption = element.getAttribute("sync-option")
let displayText = SB.config[displayOption];
element.innerText = displayText;
// See if anything extra must be run
switch (displayOption) {
case "invidiousInstances":
element.innerText = displayText.join(', ');
break;
}
}
/**
* Initializes the option to add Invidious instances
*
* @param {HTMLElement} element
* @param {String} option
*/
function invidiousInstanceAddInit(element, option) {
let textBox = element.querySelector(".option-text-box");
let button = element.querySelector(".trigger-button");
let setButton = element.querySelector(".text-change-set");
setButton.addEventListener("click", async function(e) {
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http") || textBox.value.includes(":")) {
alert(chrome.i18n.getMessage("addInvidiousInstanceError"));
} else {
// Add this
let instanceList = SB.config[option];
if (!instanceList) instanceList = [];
instanceList.push(textBox.value);
SB.config[option] = instanceList;
let checkbox = document.querySelector("#support-invidious input");
checkbox.checked = true;
invidiousOnClick(checkbox, "supportInvidious");
textBox.value = "";
// Hide this section again
element.querySelector(".option-hidden-section").classList.add("hidden");
button.classList.remove("disabled");
}
});
let resetButton = element.querySelector(".invidious-instance-reset");
resetButton.addEventListener("click", function(e) {
if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) {
// Set to a clone of the default
SB.config[option] = SB.defaults[option].slice(0);
}
});
}
/**
* Run when the invidious button is being initialized
*
* @param {HTMLElement} checkbox
* @param {string} option
*/
function invidiousInit(checkbox, option) {
let permissions = ["declarativeContent"];
if (isFirefox()) permissions = [];
chrome.permissions.contains({
origins: getInvidiousInstancesRegex(),
permissions: permissions
}, function (result) {
if (result != checkbox.checked) {
SB.config[option] = result;
checkbox.checked = result;
}
});
}
/**
* Run whenever the invidious checkbox is clicked
*
* @param {HTMLElement} checkbox
* @param {string} option
*/
function invidiousOnClick(checkbox, option) {
if (checkbox.checked) {
setupExtraSitePermissions(function (granted) {
if (!granted) {
SB.config[option] = false;
checkbox.checked = false;
}
});
} else {
removeExtraSiteRegistration();
}
}
/** /**
* Will trigger the container to ask the user for a keybind. * Will trigger the container to ask the user for a keybind.
* *
@@ -115,6 +263,13 @@ function activateTextChange(element) {
let textBox = element.querySelector(".option-text-box"); let textBox = element.querySelector(".option-text-box");
let option = element.getAttribute("sync-option"); let option = element.getAttribute("sync-option");
// See if anything extra must be done
switch (option) {
case "invidiousInstances":
element.querySelector(".option-hidden-section").classList.remove("hidden");
return;
}
textBox.value = SB.config[option]; textBox.value = SB.config[option];
let setButton = element.querySelector(".text-change-set"); let setButton = element.querySelector(".text-change-set");

View File

@@ -196,6 +196,11 @@
<button id="optionsButton" class="dangerButton popupElement">__MSG_Options__</button> <button id="optionsButton" class="dangerButton popupElement">__MSG_Options__</button>
<br/> <br/>
<sub class="popupElement">
__MSG_optionsInfo__
</sub>
<br/> <br/>
</div> </div>

View File

@@ -231,7 +231,6 @@ async function runThePopup() {
} }
//load video times for this video //load video times for this video
console.log( SB.config.sponsorTimes.set)
setTimeout(()=> console.log( SB.config.sponsorTimes.set), 200 ) setTimeout(()=> console.log( SB.config.sponsorTimes.set), 200 )
let sponsorTimesStorage = SB.config.sponsorTimes.get(currentVideoID); let sponsorTimesStorage = SB.config.sponsorTimes.get(currentVideoID);
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) { if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {

162
utils.js
View File

@@ -1,3 +1,6 @@
var isBackgroundScript = false;
var onInvidious = false;
// Function that can be used to wait for a condition before returning // Function that can be used to wait for a condition before returning
async function wait(condition, timeout = 5000, check = 100) { async function wait(condition, timeout = 5000, check = 100) {
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
@@ -20,7 +23,7 @@ async function wait(condition, timeout = 5000, check = 100) {
function getYouTubeVideoID(url) { function getYouTubeVideoID(url) {
// For YouTube TV support // For YouTube TV support
if(document.URL.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
//Attempt to parse url //Attempt to parse url
let urlObject = null; let urlObject = null;
@@ -32,7 +35,16 @@ function getYouTubeVideoID(url) {
} }
//Check if valid hostname //Check if valid hostname
if(!["www.youtube.com","www.youtube-nocookie.com"].includes(urlObject.host)) return false; if (SB.config && SB.config.invidiousInstances.includes(urlObject.host)) {
onInvidious = true;
} else if (!["www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) {
if (!SB.config) {
// Call this later, in case this is an Invidious tab
wait(() => SB.config !== undefined).then(() => videoIDChange(getYouTubeVideoID(url)));
}
return false
}
//Get ID from searchParam //Get ID from searchParam
if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) { if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) {
@@ -49,6 +61,132 @@ function getYouTubeVideoID(url) {
return false; return false;
} }
/**
* 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 {
removeExtraSiteRegistration();
}
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 }
}));
}
// Add page rule
let rule = {
id: "invidious",
conditions,
actions: [new chrome.declarativeContent.RequestContentScript({
allFrames: true,
js,
css
})]
};
chrome.declarativeContent.onPageChanged.addRules([rule]);
});
}
}
/**
* 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()
});
}
function localizeHtmlPage() { function localizeHtmlPage() {
//Localize by replacing __MSG_***__ meta tags //Localize by replacing __MSG_***__ meta tags
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children; var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
@@ -72,6 +210,19 @@ function getLocalizedMessage(text) {
} }
} }
/**
* @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;
}
function generateUserID(length = 36) { function generateUserID(length = 36) {
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = ""; let result = "";
@@ -111,3 +262,10 @@ function getErrorMessage(statusCode) {
return errorMessage; return errorMessage;
} }
/**
* Is this Firefox (web-extensions)
*/
function isFirefox() {
return typeof(browser) !== "undefined";
}

View File

@@ -166,7 +166,7 @@ class SkipNotice {
noticeElement.appendChild(secondRow); noticeElement.appendChild(secondRow);
//get reference node //get reference node
let referenceNode = document.getElementById("movie_player"); let referenceNode = document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) { if (referenceNode == null) {
//for embeds //for embeds
let player = document.getElementById("player"); let player = document.getElementById("player");