mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-17 21:18:56 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into react
# Conflicts: # public/_locales/en/messages.json # src/content.ts
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -67,10 +67,12 @@ jobs:
|
|||||||
uses: Shopify/upload-to-release@master
|
uses: Shopify/upload-to-release@master
|
||||||
with:
|
with:
|
||||||
args: builds/ChromeExtension.zip
|
args: builds/ChromeExtension.zip
|
||||||
|
name: ChromeExtension.zip
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Upload to release
|
- name: Upload to release
|
||||||
uses: Shopify/upload-to-release@master
|
uses: Shopify/upload-to-release@master
|
||||||
with:
|
with:
|
||||||
args: builds/FirefoxExtension.zip
|
args: builds/FirefoxExtension.zip
|
||||||
|
name: FirefoxExtension.zip
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "__MSG_Name__",
|
"short_name": "__MSG_Name__",
|
||||||
"version": "1.2.22",
|
"version": "1.2.24",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
|
|||||||
@@ -458,5 +458,26 @@
|
|||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
"message": "Edit"
|
"message": "Edit"
|
||||||
|
},
|
||||||
|
"copyDebugInformation": {
|
||||||
|
"message": "Copy Debug Information To Clipboard"
|
||||||
|
},
|
||||||
|
"copyDebugInformationFailed": {
|
||||||
|
"message": "Failed to write to clipboard"
|
||||||
|
},
|
||||||
|
"copyDebugInformationOptions": {
|
||||||
|
"message": "Copies information to the clipboard to be provided to a developer when raising a bug / when a developer requests it. Sensitive information such as your user ID, whitelisted channels, and custom server address have been removed. However it does contain information such as your useragent, browser, operating system, and extension version number. "
|
||||||
|
},
|
||||||
|
"copyDebugInformationComplete": {
|
||||||
|
"message": "The debug information has been copied to the clip board. Feel free to remove any information you would rather not share. Save this in a text file or paste into the bug report."
|
||||||
|
},
|
||||||
|
"theKey": {
|
||||||
|
"message": "The key"
|
||||||
|
},
|
||||||
|
"keyAlreadyUsedByYouTube": {
|
||||||
|
"message": "is already used by youtube. Please select another key."
|
||||||
|
},
|
||||||
|
"keyAlreadyUsed": {
|
||||||
|
"message": "is bound to another action. Please select another key."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,6 +334,20 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<div option-type="button-press" sync-option="copyDebugInformation" confirm-message="copyDebugInformation">
|
||||||
|
<div class="option-button trigger-button">
|
||||||
|
__MSG_copyDebugInformation__
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="small-description">__MSG_copyDebugInformationOptions__</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<div option-type="text-change" sync-option="serverAddress">
|
<div option-type="text-change" sync-option="serverAddress">
|
||||||
<label class="text-label-container">
|
<label class="text-label-container">
|
||||||
<div>__MSG_customServerAddress__</div>
|
<div>__MSG_customServerAddress__</div>
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ interface SBObject {
|
|||||||
defaults: SBConfig;
|
defaults: SBConfig;
|
||||||
localConfig: SBConfig;
|
localConfig: SBConfig;
|
||||||
config: SBConfig;
|
config: SBConfig;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
encodeStoredItem<T>(data: T): T | Array<any>;
|
||||||
|
convertJSON(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows a SBMap to be conveted into json form
|
// Allows a SBMap to be conveted into json form
|
||||||
@@ -119,7 +123,11 @@ var Config: SBObject = {
|
|||||||
mobileUpdateShowCount: 0
|
mobileUpdateShowCount: 0
|
||||||
},
|
},
|
||||||
localConfig: null,
|
localConfig: null,
|
||||||
config: null
|
config: null,
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
encodeStoredItem,
|
||||||
|
convertJSON
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function setup
|
// Function setup
|
||||||
@@ -130,7 +138,7 @@ var Config: SBObject = {
|
|||||||
*
|
*
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
function encodeStoredItem(data) {
|
function encodeStoredItem<T>(data: T): T | Array<any> {
|
||||||
// if data is SBMap convert to json for storing
|
// if data is SBMap convert to json for storing
|
||||||
if(!(data instanceof SBMap)) return data;
|
if(!(data instanceof SBMap)) return data;
|
||||||
return Array.from(data.entries());
|
return Array.from(data.entries());
|
||||||
@@ -142,7 +150,7 @@ function encodeStoredItem(data) {
|
|||||||
*
|
*
|
||||||
* @param {*} data
|
* @param {*} data
|
||||||
*/
|
*/
|
||||||
function decodeStoredItem(id: string, data) {
|
function decodeStoredItem<T>(id: string, data: T): T | SBMap<string, any> {
|
||||||
if (!Config.defaults[id]) return data;
|
if (!Config.defaults[id]) return data;
|
||||||
|
|
||||||
if (Config.defaults[id] instanceof SBMap) {
|
if (Config.defaults[id] instanceof SBMap) {
|
||||||
@@ -239,7 +247,7 @@ function resetConfig() {
|
|||||||
Config.config = Config.defaults;
|
Config.config = Config.defaults;
|
||||||
};
|
};
|
||||||
|
|
||||||
function convertJSON() {
|
function convertJSON(): void {
|
||||||
Object.keys(Config.localConfig).forEach(key => {
|
Object.keys(Config.localConfig).forEach(key => {
|
||||||
Config.localConfig[key] = decodeStoredItem(key, Config.localConfig[key]);
|
Config.localConfig[key] = decodeStoredItem(key, Config.localConfig[key]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ var UUIDs = [];
|
|||||||
var sponsorVideoID = null;
|
var sponsorVideoID = null;
|
||||||
|
|
||||||
// Skips are scheduled to ensure precision.
|
// Skips are scheduled to ensure precision.
|
||||||
// Skips are rescheduled every seeked event.
|
// Skips are rescheduled every seeking event.
|
||||||
// Skips are canceled every seeking event
|
// Skips are canceled every seeking event
|
||||||
var currentSkipSchedule: NodeJS.Timeout = null;
|
var currentSkipSchedule: NodeJS.Timeout = null;
|
||||||
var seekListenerSetUp = false
|
var seekListenerSetUp = false
|
||||||
@@ -39,6 +39,9 @@ var sponsorSkipped = [];
|
|||||||
//the video
|
//the video
|
||||||
var video: HTMLVideoElement;
|
var video: HTMLVideoElement;
|
||||||
|
|
||||||
|
/** The last time this video was seeking to */
|
||||||
|
var lastVideoTime: number = null;
|
||||||
|
|
||||||
var onInvidious;
|
var onInvidious;
|
||||||
var onMobileYouTube;
|
var onMobileYouTube;
|
||||||
|
|
||||||
@@ -271,8 +274,13 @@ function resetValues() {
|
|||||||
//reset sponsor data found check
|
//reset sponsor data found check
|
||||||
sponsorDataFound = false;
|
sponsorDataFound = false;
|
||||||
|
|
||||||
|
if (switchingVideos === null) {
|
||||||
|
// When first loading a video, it is not switching videos
|
||||||
|
switchingVideos = false;
|
||||||
|
} else {
|
||||||
switchingVideos = true;
|
switchingVideos = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function videoIDChange(id) {
|
async function videoIDChange(id) {
|
||||||
//if the id has not changed return
|
//if the id has not changed return
|
||||||
@@ -483,6 +491,10 @@ function startSponsorSchedule(currentTime?: number): void {
|
|||||||
let forcedSkipTime: number = null;
|
let forcedSkipTime: number = null;
|
||||||
|
|
||||||
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
|
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
|
||||||
|
// Double check that the videoID is correct
|
||||||
|
// TODO: Remove this bug catching if statement when the bug is found
|
||||||
|
let currentVideoID = getYouTubeVideoID(document.URL);
|
||||||
|
if (currentVideoID == sponsorVideoID) {
|
||||||
skipToTime(video, skipInfo.index, skipInfo.array, skipInfo.openNotice);
|
skipToTime(video, skipInfo.index, skipInfo.array, skipInfo.openNotice);
|
||||||
|
|
||||||
if (Config.config.disableAutoSkip) {
|
if (Config.config.disableAutoSkip) {
|
||||||
@@ -490,6 +502,14 @@ function startSponsorSchedule(currentTime?: number): void {
|
|||||||
} else {
|
} else {
|
||||||
forcedSkipTime = skipTime[1];
|
forcedSkipTime = skipTime[1];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Something has really gone wrong
|
||||||
|
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
|
||||||
|
console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID);
|
||||||
|
|
||||||
|
// Video ID change occured
|
||||||
|
videoIDChange(currentVideoID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startSponsorSchedule(forcedSkipTime);
|
startSponsorSchedule(forcedSkipTime);
|
||||||
@@ -540,12 +560,27 @@ function sponsorsLookup(id: string, channelIDPromise?) {
|
|||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
video.addEventListener('seeked', () => {
|
video.addEventListener('seeking', () => {
|
||||||
if (!video.paused) startSponsorSchedule();
|
// Reset lastCheckVideoTime
|
||||||
|
lastCheckVideoTime = -1
|
||||||
|
lastCheckTime = 0;
|
||||||
|
|
||||||
|
lastVideoTime = video.currentTime;
|
||||||
|
|
||||||
|
if (!video.paused){
|
||||||
|
startSponsorSchedule();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
video.addEventListener('ratechange', () => startSponsorSchedule());
|
video.addEventListener('ratechange', () => startSponsorSchedule());
|
||||||
video.addEventListener('seeking', cancelSponsorSchedule);
|
video.addEventListener('pause', () => {
|
||||||
video.addEventListener('pause', cancelSponsorSchedule);
|
// Reset lastCheckVideoTime
|
||||||
|
lastCheckVideoTime = -1;
|
||||||
|
lastCheckTime = 0;
|
||||||
|
|
||||||
|
lastVideoTime = video.currentTime;
|
||||||
|
|
||||||
|
cancelSponsorSchedule();
|
||||||
|
});
|
||||||
|
|
||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
}
|
}
|
||||||
@@ -600,26 +635,26 @@ function sponsorsLookup(id: string, channelIDPromise?) {
|
|||||||
UUIDs = smallUUIDs;
|
UUIDs = smallUUIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if there are any zero second sponsors
|
if (!switchingVideos) {
|
||||||
let zeroSecondSponsor = false;
|
// See if there are any starting sponsors
|
||||||
|
let startingSponsor: number = -1;
|
||||||
for (const time of sponsorTimes) {
|
for (const time of sponsorTimes) {
|
||||||
if (time[0] <= 0) {
|
if (time[0] <= video.currentTime && time[0] > startingSponsor && time[1] > video.currentTime) {
|
||||||
zeroSecondSponsor = true;
|
startingSponsor = time[0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!zeroSecondSponsor) {
|
if (!startingSponsor) {
|
||||||
for (const time of sponsorTimesSubmitting) {
|
for (const time of sponsorTimesSubmitting) {
|
||||||
if (time[0] <= 0) {
|
if (time[0] <= video.currentTime && time[0] > startingSponsor && time[1] > video.currentTime) {
|
||||||
zeroSecondSponsor = true;
|
startingSponsor = time[0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video.paused && !switchingVideos) {
|
if (startingSponsor !== -1) {
|
||||||
if (zeroSecondSponsor) {
|
startSponsorSchedule(startingSponsor);
|
||||||
startSponsorSchedule(0);
|
|
||||||
} else {
|
} else {
|
||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
}
|
}
|
||||||
|
|||||||
100
src/options.ts
100
src/options.ts
@@ -1,4 +1,6 @@
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
|
import * as CompileConfig from "../config.json";
|
||||||
|
|
||||||
// Make the config public for debugging purposes
|
// Make the config public for debugging purposes
|
||||||
(<any> window).SB = Config;
|
(<any> window).SB = Config;
|
||||||
|
|
||||||
@@ -126,6 +128,16 @@ async function init() {
|
|||||||
invidiousInstanceAddInit(<HTMLElement> optionsElements[i], privateTextChangeOption);
|
invidiousInstanceAddInit(<HTMLElement> optionsElements[i], privateTextChangeOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "button-press":
|
||||||
|
let actionButton = optionsElements[i].querySelector(".trigger-button");
|
||||||
|
|
||||||
|
switch(optionsElements[i].getAttribute("sync-option")) {
|
||||||
|
case "copyDebugInformation":
|
||||||
|
actionButton.addEventListener("click", copyDebugOutputToClipboard);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "keybind-change":
|
case "keybind-change":
|
||||||
let keybindButton = optionsElements[i].querySelector(".trigger-button");
|
let keybindButton = optionsElements[i].querySelector(".trigger-button");
|
||||||
@@ -319,16 +331,39 @@ function activateKeybindChange(element: HTMLElement) {
|
|||||||
function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
||||||
var key = e.key;
|
var key = e.key;
|
||||||
|
|
||||||
let button = element.querySelector(".trigger-button");
|
if (["Shift", "Control", "Meta", "Alt", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab"].indexOf(key) !== -1) {
|
||||||
|
|
||||||
// cancel setting a keybind
|
// Wait for more
|
||||||
if (key === "Escape") {
|
document.addEventListener("keydown", (e) => keybindKeyPressed(element, e), {once: true});
|
||||||
element.querySelector(".option-hidden-section").classList.add("hidden");
|
} else {
|
||||||
button.classList.remove("disabled");
|
let button: HTMLElement = element.querySelector(".trigger-button");
|
||||||
|
let option = element.getAttribute("sync-option");
|
||||||
|
|
||||||
|
// Don't allow keys which are already listened for by youtube
|
||||||
|
let restrictedKeys = "1234567890,.jklftcibmJKLFTCIBMNP/<> -+";
|
||||||
|
if (restrictedKeys.indexOf(key) !== -1 ) {
|
||||||
|
closeKeybindOption(element, button);
|
||||||
|
|
||||||
|
alert(chrome.i18n.getMessage("theKey") + " " + key + " " + chrome.i18n.getMessage("keyAlreadyUsedByYouTube"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let option = element.getAttribute("sync-option");
|
// Make sure keybind isn't used by the other listener
|
||||||
|
// TODO: If other keybindings are going to be added, we need a better way to find the other keys used.
|
||||||
|
let otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
|
||||||
|
if (key === otherKeybind) {
|
||||||
|
closeKeybindOption(element, button);
|
||||||
|
|
||||||
|
alert(chrome.i18n.getMessage("theKey") + " " + key + " " + chrome.i18n.getMessage("keyAlreadyUsed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel setting a keybind
|
||||||
|
if (key === "Escape") {
|
||||||
|
closeKeybindOption(element, button);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Config.config[option] = key;
|
Config.config[option] = key;
|
||||||
|
|
||||||
@@ -340,6 +375,18 @@ function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
|||||||
|
|
||||||
button.classList.remove("disabled");
|
button.classList.remove("disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the menu for editing the keybind
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* @param button
|
||||||
|
*/
|
||||||
|
function closeKeybindOption(element: HTMLElement, button: HTMLElement) {
|
||||||
|
element.querySelector(".option-hidden-section").classList.add("hidden");
|
||||||
|
button.classList.remove("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will trigger the textbox to appear to be able to change an option's text.
|
* Will trigger the textbox to appear to be able to change an option's text.
|
||||||
@@ -367,7 +414,12 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||||||
// See if anything extra must be done
|
// See if anything extra must be done
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case "*":
|
case "*":
|
||||||
result = JSON.stringify(Config.localConfig);
|
let jsonData = JSON.parse(JSON.stringify(Config.localConfig));
|
||||||
|
|
||||||
|
// Fix sponsorTimes data as it is destroyed from the JSON stringify
|
||||||
|
jsonData.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes);
|
||||||
|
|
||||||
|
result = JSON.stringify(jsonData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +439,9 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||||||
for (const key in newConfig) {
|
for (const key in newConfig) {
|
||||||
Config.config[key] = newConfig[key];
|
Config.config[key] = newConfig[key];
|
||||||
}
|
}
|
||||||
|
Config.convertJSON();
|
||||||
|
|
||||||
|
// Reload options on page
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (newConfig.supportInvidious) {
|
if (newConfig.supportInvidious) {
|
||||||
@@ -433,3 +487,35 @@ function validateServerAddress(input: string): string {
|
|||||||
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyDebugOutputToClipboard() {
|
||||||
|
// Build output debug information object
|
||||||
|
let output = {
|
||||||
|
debug: {
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
platform: navigator.platform,
|
||||||
|
language: navigator.language,
|
||||||
|
extensionVersion: chrome.runtime.getManifest().version
|
||||||
|
},
|
||||||
|
config: JSON.parse(JSON.stringify(Config.localConfig)) // Deep clone config object
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fix sponsorTimes data as it is destroyed from the JSON stringify
|
||||||
|
output.config.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes);
|
||||||
|
|
||||||
|
// Sanitise sensitive user config values
|
||||||
|
delete output.config.userID;
|
||||||
|
output.config.serverAddress = (output.config.serverAddress === CompileConfig.serverAddress)
|
||||||
|
? "Default server address" : "Custom server address";
|
||||||
|
output.config.invidiousInstances = output.config.invidiousInstances.length;
|
||||||
|
output.config.whitelistedChannels = output.config.whitelistedChannels.length;
|
||||||
|
|
||||||
|
// Copy object to clipboard
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(output, null, 4))
|
||||||
|
.then(() => {
|
||||||
|
alert(chrome.i18n.getMessage("copyDebugInformationComplete"));
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(chrome.i18n.getMessage("copyDebugInformationFailed"));
|
||||||
|
});;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user