mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-25 00:48:26 +03:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52bd85b850 | ||
|
|
09e7c41479 | ||
|
|
bdcb2d05c7 | ||
|
|
a522e3065c | ||
|
|
72c62d0fa4 | ||
|
|
133ea360d7 | ||
|
|
e722ded58a | ||
|
|
6d37180d00 | ||
|
|
14d50b9e70 | ||
|
|
cfe314742d | ||
|
|
9a71e8bb8c | ||
|
|
dc2c7cc425 | ||
|
|
7bf3237b72 | ||
|
|
b48c854926 | ||
|
|
0bb7bef52c | ||
|
|
4ffa019c68 | ||
|
|
9c2007e0cf | ||
|
|
9176854d56 | ||
|
|
65c72d38ea | ||
|
|
6f54c8a731 | ||
|
|
ca7eb50a82 | ||
|
|
a7030fab9f | ||
|
|
0bb3528cde | ||
|
|
c8c141f5c9 | ||
|
|
88cfa023c9 | ||
|
|
41a2fc2cb3 | ||
|
|
0f0e404920 | ||
|
|
f34fe5a032 | ||
|
|
e4c9afecbd | ||
|
|
79e855a038 | ||
|
|
09a3a4e6d4 | ||
|
|
e271f2cbcc | ||
|
|
1cc4c18665 |
@@ -1 +1 @@
|
|||||||
["www.youtubekids.com","anontube.lvkaszus.pl","inv.bp.projectsegfau.lt","inv.makerlab.tech","inv.pistasjis.net","inv.tux.pizza","inv.zzls.xyz","invidious.asir.dev","invidious.flokinet.to","invidious.io.lol","invidious.lunar.icu","invidious.no-logs.com","invidious.privacydev.net","invidious.private.coffee","invidious.protokolla.fi","invidious.slipfox.xyz","invidious.tiekoetter.com","iv.ggtyler.dev","iv.melmac.space","iv.nboeck.de","onion.tube","vid.priv.au","vid.puffyan.us","yewtu.be","yt.artemislena.eu","yt.drgnz.club","yt.oelrichsgarcia.de"]
|
["www.youtubekids.com","anontube.lvkaszus.pl","inv.citw.lgbt","inv.in.projectsegfau.lt","inv.tux.pizza","inv.zzls.xyz","invidious.asir.dev","invidious.drgns.space","invidious.fdn.fr","invidious.flokinet.to","invidious.io.lol","invidious.lunar.icu","invidious.nerdvpn.de","invidious.no-logs.com","invidious.perennialte.ch","invidious.privacydev.net","invidious.private.coffee","invidious.projectsegfau.lt","invidious.protokolla.fi","invidious.slipfox.xyz","iv.datura.network","iv.ggtyler.dev","iv.melmac.space","iv.nboeck.de","onion.tube","vid.priv.au","vid.puffyan.us","yewtu.be","yt.artemislena.eu","yt.cdaut.de","yt.drgnz.club","yt.oelrichsgarcia.de"]
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "sponsorBlocker@ajay.app"
|
"id": "sponsorBlocker@ajay.app",
|
||||||
|
"strict_min_version": "48.0"
|
||||||
|
},
|
||||||
|
"gecko_android": {
|
||||||
|
"strict_min_version": "79.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.4.23",
|
"version": "5.4.27",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
|
|||||||
Submodule maze-utils updated: a984d11483...6bdc9402c6
Submodule public/_locales updated: eecd3eba65...b31f76dd29
@@ -742,6 +742,7 @@ input::-webkit-inner-spin-button {
|
|||||||
color: white;
|
color: white;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sponsorBlockTooltip a {
|
.sponsorBlockTooltip a {
|
||||||
@@ -764,6 +765,12 @@ input::-webkit-inner-spin-button {
|
|||||||
right: 50%;
|
right: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorBlockTooltip.sbTriangle.sbTopTriangle::after {
|
||||||
|
bottom: 100%;
|
||||||
|
top: unset;
|
||||||
|
border-color: transparent transparent rgba(28, 28, 28, 0.7) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.sponsorBlockLockedColor {
|
.sponsorBlockLockedColor {
|
||||||
color: #ffc83d !important;
|
color: #ffc83d !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,20 @@
|
|||||||
Come contribute, make some suggestions and help out on <a href="https://discord.gg/SponsorBlock">Discord</a> or on <a href="https://matrix.to/#/#sponsor:ajay.app?via=ajay.app&via=matrix.org&via=mozilla.org">Matrix</a>.
|
Come contribute, make some suggestions and help out on <a href="https://discord.gg/SponsorBlock">Discord</a> or on <a href="https://matrix.to/#/#sponsor:ajay.app?via=ajay.app&via=matrix.org&via=mozilla.org">Matrix</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<a href="https://dearrow.ajay.app"
|
||||||
|
target="_blank"
|
||||||
|
id="dearrow-link"
|
||||||
|
class="dearrow-link hidden"
|
||||||
|
rel="noreferrer">
|
||||||
|
<img src="/icons/dearrow.svg"/>
|
||||||
|
|
||||||
|
<span id="dearrow-link-text">
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<img src="/icons/close.png" class="close-button"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
<p style="margin-bottom: 0; margin-top: 0" class="bigText center">__MSG_helpPageReviewOptions__</p>
|
<p style="margin-bottom: 0; margin-top: 0" class="bigText center">__MSG_helpPageReviewOptions__</p>
|
||||||
|
|
||||||
<p class="smallText">
|
<p class="smallText">
|
||||||
|
|||||||
@@ -323,3 +323,32 @@ svg {
|
|||||||
background-color: var(--disabled);
|
background-color: var(--disabled);
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dearrow-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dearrow-link img {
|
||||||
|
width: 35px;
|
||||||
|
padding: 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
.dearrow-link .close-button {
|
||||||
|
opacity: 0;
|
||||||
|
width: 15px;
|
||||||
|
filter: invert(0.3);
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dearrow-link:hover .close-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -718,3 +718,15 @@ svg {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dearrow-link .close-button {
|
||||||
|
opacity: 0;
|
||||||
|
width: 15px;
|
||||||
|
filter: invert(0.3);
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dearrow-link:hover .close-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
@@ -75,6 +75,8 @@
|
|||||||
<span class="promotion-description">
|
<span class="promotion-description">
|
||||||
__MSG_DeArrowPromotionMessage__
|
__MSG_DeArrowPromotionMessage__
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<img src="/icons/close.png" class="close-button"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -357,6 +359,18 @@
|
|||||||
<div class="small-description">__MSG_showTimeWithSkipsDescription__</div>
|
<div class="small-description">__MSG_showTimeWithSkipsDescription__</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-type="toggle" data-sync="cleanPopup" data-no-safari="true">
|
||||||
|
<div class="switch-container">
|
||||||
|
<label class="switch">
|
||||||
|
<input id="cleanPopup" type="checkbox" checked>
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
<label class="switch-label" for="cleanPopup">
|
||||||
|
__MSG_cleanPopup__
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-type="toggle" data-sync="darkMode">
|
<div data-type="toggle" data-sync="darkMode">
|
||||||
<div class="switch-container">
|
<div class="switch-container">
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
|
|||||||
@@ -130,6 +130,7 @@
|
|||||||
top: 5px;
|
top: 5px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sbCloseButton:hover {
|
.sbCloseButton:hover {
|
||||||
@@ -260,19 +261,6 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Buttons that appear under a segment on click
|
|
||||||
*/
|
|
||||||
.voteButton {
|
|
||||||
height: 20px;
|
|
||||||
padding: 0 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voteButton:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Voted!" text that appears after voting on a segment
|
* "Voted!" text that appears after voting on a segment
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
__MSG_betaServerWarning__
|
__MSG_betaServerWarning__
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<header class="sbPopupLogo">
|
<header id="sbPopupLogo" class="sbPopupLogo">
|
||||||
<img src="icons/IconSponsorBlocker256px.png" alt="SponsorBlock" width="40" height="40" id="sponsorBlockPopupLogo">
|
<img src="icons/IconSponsorBlocker256px.png" alt="SponsorBlock" width="40" height="40" id="sponsorBlockPopupLogo">
|
||||||
<p class="u-mZ">SponsorBlock</p>
|
<p class="u-mZ">SponsorBlock</p>
|
||||||
</header>
|
</header>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Your Work box -->
|
<!-- Your Work box -->
|
||||||
<div class="sbYourWorkBox">
|
<div id="sbYourWorkBox" class="sbYourWorkBox">
|
||||||
<h1 class="sbHeader" style="padding: 8px 15px;">
|
<h1 class="sbHeader" style="padding: 8px 15px;">
|
||||||
__MSG_yourWork__
|
__MSG_yourWork__
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sponsorSkipNoticeParent {
|
.sponsorSkipNoticeParent {
|
||||||
min-width: 375px;
|
min-width: 390px;
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,3 +217,16 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Buttons that appear under a segment on click
|
||||||
|
*/
|
||||||
|
.voteButton {
|
||||||
|
height: 20px;
|
||||||
|
padding: 0 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voteButton:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
@@ -2,14 +2,12 @@ import * as React from "react";
|
|||||||
import * as CompileConfig from "../../config.json";
|
import * as CompileConfig from "../../config.json";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { ActionType, Category, ChannelIDStatus, ContentContainer, SponsorTime } from "../types";
|
import { ActionType, Category, ChannelIDStatus, ContentContainer, SponsorTime } from "../types";
|
||||||
import Utils from "../utils";
|
|
||||||
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
||||||
import { RectangleTooltip } from "../render/RectangleTooltip";
|
import { RectangleTooltip } from "../render/RectangleTooltip";
|
||||||
import SelectorComponent, { SelectorOption } from "./SelectorComponent";
|
import SelectorComponent, { SelectorOption } from "./SelectorComponent";
|
||||||
import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
|
import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
|
||||||
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||||
|
import { asyncRequestToServer } from "../utils/requests";
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
export interface SponsorTimeEditProps {
|
export interface SponsorTimeEditProps {
|
||||||
index: number;
|
index: number;
|
||||||
@@ -297,7 +295,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
): ""}
|
): ""}
|
||||||
|
|
||||||
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
|
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
|
||||||
<span id={"sponsorTimePreviewButton" + this.idSuffix}
|
<span id={"sponsorTimePreviewEndButton" + this.idSuffix}
|
||||||
className="sponsorTimeEditButton"
|
className="sponsorTimeEditButton"
|
||||||
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey, true)}>
|
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey, true)}>
|
||||||
{chrome.i18n.getMessage("End")}
|
{chrome.i18n.getMessage("End")}
|
||||||
@@ -590,7 +588,24 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
getFormattedTime(sponsorTime.segment[1], true)];
|
getFormattedTime(sponsorTime.segment[1], true)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastEditTime = 0;
|
||||||
|
editTimeTimeout: NodeJS.Timeout | null = null;
|
||||||
saveEditTimes(): void {
|
saveEditTimes(): void {
|
||||||
|
// Rate limit edits
|
||||||
|
const timeSinceLastEdit = Date.now() - this.lastEditTime;
|
||||||
|
if (timeSinceLastEdit < 200) {
|
||||||
|
if (!this.editTimeTimeout) {
|
||||||
|
this.editTimeTimeout = setTimeout(() => {
|
||||||
|
this.saveEditTimes();
|
||||||
|
}, timeSinceLastEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastEditTime = Date.now();
|
||||||
|
this.editTimeTimeout = null;
|
||||||
|
|
||||||
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||||
const category = this.categoryOptionRef.current.value as Category
|
const category = this.categoryOptionRef.current.value as Category
|
||||||
|
|
||||||
@@ -710,7 +725,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||||||
if (this.props.contentContainer().channelIDInfo.status !== ChannelIDStatus.Found) return;
|
if (this.props.contentContainer().channelIDInfo.status !== ChannelIDStatus.Found) return;
|
||||||
|
|
||||||
this.fetchingSuggestions = true;
|
this.fetchingSuggestions = true;
|
||||||
const result = await utils.asyncRequestToServer("GET", "/api/chapterNames", {
|
const result = await asyncRequestToServer("GET", "/api/chapterNames", {
|
||||||
description,
|
description,
|
||||||
channelID: this.props.contentContainer().channelIDInfo.id
|
channelID: this.props.contentContainer().channelIDInfo.id
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||||||
|
|
||||||
guidelinesReminder: GenericNotice;
|
guidelinesReminder: GenericNotice;
|
||||||
|
|
||||||
|
lastSegmentCount: number;
|
||||||
|
|
||||||
constructor(props: SubmissionNoticeProps) {
|
constructor(props: SubmissionNoticeProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.noticeRef = React.createRef();
|
this.noticeRef = React.createRef();
|
||||||
@@ -47,12 +49,14 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||||||
|
|
||||||
const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
||||||
|
|
||||||
|
this.lastSegmentCount = this.props.contentContainer().sponsorTimesSubmitting.length;
|
||||||
|
|
||||||
// Setup state
|
// Setup state
|
||||||
this.state = {
|
this.state = {
|
||||||
noticeTitle,
|
noticeTitle,
|
||||||
messages: [],
|
messages: [],
|
||||||
idSuffix: "SubmissionNotice"
|
idSuffix: "SubmissionNotice"
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -73,6 +77,18 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const currentSegmentCount = this.props.contentContainer().sponsorTimesSubmitting.length;
|
||||||
|
if (currentSegmentCount > this.lastSegmentCount) {
|
||||||
|
this.lastSegmentCount = currentSegmentCount;
|
||||||
|
|
||||||
|
const scrollElement = this.noticeRef.current.getElement().current.querySelector("#sponsorSkipNoticeMiddleRowSubmissionNotice");
|
||||||
|
scrollElement.scrollTo({
|
||||||
|
top: scrollElement.scrollHeight + 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(): React.ReactElement {
|
render(): React.ReactElement {
|
||||||
const sortButton =
|
const sortButton =
|
||||||
<img id={"sponsorSkipSortButton" + this.state.idSuffix}
|
<img id={"sponsorSkipSortButton" + this.state.idSuffix}
|
||||||
|
|||||||
@@ -75,7 +75,10 @@ interface SBConfig {
|
|||||||
allowScrollingToEdit: boolean;
|
allowScrollingToEdit: boolean;
|
||||||
deArrowInstalled: boolean;
|
deArrowInstalled: boolean;
|
||||||
showDeArrowPromotion: boolean;
|
showDeArrowPromotion: boolean;
|
||||||
|
showDeArrowInSettings: boolean;
|
||||||
|
shownDeArrowPromotion: boolean;
|
||||||
showZoomToFillError2: boolean;
|
showZoomToFillError2: boolean;
|
||||||
|
cleanPopup: boolean;
|
||||||
|
|
||||||
// Used to cache calculated text color info
|
// Used to cache calculated text color info
|
||||||
categoryPillColors: {
|
categoryPillColors: {
|
||||||
@@ -317,7 +320,10 @@ const syncDefaults = {
|
|||||||
allowScrollingToEdit: true,
|
allowScrollingToEdit: true,
|
||||||
deArrowInstalled: false,
|
deArrowInstalled: false,
|
||||||
showDeArrowPromotion: true,
|
showDeArrowPromotion: true,
|
||||||
|
showDeArrowInSettings: true,
|
||||||
|
shownDeArrowPromotion: false,
|
||||||
showZoomToFillError2: true,
|
showZoomToFillError2: true,
|
||||||
|
cleanPopup: false,
|
||||||
|
|
||||||
categoryPillColors: {},
|
categoryPillColors: {},
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isV
|
|||||||
import { CategoryPill } from "./render/CategoryPill";
|
import { CategoryPill } from "./render/CategoryPill";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "./utils/animationUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
import { logDebug } from "./utils/logger";
|
import { logDebug, logWarn } from "./utils/logger";
|
||||||
import { importTimes } from "./utils/exporter";
|
import { importTimes } from "./utils/exporter";
|
||||||
import { ChapterVote } from "./render/ChapterVote";
|
import { ChapterVote } from "./render/ChapterVote";
|
||||||
import { openWarningDialog } from "./utils/warnings";
|
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 { 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 { 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 { 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 { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
import { generateUserID } from "../maze-utils/src/setup";
|
import { generateUserID } from "../maze-utils/src/setup";
|
||||||
import { updateAll } from "../maze-utils/src/thumbnailManagement";
|
import { updateAll } from "../maze-utils/src/thumbnailManagement";
|
||||||
import { setupThumbnailListener } from "./utils/thumbnails";
|
import { setupThumbnailListener } from "./utils/thumbnails";
|
||||||
import * as documentScript from "../dist/js/document.js";
|
import * as documentScript from "../dist/js/document.js";
|
||||||
import { Tooltip } from "./render/Tooltip";
|
|
||||||
import { isDeArrowInstalled } from "./utils/crossExtension";
|
|
||||||
import { runCompatibilityChecks } from "./utils/compatibility";
|
import { runCompatibilityChecks } from "./utils/compatibility";
|
||||||
import { cleanPage } from "./utils/pageCleaner";
|
import { cleanPage } from "./utils/pageCleaner";
|
||||||
import { addCleanupListener } from "../maze-utils/src/cleanup";
|
import { addCleanupListener } from "../maze-utils/src/cleanup";
|
||||||
|
import { hideDeArrowPromotion, tryShowingDeArrowPromotion } from "./dearrowPromotion";
|
||||||
|
import { asyncRequestToServer } from "./utils/requests";
|
||||||
|
|
||||||
cleanPage();
|
cleanPage();
|
||||||
|
|
||||||
@@ -57,47 +57,12 @@ utils.wait(() => Config.isReady(), 5000, 10).then(() => {
|
|||||||
addCSS();
|
addCSS();
|
||||||
setCategoryColorCSSVariables();
|
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();
|
runCompatibilityChecks();
|
||||||
});
|
});
|
||||||
|
|
||||||
const skipBuffer = 0.003;
|
const skipBuffer = 0.003;
|
||||||
|
// If this close to the end, skip to the end
|
||||||
|
const endTimeSkipBuffer = 0.5;
|
||||||
|
|
||||||
//was sponsor data found when doing SponsorsLookup
|
//was sponsor data found when doing SponsorsLookup
|
||||||
let sponsorDataFound = false;
|
let sponsorDataFound = false;
|
||||||
@@ -302,8 +267,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
reskipSponsorTime(sponsorTimes.find((segment) => segment.UUID === request.UUID), true);
|
reskipSponsorTime(sponsorTimes.find((segment) => segment.UUID === request.UUID), true);
|
||||||
break;
|
break;
|
||||||
case "selectSegment":
|
case "selectSegment":
|
||||||
selectedSegment = request.UUID;
|
selectSegment(request.UUID);
|
||||||
updatePreviewBar();
|
|
||||||
break;
|
break;
|
||||||
case "submitVote":
|
case "submitVote":
|
||||||
vote(request.type, request.UUID).then((response) => sendResponse(response));
|
vote(request.type, request.UUID).then((response) => sendResponse(response));
|
||||||
@@ -439,6 +403,8 @@ function resetValues() {
|
|||||||
for (let i = 0; i < skipNotices.length; i++) {
|
for (let i = 0; i < skipNotices.length; i++) {
|
||||||
skipNotices.pop()?.close();
|
skipNotices.pop()?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideDeArrowPromotion();
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoIDChange(): void {
|
function videoIDChange(): void {
|
||||||
@@ -479,6 +445,8 @@ function videoIDChange(): void {
|
|||||||
// Clear unsubmitted segments from the previous video
|
// Clear unsubmitted segments from the previous video
|
||||||
sponsorTimesSubmitting = [];
|
sponsorTimesSubmitting = [];
|
||||||
updateSponsorTimesSubmitting();
|
updateSponsorTimesSubmitting();
|
||||||
|
|
||||||
|
tryShowingDeArrowPromotion().catch(logWarn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMobileControlsMutations(): void {
|
function handleMobileControlsMutations(): void {
|
||||||
@@ -623,7 +591,8 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
|
|
||||||
updateActiveSegment(currentTime);
|
updateActiveSegment(currentTime);
|
||||||
|
|
||||||
if (getVideo().paused) return;
|
if (getVideo().paused
|
||||||
|
|| (getVideo().currentTime >= getVideo().duration - 0.01 && getVideo().duration > 1)) return;
|
||||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||||
|
|
||||||
const currentSkip = skipInfo.array[skipInfo.index];
|
const currentSkip = skipInfo.array[skipInfo.index];
|
||||||
@@ -702,8 +671,12 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
forcedSkipTime = skipTime[0] + 0.001;
|
forcedSkipTime = skipTime[0] + 0.001;
|
||||||
} else {
|
} else {
|
||||||
forcedSkipTime = skipTime[1];
|
forcedSkipTime = skipTime[1];
|
||||||
forcedIncludeIntersectingSegments = true;
|
|
||||||
forcedIncludeNonIntersectingSegments = false;
|
forcedIncludeNonIntersectingSegments = false;
|
||||||
|
|
||||||
|
// Only if not at the end of the video
|
||||||
|
if (Math.abs(skipTime[1] - getVideo().duration) > endTimeSkipBuffer) {
|
||||||
|
forcedIncludeIntersectingSegments = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
forcedSkipTime = forceVideoTime + 0.001;
|
forcedSkipTime = forceVideoTime + 0.001;
|
||||||
@@ -1071,6 +1044,7 @@ function setupSkipButtonControlBar() {
|
|||||||
openNotice: true,
|
openNotice: true,
|
||||||
forceAutoSkip: true
|
forceAutoSkip: true
|
||||||
}),
|
}),
|
||||||
|
selectSegment,
|
||||||
onMobileYouTube: isOnMobileYouTube()
|
onMobileYouTube: isOnMobileYouTube()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1105,7 +1079,7 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
|||||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||||
|
|
||||||
const hashPrefix = (await getHash(getVideoID(), 1)).slice(0, 4) as VideoID & HashedValue;
|
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,
|
categories,
|
||||||
actionTypes: getEnabledActionTypes(),
|
actionTypes: getEnabledActionTypes(),
|
||||||
userAgent: `${chrome.runtime.id}`,
|
userAgent: `${chrome.runtime.id}`,
|
||||||
@@ -1245,7 +1219,7 @@ function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
|
|||||||
|
|
||||||
async function lockedCategoriesLookup(): Promise<void> {
|
async function lockedCategoriesLookup(): Promise<void> {
|
||||||
const hashPrefix = (await getHash(getVideoID(), 1)).slice(0, 4);
|
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) {
|
if (response.ok) {
|
||||||
try {
|
try {
|
||||||
@@ -1349,6 +1323,11 @@ function updatePreviewBarPositionMobile(parent: HTMLElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectSegment(UUID: SegmentUUID): void {
|
||||||
|
selectedSegment = UUID;
|
||||||
|
updatePreviewBar();
|
||||||
|
}
|
||||||
|
|
||||||
function updatePreviewBar(): void {
|
function updatePreviewBar(): void {
|
||||||
if (previewBar === null) return;
|
if (previewBar === null) return;
|
||||||
|
|
||||||
@@ -1374,7 +1353,7 @@ function updatePreviewBar(): void {
|
|||||||
showLarger: segment.actionType === ActionType.Poi,
|
showLarger: segment.actionType === ActionType.Poi,
|
||||||
description: segment.description,
|
description: segment.description,
|
||||||
source: segment.source,
|
source: segment.source,
|
||||||
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID.startsWith(requiredSegment)),
|
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID?.startsWith(requiredSegment)),
|
||||||
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1634,7 +1613,7 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
|||||||
counted = true;
|
counted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullSkip) utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1660,6 +1639,9 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
|||||||
// MacOS will loop otherwise #1027
|
// MacOS will loop otherwise #1027
|
||||||
// Sometimes playlists loop too #1804
|
// Sometimes playlists loop too #1804
|
||||||
v.currentTime = v.duration - 0.001;
|
v.currentTime = v.duration - 0.001;
|
||||||
|
} else if (v.duration > 1 && Math.abs(skipTime[1] - v.duration) < endTimeSkipBuffer
|
||||||
|
&& isFirefoxOrSafari() && !isSafari()) {
|
||||||
|
v.currentTime = v.duration;
|
||||||
} else {
|
} else {
|
||||||
if (inMuteSegment(skipTime[1], true)) {
|
if (inMuteSegment(skipTime[1], true)) {
|
||||||
// Make sure not to mute if skipping into a mute segment
|
// Make sure not to mute if skipping into a mute segment
|
||||||
@@ -2267,7 +2249,7 @@ async function sendSubmitMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
|
const response = await asyncRequestToServer("POST", "/api/skipSegments", {
|
||||||
videoID: getVideoID(),
|
videoID: getVideoID(),
|
||||||
userID: Config.config.userID,
|
userID: Config.config.userID,
|
||||||
segments: sponsorTimesSubmitting,
|
segments: sponsorTimesSubmitting,
|
||||||
|
|||||||
71
src/dearrowPromotion.ts
Normal file
71
src/dearrowPromotion.ts
Normal file
@@ -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();
|
||||||
|
}
|
||||||
27
src/help.ts
27
src/help.ts
@@ -3,6 +3,7 @@ import Config from "./config";
|
|||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
|
|
||||||
import { waitFor } from "../maze-utils/src";
|
import { waitFor } from "../maze-utils/src";
|
||||||
|
import { isDeArrowInstalled } from "./utils/crossExtension";
|
||||||
|
|
||||||
if (document.readyState === "complete") {
|
if (document.readyState === "complete") {
|
||||||
init();
|
init();
|
||||||
@@ -10,6 +11,32 @@ if (document.readyState === "complete") {
|
|||||||
document.addEventListener("DOMContentLoaded", init);
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeArrow promotion
|
||||||
|
waitFor(() => Config.isReady()).then(() => {
|
||||||
|
if (Config.config.showNewFeaturePopups && Config.config.showUpsells) {
|
||||||
|
isDeArrowInstalled().then((installed) => {
|
||||||
|
if (!installed) {
|
||||||
|
const deArrowPromotion = document.getElementById("dearrow-link");
|
||||||
|
deArrowPromotion.classList.remove("hidden");
|
||||||
|
|
||||||
|
deArrowPromotion.addEventListener("click", () => Config.config.showDeArrowPromotion = false);
|
||||||
|
|
||||||
|
const text = deArrowPromotion.querySelector("#dearrow-link-text");
|
||||||
|
text.textContent = `${chrome.i18n.getMessage("DeArrowPromotionMessage2").split("?")[0]}? ${chrome.i18n.getMessage("DeArrowPromotionMessage3")}`;
|
||||||
|
|
||||||
|
const closeButton = deArrowPromotion.querySelector(".close-button");
|
||||||
|
closeButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
deArrowPromotion.classList.add("hidden");
|
||||||
|
Config.config.showDeArrowPromotion = false;
|
||||||
|
Config.config.showDeArrowInSettings = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
localizeHtmlPage();
|
localizeHtmlPage();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { SponsorTime } from "../types";
|
import { SegmentUUID, SponsorTime } from "../types";
|
||||||
import { getSkippingText } from "../utils/categoryUtils";
|
import { getSkippingText } from "../utils/categoryUtils";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
import { keybindToString } from "../../maze-utils/src/config";
|
import { keybindToString } from "../../maze-utils/src/config";
|
||||||
|
|
||||||
export interface SkipButtonControlBarProps {
|
export interface SkipButtonControlBarProps {
|
||||||
skip: (segment: SponsorTime) => void;
|
skip: (segment: SponsorTime) => void;
|
||||||
|
selectSegment: (UUID: SegmentUUID) => void;
|
||||||
onMobileYouTube: boolean;
|
onMobileYouTube: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +55,18 @@ export class SkipButtonControlBar {
|
|||||||
this.container.appendChild(this.skipIcon);
|
this.container.appendChild(this.skipIcon);
|
||||||
this.container.appendChild(this.textContainer);
|
this.container.appendChild(this.textContainer);
|
||||||
this.container.addEventListener("click", () => this.toggleSkip());
|
this.container.addEventListener("click", () => this.toggleSkip());
|
||||||
this.container.addEventListener("mouseenter", () => this.stopTimer());
|
this.container.addEventListener("mouseenter", () => {
|
||||||
this.container.addEventListener("mouseleave", () => this.startTimer());
|
this.stopTimer();
|
||||||
|
|
||||||
|
if (this.segment) {
|
||||||
|
props.selectSegment(this.segment.UUID);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.container.addEventListener("mouseleave", () => {
|
||||||
|
this.startTimer();
|
||||||
|
|
||||||
|
props.selectSegment(null);
|
||||||
|
});
|
||||||
if (this.onMobileYouTube) {
|
if (this.onMobileYouTube) {
|
||||||
this.container.addEventListener("touchstart", (e) => this.handleTouchStart(e));
|
this.container.addEventListener("touchstart", (e) => this.handleTouchStart(e));
|
||||||
this.container.addEventListener("touchmove", (e) => this.handleTouchMove(e));
|
this.container.addEventListener("touchmove", (e) => this.handleTouchMove(e));
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { StorageChangesObject } from "../maze-utils/src/config";
|
|||||||
import { getHash } from "../maze-utils/src/hash";
|
import { getHash } from "../maze-utils/src/hash";
|
||||||
import { isFirefoxOrSafari } from "../maze-utils/src";
|
import { isFirefoxOrSafari } from "../maze-utils/src";
|
||||||
import { isDeArrowInstalled } from "./utils/crossExtension";
|
import { isDeArrowInstalled } from "./utils/crossExtension";
|
||||||
|
import { asyncRequestToServer } from "./utils/requests";
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
let embed = false;
|
let embed = false;
|
||||||
|
|
||||||
@@ -74,13 +75,22 @@ async function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeArrow promotion
|
// DeArrow promotion
|
||||||
if (Config.config.showNewFeaturePopups && Config.config.showUpsells) {
|
if (Config.config.showNewFeaturePopups && Config.config.showUpsells && Config.config.showDeArrowInSettings) {
|
||||||
isDeArrowInstalled().then((installed) => {
|
isDeArrowInstalled().then((installed) => {
|
||||||
if (!installed) {
|
if (!installed) {
|
||||||
const deArrowPromotion = document.getElementById("deArrowPromotion");
|
const deArrowPromotion = document.getElementById("deArrowPromotion");
|
||||||
deArrowPromotion.classList.remove("hidden");
|
deArrowPromotion.classList.remove("hidden");
|
||||||
|
|
||||||
deArrowPromotion.addEventListener("click", () => Config.config.showDeArrowPromotion = false);
|
deArrowPromotion.addEventListener("click", () => Config.config.showDeArrowPromotion = false);
|
||||||
|
|
||||||
|
const closeButton = deArrowPromotion.querySelector(".close-button");
|
||||||
|
closeButton.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
deArrowPromotion.classList.add("hidden");
|
||||||
|
Config.config.showDeArrowPromotion = false;
|
||||||
|
Config.config.showDeArrowInSettings = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -558,7 +568,7 @@ function activatePrivateTextChange(element: HTMLElement) {
|
|||||||
switch (option) {
|
switch (option) {
|
||||||
case "userID":
|
case "userID":
|
||||||
if (Config.config[option]) {
|
if (Config.config[option]) {
|
||||||
utils.asyncRequestToServer("GET", "/api/userInfo", {
|
asyncRequestToServer("GET", "/api/userInfo", {
|
||||||
publicUserID: getHash(Config.config[option]),
|
publicUserID: getHash(Config.config[option]),
|
||||||
values: ["warnings", "banned"]
|
values: ["warnings", "banned"]
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
|
|||||||
24
src/popup.ts
24
src/popup.ts
@@ -27,6 +27,7 @@ import GenericNotice from "./render/GenericNotice";
|
|||||||
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
||||||
import { StorageChangesObject } from "../maze-utils/src/config";
|
import { StorageChangesObject } from "../maze-utils/src/config";
|
||||||
import { getHash } from "../maze-utils/src/hash";
|
import { getHash } from "../maze-utils/src/hash";
|
||||||
|
import { asyncRequestToServer, sendRequestToServer } from "./utils/requests";
|
||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
@@ -108,6 +109,10 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
const PageElements: PageElements = {};
|
const PageElements: PageElements = {};
|
||||||
|
|
||||||
[
|
[
|
||||||
|
"sbPopupLogo",
|
||||||
|
"sbYourWorkBox",
|
||||||
|
"videoInfo",
|
||||||
|
"sbFooter",
|
||||||
"sponsorBlockPopupBody",
|
"sponsorBlockPopupBody",
|
||||||
"sponsorblockPopup",
|
"sponsorblockPopup",
|
||||||
"sponsorStart",
|
"sponsorStart",
|
||||||
@@ -198,6 +203,16 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
}
|
}
|
||||||
PageElements.sbDonate.addEventListener("click", () => Config.config.donateClicked = Config.config.donateClicked + 1);
|
PageElements.sbDonate.addEventListener("click", () => Config.config.donateClicked = Config.config.donateClicked + 1);
|
||||||
|
|
||||||
|
if (Config.config.cleanPopup) {
|
||||||
|
PageElements.sbPopupLogo.classList.add("hidden");
|
||||||
|
PageElements.sbYourWorkBox.classList.add("hidden");
|
||||||
|
PageElements.sbFooter.classList.add("hidden");
|
||||||
|
PageElements.sponsorTimesDonateContainer.classList.add("hidden");
|
||||||
|
PageElements.mainControls.classList.add("hidden");
|
||||||
|
|
||||||
|
PageElements.videoInfo.style.marginTop = "10px";
|
||||||
|
}
|
||||||
|
|
||||||
if (Config.config.testingServer) {
|
if (Config.config.testingServer) {
|
||||||
PageElements.sbBetaServerWarning.classList.remove("hidden");
|
PageElements.sbBetaServerWarning.classList.remove("hidden");
|
||||||
PageElements.sbBetaServerWarning.addEventListener("click", function () {
|
PageElements.sbBetaServerWarning.addEventListener("click", function () {
|
||||||
@@ -281,7 +296,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
|
|
||||||
const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions"];
|
const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions"];
|
||||||
|
|
||||||
utils.asyncRequestToServer("GET", "/api/userInfo", {
|
asyncRequestToServer("GET", "/api/userInfo", {
|
||||||
publicUserID: await getHash(Config.config.userID),
|
publicUserID: await getHash(Config.config.userID),
|
||||||
values
|
values
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@@ -804,7 +819,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
PageElements.setUsernameStatus.style.display = "unset";
|
PageElements.setUsernameStatus.style.display = "unset";
|
||||||
PageElements.setUsernameStatus.innerText = chrome.i18n.getMessage("Loading");
|
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) {
|
if (response.status == 200) {
|
||||||
//submitted
|
//submitted
|
||||||
PageElements.submitUsername.style.display = "none";
|
PageElements.submitUsername.style.display = "none";
|
||||||
@@ -1052,9 +1067,10 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
*/
|
*/
|
||||||
function getFormattedHours(minutes) {
|
function getFormattedHours(minutes) {
|
||||||
minutes = Math.round(minutes * 10) / 10;
|
minutes = Math.round(minutes * 10) / 10;
|
||||||
const days = Math.floor(minutes / 1440);
|
const years = Math.floor(minutes / 525600); // Assumes 365.0 days in a year
|
||||||
|
const days = Math.floor(minutes / 1440) % 365;
|
||||||
const hours = Math.floor(minutes / 60) % 24;
|
const hours = Math.floor(minutes / 60) % 24;
|
||||||
return (days > 0 ? days + chrome.i18n.getMessage("dayAbbreviation") + " " : "") + (hours > 0 ? hours + chrome.i18n.getMessage("hourAbbreviation") + " " : "") + (minutes % 60).toFixed(1);
|
return (years > 0 ? years + chrome.i18n.getMessage("yearAbbreviation") + " " : "") + (days > 0 ? days + chrome.i18n.getMessage("dayAbbreviation") + " " : "") + (hours > 0 ? hours + chrome.i18n.getMessage("hourAbbreviation") + " " : "") + (minutes % 60).toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function contentConfigUpdateListener(changes: StorageChangesObject) {
|
function contentConfigUpdateListener(changes: StorageChangesObject) {
|
||||||
|
|||||||
46
src/utils.ts
46
src/utils.ts
@@ -2,10 +2,8 @@ import Config, { VideoDownvotes } from "./config";
|
|||||||
import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types";
|
import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types";
|
||||||
|
|
||||||
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
import * as CompileConfig from "../config.json";
|
|
||||||
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||||
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
||||||
import { FetchResponse, sendRequestToCustomServer } from "../maze-utils/src/background-request-proxy"
|
|
||||||
import { isSafari } from "../maze-utils/src/config";
|
import { isSafari } from "../maze-utils/src/config";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
@@ -240,50 +238,6 @@ export default class Utils {
|
|||||||
return permissionRegex;
|
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 {
|
findReferenceNode(): HTMLElement {
|
||||||
const selectors = [
|
const selectors = [
|
||||||
"#player-container-id", // Mobile YouTube
|
"#player-container-id", // Mobile YouTube
|
||||||
|
|||||||
@@ -106,5 +106,5 @@ export function exportTimesAsHashParam(segments: SponsorTime[]): string {
|
|||||||
|
|
||||||
|
|
||||||
export function normalizeChapterName(description: string): string {
|
export function normalizeChapterName(description: string): string {
|
||||||
return description.toLowerCase().replace(/\.|:|-/g, "").replace(/\s+/g, " ");
|
return description.toLowerCase().replace(/[.:'’`‛‘"‟”-]/ug, "").replace(/\s+/g, " ");
|
||||||
}
|
}
|
||||||
47
src/utils/requests.ts
Normal file
47
src/utils/requests.ts
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Category, CategorySkipOption, VideoID } from "../types";
|
|||||||
import { getHash } from "../../maze-utils/src/hash";
|
import { getHash } from "../../maze-utils/src/hash";
|
||||||
import Utils from "../utils";
|
import Utils from "../utils";
|
||||||
import { logWarn } from "./logger";
|
import { logWarn } from "./logger";
|
||||||
|
import { asyncRequestToServer } from "./requests";
|
||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
return cachedEntry;
|
return cachedEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await utils.asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
|
const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
// No video labels or server down
|
// No video labels or server down
|
||||||
labelCache[hashPrefix] = {
|
labelCache[hashPrefix] = {
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { getHash } from "../../maze-utils/src/hash";
|
|||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
|
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
|
||||||
import { ContentContainer } from "../types";
|
import { ContentContainer } from "../types";
|
||||||
import Utils from "../utils";
|
import { asyncRequestToServer } from "./requests";
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
export interface ChatConfig {
|
export interface ChatConfig {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
@@ -13,14 +12,14 @@ export interface ChatConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function openWarningDialog(contentContainer: ContentContainer): Promise<void> {
|
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),
|
publicUserID: await getHash(Config.config.userID),
|
||||||
values: ["warningReason"]
|
values: ["warningReason"]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userInfo.ok) {
|
if (userInfo.ok) {
|
||||||
const warningReason = JSON.parse(userInfo.responseText)?.warningReason;
|
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 userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : "";
|
||||||
const publicUserID = await getHash(Config.config.userID);
|
const publicUserID = await getHash(Config.config.userID);
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ export async function openWarningDialog(contentContainer: ContentContainer): Pro
|
|||||||
{
|
{
|
||||||
name: chrome.i18n.getMessage("warningConfirmButton"),
|
name: chrome.i18n.getMessage("warningConfirmButton"),
|
||||||
listener: async () => {
|
listener: async () => {
|
||||||
const result = await utils.asyncRequestToServer("POST", "/api/warnUser", {
|
const result = await asyncRequestToServer("POST", "/api/warnUser", {
|
||||||
userID: Config.config.userID,
|
userID: Config.config.userID,
|
||||||
enabled: false
|
enabled: false
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user