aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAjay <[email protected]>2023-11-08 16:07:59 -0500
committerAjay <[email protected]>2023-11-08 16:07:59 -0500
commite722ded58a928989bb583ae266acf765c129592c (patch)
tree28d2dea746c1b764737775564e65ce10b01ef3d4
parent6d37180d005f8f1c7691e0567ea4802425ca7569 (diff)
downloadSponsorBlock-e722ded58a928989bb583ae266acf765c129592c.tar.gz
SponsorBlock-e722ded58a928989bb583ae266acf765c129592c.zip
Add dearrow promo based on title and remove old one
Also refactor requests out to seperate file
m---------maze-utils0
m---------public/_locales0
-rw-r--r--public/content.css7
-rw-r--r--src/components/SponsorTimeEditComponent.tsx6
-rw-r--r--src/config.ts4
-rw-r--r--src/content.ts57
-rw-r--r--src/dearrowPromotion.ts71
-rw-r--r--src/options.ts3
-rw-r--r--src/popup.ts5
-rw-r--r--src/utils.ts46
-rw-r--r--src/utils/requests.ts47
-rw-r--r--src/utils/videoLabels.ts3
-rw-r--r--src/utils/warnings.ts9
13 files changed, 153 insertions, 105 deletions
diff --git a/maze-utils b/maze-utils
-Subproject 92d368b051c1af360ab9db45215df274b71de8f
+Subproject 6bdc9402c6cf2a8c97e53e1c360744e12afd919
diff --git a/public/_locales b/public/_locales
-Subproject 6ff5f86e9aca64c2ad51aa13e39db1a040506f9
+Subproject 9ba877c006a9a78774e56713cc170f355d81a4a
diff --git a/public/content.css b/public/content.css
index 39051417..dc2b1ba1 100644
--- a/public/content.css
+++ b/public/content.css
@@ -742,6 +742,7 @@ input::-webkit-inner-spin-button {
color: white;
font-size: 12px;
z-index: 10000;
+ font-weight: normal;
}
.sponsorBlockTooltip a {
@@ -764,6 +765,12 @@ input::-webkit-inner-spin-button {
right: 50%;
}
+.sponsorBlockTooltip.sbTriangle.sbTopTriangle::after {
+ bottom: 100%;
+ top: unset;
+ border-color: transparent transparent rgba(28, 28, 28, 0.7) transparent;
+}
+
.sponsorBlockLockedColor {
color: #ffc83d !important;
}
diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx
index 191f8ce7..1b470a78 100644
--- a/src/components/SponsorTimeEditComponent.tsx
+++ b/src/components/SponsorTimeEditComponent.tsx
@@ -2,14 +2,12 @@ import * as React from "react";
import * as CompileConfig from "../../config.json";
import Config from "../config";
import { ActionType, Category, ChannelIDStatus, ContentContainer, SponsorTime } from "../types";
-import Utils from "../utils";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
import { RectangleTooltip } from "../render/RectangleTooltip";
import SelectorComponent, { SelectorOption } from "./SelectorComponent";
import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
-
-const utils = new Utils();
+import { asyncRequestToServer } from "../utils/requests";
export interface SponsorTimeEditProps {
index: number;
@@ -727,7 +725,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
if (this.props.contentContainer().channelIDInfo.status !== ChannelIDStatus.Found) return;
this.fetchingSuggestions = true;
- const result = await utils.asyncRequestToServer("GET", "/api/chapterNames", {
+ const result = await asyncRequestToServer("GET", "/api/chapterNames", {
description,
channelID: this.props.contentContainer().channelIDInfo.id
});
diff --git a/src/config.ts b/src/config.ts
index f13cd3ad..a753a36f 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -76,6 +76,7 @@ interface SBConfig {
deArrowInstalled: boolean;
showDeArrowPromotion: boolean;
showDeArrowInSettings: boolean;
+ shownDeArrowPromotion: boolean;
showZoomToFillError2: boolean;
cleanPopup: boolean;
@@ -318,8 +319,9 @@ const syncDefaults = {
showSegmentFailedToFetchWarning: true,
allowScrollingToEdit: true,
deArrowInstalled: false,
- showDeArrowPromotion: false,
+ showDeArrowPromotion: true,
showDeArrowInSettings: true,
+ shownDeArrowPromotion: false,
showZoomToFillError2: true,
cleanPopup: false,
diff --git a/src/content.ts b/src/content.ts
index 9fbe4cb6..2804dc4c 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -28,7 +28,7 @@ import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isV
import { CategoryPill } from "./render/CategoryPill";
import { AnimationUtils } from "./utils/animationUtils";
import { GenericUtils } from "./utils/genericUtils";
-import { logDebug } from "./utils/logger";
+import { logDebug, logWarn } from "./utils/logger";
import { importTimes } from "./utils/exporter";
import { ChapterVote } from "./render/ChapterVote";
import { openWarningDialog } from "./utils/warnings";
@@ -36,17 +36,17 @@ import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube } from "../maze-utils/src/video";
import { Keybind, StorageChangesObject, isSafari, keybindEquals } from "../maze-utils/src/config";
-import { findValidElement, waitForElement } from "../maze-utils/src/dom"
+import { findValidElement } from "../maze-utils/src/dom"
import { getHash, HashedValue } from "../maze-utils/src/hash";
import { generateUserID } from "../maze-utils/src/setup";
import { updateAll } from "../maze-utils/src/thumbnailManagement";
import { setupThumbnailListener } from "./utils/thumbnails";
import * as documentScript from "../dist/js/document.js";
-import { Tooltip } from "./render/Tooltip";
-import { isDeArrowInstalled } from "./utils/crossExtension";
import { runCompatibilityChecks } from "./utils/compatibility";
import { cleanPage } from "./utils/pageCleaner";
import { addCleanupListener } from "../maze-utils/src/cleanup";
+import { hideDeArrowPromotion, tryShowingDeArrowPromotion } from "./dearrowPromotion";
+import { asyncRequestToServer } from "./utils/requests";
cleanPage();
@@ -57,43 +57,6 @@ utils.wait(() => Config.isReady(), 5000, 10).then(() => {
addCSS();
setCategoryColorCSSVariables();
- // DeArrow promotion
- setTimeout(async () => {
- if (document.URL === "https://www.youtube.com/"
- && Config.config.showDeArrowPromotion
- && Config.config.showUpsells
- && Config.config.showNewFeaturePopups
- && (Config.config.skipCount > 30 || !Config.config.trackViewCount)
- && Math.random() < 0.05) {
-
- if (!await isDeArrowInstalled()) {
- const element = await waitForElement("#contents") as HTMLElement;
- if (element) {
- Config.config.showDeArrowPromotion = false;
-
- new Tooltip({
- text: chrome.i18n.getMessage("DeArrowPromotionMessage2"),
- linkOnClick: () => window.open("https://dearrow.ajay.app"),
- referenceNode: element,
- prependElement: element.firstElementChild as HTMLElement,
- timeout: 15000,
- positionRealtive: false,
- containerAbsolute: true,
- bottomOffset: "inherit",
- topOffset: "-82px",
- leftOffset: "0",
- rightOffset: "0",
- displayTriangle: false,
- center: true,
- opacity: 1
- });
- }
- } else {
- Config.config.showDeArrowPromotion = false;
- }
- }
- }, 5000);
-
runCompatibilityChecks();
});
@@ -440,6 +403,8 @@ function resetValues() {
for (let i = 0; i < skipNotices.length; i++) {
skipNotices.pop()?.close();
}
+
+ hideDeArrowPromotion();
}
function videoIDChange(): void {
@@ -480,6 +445,8 @@ function videoIDChange(): void {
// Clear unsubmitted segments from the previous video
sponsorTimesSubmitting = [];
updateSponsorTimesSubmitting();
+
+ tryShowingDeArrowPromotion().catch(logWarn);
}
function handleMobileControlsMutations(): void {
@@ -1112,7 +1079,7 @@ async function sponsorsLookup(keepOldSubmissions = true) {
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
const hashPrefix = (await getHash(getVideoID(), 1)).slice(0, 4) as VideoID & HashedValue;
- const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
+ const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
categories,
actionTypes: getEnabledActionTypes(),
userAgent: `${chrome.runtime.id}`,
@@ -1252,7 +1219,7 @@ function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
async function lockedCategoriesLookup(): Promise<void> {
const hashPrefix = (await getHash(getVideoID(), 1)).slice(0, 4);
- const response = await utils.asyncRequestToServer("GET", "/api/lockCategories/" + hashPrefix);
+ const response = await asyncRequestToServer("GET", "/api/lockCategories/" + hashPrefix);
if (response.ok) {
try {
@@ -1646,7 +1613,7 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
counted = true;
}
- if (fullSkip) utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
+ if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
}
}
}
@@ -2282,7 +2249,7 @@ async function sendSubmitMessage() {
}
}
- const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
+ const response = await asyncRequestToServer("POST", "/api/skipSegments", {
videoID: getVideoID(),
userID: Config.config.userID,
segments: sponsorTimesSubmitting,
diff --git a/src/dearrowPromotion.ts b/src/dearrowPromotion.ts
new file mode 100644
index 00000000..208c627f
--- /dev/null
+++ b/src/dearrowPromotion.ts
@@ -0,0 +1,71 @@
+import { waitFor } from "../maze-utils/src";
+import { getYouTubeTitleNode } from "../maze-utils/src/elements";
+import { getHash } from "../maze-utils/src/hash";
+import { getVideoID, isOnInvidious, isOnMobileYouTube } from "../maze-utils/src/video";
+import Config from "./config";
+import { Tooltip } from "./render/Tooltip";
+import { isDeArrowInstalled } from "./utils/crossExtension";
+import { isVisible } from "./utils/pageUtils";
+import { asyncRequestToServer } from "./utils/requests";
+
+let tooltip: Tooltip = null;
+export async function tryShowingDeArrowPromotion() {
+ if (Config.config.showDeArrowPromotion
+ && !isOnMobileYouTube()
+ && !isOnInvidious()
+ && document.URL.includes("watch")
+ && Config.config.showUpsells
+ && Config.config.showNewFeaturePopups
+ && (Config.config.skipCount > 30 || !Config.config.trackViewCount)) {
+
+ if (!await isDeArrowInstalled()) {
+ try {
+ const element = await waitFor(() => getYouTubeTitleNode(), 5000, 500, (e) => isVisible(e)) as HTMLElement;
+ if (element && element.innerText && badTitle(element.innerText)) {
+ const hashPrefix = (await getHash(getVideoID(), 1)).slice(0, 4);
+ const deArrowData = await asyncRequestToServer("GET", "/api/branding/" + hashPrefix);
+ if (!deArrowData.ok) return;
+
+ const deArrowDataJson = JSON.parse(deArrowData.responseText);
+ const title = deArrowDataJson?.[getVideoID()]?.titles?.[0];
+ if (title && title.title && (title.locked || title.votes > 0)) {
+ Config.config.showDeArrowPromotion = false;
+
+ tooltip = new Tooltip({
+ text: chrome.i18n.getMessage("DeArrowTitleReplacementSuggestion") + "\n\n" + title.title,
+ linkOnClick: () => {
+ window.open("https://dearrow.ajay.app");
+ Config.config.shownDeArrowPromotion = true;
+ },
+ referenceNode: element,
+ prependElement: element.firstElementChild as HTMLElement,
+ timeout: 15000,
+ positionRealtive: false,
+ containerAbsolute: true,
+ bottomOffset: "inherit",
+ topOffset: "55px",
+ leftOffset: "0",
+ rightOffset: "0",
+ topTriangle: true,
+ center: true,
+ opacity: 1
+ });
+ }
+ }
+ } catch { } // eslint-disable-line no-empty
+ } else {
+ Config.config.showDeArrowPromotion = false;
+ }
+ }
+}
+
+/**
+ * Two upper case words (at least 2 letters long)
+ */
+function badTitle(title: string): boolean {
+ return !!title.match(/\p{Lu}{2,} \p{Lu}{2,}[.!? ]/u);
+}
+
+export function hideDeArrowPromotion(): void {
+ if (tooltip) tooltip.close();
+} \ No newline at end of file
diff --git a/src/options.ts b/src/options.ts
index 8acdd86e..6b088538 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -18,6 +18,7 @@ import { StorageChangesObject } from "../maze-utils/src/config";
import { getHash } from "../maze-utils/src/hash";
import { isFirefoxOrSafari } from "../maze-utils/src";
import { isDeArrowInstalled } from "./utils/crossExtension";
+import { asyncRequestToServer } from "./utils/requests";
const utils = new Utils();
let embed = false;
@@ -567,7 +568,7 @@ function activatePrivateTextChange(element: HTMLElement) {
switch (option) {
case "userID":
if (Config.config[option]) {
- utils.asyncRequestToServer("GET", "/api/userInfo", {
+ asyncRequestToServer("GET", "/api/userInfo", {
publicUserID: getHash(Config.config[option]),
values: ["warnings", "banned"]
}).then((result) => {
diff --git a/src/popup.ts b/src/popup.ts
index 41fb9210..21267afe 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -27,6 +27,7 @@ import GenericNotice from "./render/GenericNotice";
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
import { StorageChangesObject } from "../maze-utils/src/config";
import { getHash } from "../maze-utils/src/hash";
+import { asyncRequestToServer, sendRequestToServer } from "./utils/requests";
const utils = new Utils();
@@ -295,7 +296,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions"];
- utils.asyncRequestToServer("GET", "/api/userInfo", {
+ asyncRequestToServer("GET", "/api/userInfo", {
publicUserID: await getHash(Config.config.userID),
values
}).then((res) => {
@@ -818,7 +819,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.setUsernameStatus.style.display = "unset";
PageElements.setUsernameStatus.innerText = chrome.i18n.getMessage("Loading");
- utils.sendRequestToServer("POST", "/api/setUsername?userID=" + Config.config.userID + "&username=" + PageElements.usernameInput.value, function (response) {
+ sendRequestToServer("POST", "/api/setUsername?userID=" + Config.config.userID + "&username=" + PageElements.usernameInput.value, function (response) {
if (response.status == 200) {
//submitted
PageElements.submitUsername.style.display = "none";
diff --git a/src/utils.ts b/src/utils.ts
index 7b3a3841..c96a7772 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -2,10 +2,8 @@ import Config, { VideoDownvotes } from "./config";
import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types";
import { getHash, HashedValue } from "../maze-utils/src/hash";
-import * as CompileConfig from "../config.json";
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
import { findValidElementFromSelector } from "../maze-utils/src/dom";
-import { FetchResponse, sendRequestToCustomServer } from "../maze-utils/src/background-request-proxy"
import { isSafari } from "../maze-utils/src/config";
export default class Utils {
@@ -240,50 +238,6 @@ export default class Utils {
return permissionRegex;
}
- /**
- * Sends a request to a custom server
- *
- * @param type The request type. "GET", "POST", etc.
- * @param address The address to add to the SponsorBlock server address
- * @param callback
- */
- asyncRequestToCustomServer(type: string, url: string, data = {}): Promise<FetchResponse> {
- return sendRequestToCustomServer(type, url, data);
- }
-
- /**
- * Sends a request to the SponsorBlock server with address added as a query
- *
- * @param type The request type. "GET", "POST", etc.
- * @param address The address to add to the SponsorBlock server address
- * @param callback
- */
- async asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> {
- const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
-
- return await (this.asyncRequestToCustomServer(type, serverAddress + address, data));
- }
-
- /**
- * Sends a request to the SponsorBlock server with address added as a query
- *
- * @param type The request type. "GET", "POST", etc.
- * @param address The address to add to the SponsorBlock server address
- * @param callback
- */
- sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void): void {
- const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
-
- // Ask the background script to do the work
- chrome.runtime.sendMessage({
- message: "sendRequest",
- type,
- url: serverAddress + address
- }, (response) => {
- callback(response);
- });
- }
-
findReferenceNode(): HTMLElement {
const selectors = [
"#player-container-id", // Mobile YouTube
diff --git a/src/utils/requests.ts b/src/utils/requests.ts
new file mode 100644
index 00000000..8c160eb0
--- /dev/null
+++ b/src/utils/requests.ts
@@ -0,0 +1,47 @@
+import Config from "../config";
+import * as CompileConfig from "../../config.json";
+import { FetchResponse, sendRequestToCustomServer } from "../../maze-utils/src/background-request-proxy";
+
+/**
+ * Sends a request to a custom server
+ *
+ * @param type The request type. "GET", "POST", etc.
+ * @param address The address to add to the SponsorBlock server address
+ * @param callback
+ */
+export function asyncRequestToCustomServer(type: string, url: string, data = {}): Promise<FetchResponse> {
+ return sendRequestToCustomServer(type, url, data);
+}
+
+/**
+ * Sends a request to the SponsorBlock server with address added as a query
+ *
+ * @param type The request type. "GET", "POST", etc.
+ * @param address The address to add to the SponsorBlock server address
+ * @param callback
+ */
+export async function asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> {
+ const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
+
+ return await (asyncRequestToCustomServer(type, serverAddress + address, data));
+}
+
+/**
+ * Sends a request to the SponsorBlock server with address added as a query
+ *
+ * @param type The request type. "GET", "POST", etc.
+ * @param address The address to add to the SponsorBlock server address
+ * @param callback
+ */
+export function sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void): void {
+ const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
+
+ // Ask the background script to do the work
+ chrome.runtime.sendMessage({
+ message: "sendRequest",
+ type,
+ url: serverAddress + address
+ }, (response) => {
+ callback(response);
+ });
+} \ No newline at end of file
diff --git a/src/utils/videoLabels.ts b/src/utils/videoLabels.ts
index 731bfd33..82af788c 100644
--- a/src/utils/videoLabels.ts
+++ b/src/utils/videoLabels.ts
@@ -2,6 +2,7 @@ import { Category, CategorySkipOption, VideoID } from "../types";
import { getHash } from "../../maze-utils/src/hash";
import Utils from "../utils";
import { logWarn } from "./logger";
+import { asyncRequestToServer } from "./requests";
const utils = new Utils();
@@ -20,7 +21,7 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
return cachedEntry;
}
- const response = await utils.asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
+ const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
if (response.status !== 200) {
// No video labels or server down
labelCache[hashPrefix] = {
diff --git a/src/utils/warnings.ts b/src/utils/warnings.ts
index 72062699..e35c703c 100644
--- a/src/utils/warnings.ts
+++ b/src/utils/warnings.ts
@@ -3,8 +3,7 @@ import { getHash } from "../../maze-utils/src/hash";
import Config from "../config";
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
import { ContentContainer } from "../types";
-import Utils from "../utils";
-const utils = new Utils();
+import { asyncRequestToServer } from "./requests";
export interface ChatConfig {
displayName: string;
@@ -13,14 +12,14 @@ export interface ChatConfig {
}
export async function openWarningDialog(contentContainer: ContentContainer): Promise<void> {
- const userInfo = await utils.asyncRequestToServer("GET", "/api/userInfo", {
+ const userInfo = await asyncRequestToServer("GET", "/api/userInfo", {
publicUserID: await getHash(Config.config.userID),
values: ["warningReason"]
});
if (userInfo.ok) {
const warningReason = JSON.parse(userInfo.responseText)?.warningReason;
- const userNameData = await utils.asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID);
+ const userNameData = await asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID);
const userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : "";
const publicUserID = await getHash(Config.config.userID);
@@ -43,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 asyncRequestToServer("POST", "/api/warnUser", {
userID: Config.config.userID,
enabled: false
});