diff --git a/config.json.example b/config.json.example
index c5e8644459..13aebf15c4 100644
--- a/config.json.example
+++ b/config.json.example
@@ -1,7 +1,9 @@
{
"serverAddress": "https://sponsor.ajay.app",
"testingServerAddress": "https://sponsor.ajay.app/test",
+ "fallbackServerAddress": "",
"serverAddressComment": "This specifies the default SponsorBlock server to connect to",
+ "fallbackAddressComment": "This specifies the server SponsorBlock will attempt to connect to if the primary one fails.",
"categoryList": ["sponsor", "selfpromo", "exclusive_access", "interaction", "poi_highlight", "intro", "outro", "preview", "filler", "chapter", "music_offtopic"],
"categorySupport": {
"sponsor": ["skip", "mute", "full"],
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 7a5dd69be1..f215b8f399 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -505,6 +505,12 @@
"customServerAddressDescription": {
"message": "The address SponsorBlock uses to make calls to the server.\nUnless you have your own server instance, this should not be changed."
},
+ "fallbackServerAddress": {
+ "message": "SponsorBlock Fallback Server Address"
+ },
+ "fallbackServerAddressDescription": {
+ "message": "The address SponsorBlock uses to make calls to the server if the main server is down."
+ },
"save": {
"message": "Save"
},
diff --git a/public/options/options.html b/public/options/options.html
index 9111294144..5553654733 100644
--- a/public/options/options.html
+++ b/public/options/options.html
@@ -554,6 +554,26 @@
__MSG_exportOptions__
+
+
+
+
+
__MSG_fallbackServerAddressDescription__
+
+
+
+ __MSG_save__
+
+
+
+ __MSG_reset__
+
+
+
diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx
index 84b69cbf5d..f8503938b4 100644
--- a/src/components/SponsorTimeEditComponent.tsx
+++ b/src/components/SponsorTimeEditComponent.tsx
@@ -696,11 +696,10 @@ class SponsorTimeEditComponent extends React.Component {
+ async fetchSuggestions(description: string, fallbackServer = false): Promise {
if (this.props.contentContainer().channelIDInfo.status !== ChannelIDStatus.Found) return;
-
this.fetchingSuggestions = true;
- const result = await utils.asyncRequestToServer("GET", "/api/chapterNames", {
+ const result = await utils.asyncRequestToServer("GET", "/api/chapterNames", fallbackServer, {
description,
channelID: this.props.contentContainer().channelIDInfo.id
});
@@ -713,7 +712,11 @@ class SponsorTimeEditComponent extends React.Component ({
vote,
@@ -1008,9 +1011,8 @@ async function sponsorsLookup(keepOldSubmissions = true) {
const extraRequestData: Record = {};
const hashParams = getHashParams();
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
-
const hashPrefix = (await utils.getHash(sponsorVideoID, 1)).slice(0, 4) as VideoID & HashedValue;
- const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
+ const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, fallbackServer, {
categories,
actionTypes: getEnabledActionTypes(showChapterMessage),
userAgent: `${chrome.runtime.id}`,
@@ -1019,7 +1021,6 @@ async function sponsorsLookup(keepOldSubmissions = true) {
// store last response status
lastResponseStatus = response?.status;
-
if (response?.ok) {
let recievedSegments: SponsorTime[] = JSON.parse(response.responseText)
?.filter((video) => video.videoID === sponsorVideoID)
@@ -1170,7 +1171,7 @@ function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
async function lockedCategoriesLookup(): Promise {
const hashPrefix = (await utils.getHash(sponsorVideoID, 1)).slice(0, 4);
- const response = await utils.asyncRequestToServer("GET", "/api/lockCategories/" + hashPrefix);
+ const response = await utils.asyncRequestToServer("GET", "/api/lockCategories/" + hashPrefix, fallbackServer);
if (response.ok) {
try {
@@ -1192,9 +1193,16 @@ function retryFetch(errorCode: number): void {
return;
}
+ fallbackServer = retryCount % 2 === 0
retryCount++;
-
- const delay = errorCode === 404 ? (30000 + Math.random() * 30000) : (2000 + Math.random() * 10000);
+ let delay: number;
+ switch (errorCode) {
+ case 404:
+ delay = (30000 + Math.random() * 30000);
+ break;
+ default:
+ delay = retryCount <= 2 ? 0 : (2000 + Math.random() * 10000);
+ }
retryFetchTimeout = setTimeout(() => {
if (sponsorVideoID && sponsorTimes?.length === 0
|| sponsorTimes.every((segment) => segment.source !== SponsorSourceType.Server)) {
@@ -1630,7 +1638,7 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
counted = true;
}
- if (fullSkip) utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
+ if (fullSkip) utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID, false);
}
}
}
@@ -2236,7 +2244,7 @@ async function sendSubmitMessage() {
}
}
- const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
+ const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", false, {
videoID: sponsorVideoID,
userID: Config.config.userID,
segments: sponsorTimesSubmitting,
diff --git a/src/options.ts b/src/options.ts
index e963a683af..b36b359206 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -175,7 +175,8 @@ async function init() {
textChangeSetButton.addEventListener("click", async () => {
// See if anything extra must be done
switch (option) {
- case "serverAddress": {
+ case "serverAddress":
+ case "fallbackServerAddress": {
const result = validateServerAddress(textChangeInput.value);
if (result !== null) {
@@ -531,8 +532,8 @@ function activatePrivateTextChange(element: HTMLElement) {
switch (option) {
case "userID":
if (Config.config[option]) {
- utils.asyncRequestToServer("GET", "/api/userInfo", {
- publicUserID: utils.getHash(Config.config[option]),
+ utils.asyncRequestToServer("GET", "/api/userInfo", false, {
+ userID: Config.config[option],
values: ["warnings", "banned"]
}).then((result) => {
const userInfo = JSON.parse(result.responseText);
@@ -672,4 +673,4 @@ function copyDebugOutputToClipboard() {
function isIncognitoAllowed(): Promise {
return new Promise((resolve) => chrome.extension.isAllowedIncognitoAccess(resolve));
-}
+}
\ No newline at end of file
diff --git a/src/popup.ts b/src/popup.ts
index 3584fba5b0..ada65c7217 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -282,7 +282,7 @@ async function runThePopup(messageListener?: MessageListener): Promise {
const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions"];
if (!Config.config.payments.freeAccess && !noRefreshFetchingChaptersAllowed()) values.push("freeChaptersAccess");
- utils.asyncRequestToServer("GET", "/api/userInfo", {
+ utils.asyncRequestToServer("GET", "/api/userInfo", false,{
publicUserID: await utils.getHash(Config.config.userID),
values
}).then((res) => {
diff --git a/src/utils.ts b/src/utils.ts
index 1ee5781d7c..d22941493a 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -319,7 +319,6 @@ export default class Utils {
return selection;
}
}
- return { name: "None", option: 0} as CategorySelection;
}
/**
@@ -365,11 +364,17 @@ export default class Utils {
*
* @param type The request type. "GET", "POST", etc.
* @param address The address to add to the SponsorBlock server address
+ * @param fallback Use the fallback server URL if true.
* @param callback
*/
- async asyncRequestToServer(type: string, address: string, data = {}): Promise {
- const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
-
+ async asyncRequestToServer(type: string, address: string, fallback = false, data = {}): Promise {
+ let serverAddress = Config.config.serverAddress;
+ if (Config.config.testingServer){
+ serverAddress = CompileConfig.testingServerAddress;
+ } else if (fallback && Config.config.fallbackServerAddress) {
+ serverAddress = Config.config.fallbackServerAddress;
+ }
+
return await (this.asyncRequestToCustomServer(type, serverAddress + address, data));
}
diff --git a/src/utils/licenseKey.ts b/src/utils/licenseKey.ts
index 77eca21d52..f984927462 100644
--- a/src/utils/licenseKey.ts
+++ b/src/utils/licenseKey.ts
@@ -5,7 +5,7 @@ import * as CompileConfig from "../../config.json";
const utils = new Utils();
export async function checkLicenseKey(licenseKey: string): Promise {
- const result = await utils.asyncRequestToServer("GET", "/api/verifyToken", {
+ const result = await utils.asyncRequestToServer("GET", "/api/verifyToken", false,{
licenseKey
});
@@ -15,7 +15,7 @@ export async function checkLicenseKey(licenseKey: string): Promise {
Config.config.showChapterInfoMessage = false;
Config.config.payments.lastCheck = Date.now();
Config.forceSyncUpdate("payments");
-
+
return true;
}
} catch (e) { } //eslint-disable-line no-empty
@@ -43,7 +43,7 @@ export async function fetchingChaptersAllowed(): Promise {
return licensePromise;
}
}
-
+
if (Config.config.payments.chaptersAllowed) return true;
if (Config.config.payments.lastCheck === 0 && Date.now() - Config.config.payments.lastFreeCheck > 2 * 24 * 60 * 60 * 1000) {
@@ -51,9 +51,9 @@ export async function fetchingChaptersAllowed(): Promise {
Config.forceSyncUpdate("payments");
// Check for free access if no license key, and it is the first time
- const result = await utils.asyncRequestToServer("GET", "/api/userInfo", {
+ const result = await utils.asyncRequestToServer("GET", "/api/userInfo", false,{
value: "freeChaptersAccess",
- publicUserID: await utils.getHash(Config.config.userID)
+ userID: Config.config.userID
});
try {
@@ -66,7 +66,7 @@ export async function fetchingChaptersAllowed(): Promise {
Config.config.payments.chaptersAllowed = true;
Config.config.showChapterInfoMessage = false;
Config.forceSyncUpdate("payments");
-
+
return true;
}
}
@@ -74,4 +74,4 @@ export async function fetchingChaptersAllowed(): Promise {
}
return false;
-}
+}
\ No newline at end of file
diff --git a/src/utils/warnings.ts b/src/utils/warnings.ts
index 17e919bad2..775fafba9a 100644
--- a/src/utils/warnings.ts
+++ b/src/utils/warnings.ts
@@ -12,8 +12,8 @@ export interface ChatConfig {
}
export async function openWarningDialog(contentContainer: ContentContainer): Promise {
- const userInfo = await utils.asyncRequestToServer("GET", "/api/userInfo", {
- publicUserID: await utils.getHash(Config.config.userID),
+ const userInfo = await utils.asyncRequestToServer("GET", "/api/userInfo", false, {
+ userID: Config.config.userID,
values: ["warningReason"]
});
@@ -42,7 +42,7 @@ export async function openWarningDialog(contentContainer: ContentContainer): Pro
{
name: chrome.i18n.getMessage("warningConfirmButton"),
listener: async () => {
- const result = await utils.asyncRequestToServer("POST", "/api/warnUser", {
+ const result = await utils.asyncRequestToServer("POST", "/api/warnUser", false, {
userID: Config.config.userID,
enabled: false
});
@@ -63,4 +63,4 @@ export async function openWarningDialog(contentContainer: ContentContainer): Pro
export function openChat(config: ChatConfig): void {
window.open("https://chat.sponsor.ajay.app/#" + GenericUtils.objectToURI("", config, false));
-}
+}
\ No newline at end of file