mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 11:37:02 +03:00
Compare commits
98 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 | ||
|
|
e650b7183a | ||
|
|
4eb097b422 | ||
|
|
04a9f82bdc | ||
|
|
39cfdc7b6c | ||
|
|
a8701b02a1 | ||
|
|
3f1ad528c3 | ||
|
|
ae685baeef | ||
|
|
d2ee67f3cf | ||
|
|
d440a4d52a | ||
|
|
7566b71ccd | ||
|
|
109b7ed5bc | ||
|
|
3eb853154f | ||
|
|
ee3ce8aa46 | ||
|
|
1557af5d2a | ||
|
|
465e7065ca | ||
|
|
a3f8419c49 | ||
|
|
dde443ccec | ||
|
|
01b1380b78 | ||
|
|
c51b18465e | ||
|
|
ad9888cf52 | ||
|
|
7856791f90 | ||
|
|
273ee63ec7 | ||
|
|
be36583aee | ||
|
|
433bbbf904 | ||
|
|
6c2ee76198 | ||
|
|
42f59898f3 | ||
|
|
8ab126f502 | ||
|
|
4954abf9e3 | ||
|
|
30a21d5ff5 | ||
|
|
d1b2def47c | ||
|
|
48cdabe2a5 | ||
|
|
bc2db0cf2c | ||
|
|
843ef37dcd | ||
|
|
ed260a0667 | ||
|
|
2e131c2a95 | ||
|
|
f5e884b6aa | ||
|
|
a9929d0c93 | ||
|
|
3fb43d1c0e | ||
|
|
a1b2855538 | ||
|
|
07236baed5 | ||
|
|
f991435857 | ||
|
|
faa3259165 | ||
|
|
c96bafb6f7 | ||
|
|
9b7680f0e6 | ||
|
|
16e01b7494 | ||
|
|
6cd697dc32 | ||
|
|
9946bd1af2 | ||
|
|
3b06d72270 | ||
|
|
4bd0556464 | ||
|
|
7e12a914d5 | ||
|
|
25eaf4fa20 | ||
|
|
b3efa1f787 | ||
|
|
9a18e70e34 | ||
|
|
64ece9cb73 | ||
|
|
66c974b011 | ||
|
|
d8cc93c841 | ||
|
|
de22accfda | ||
|
|
e5b0b60dde | ||
|
|
32d98e6544 | ||
|
|
3dde05eda2 | ||
|
|
6aeefaae64 | ||
|
|
93d695e6c2 | ||
|
|
160924feee | ||
|
|
e3f3ed20e6 | ||
|
|
edaed61612 |
@@ -1,5 +1,8 @@
|
||||
If you make any contributions to SponsorBlock after this file was created, you are agreeing that any code you have contributed will be licensed under LGPL-3.0 or later.
|
||||
|
||||
# Translations
|
||||
https://crowdin.com/project/sponsorblock
|
||||
|
||||
# Building
|
||||
## Building locally
|
||||
0. You must have [Node.js 16 or later](https://nodejs.org/) and npm installed. Works best on Linux
|
||||
@@ -24,5 +27,5 @@ Run `npm run dev` (for Chrome) or `npm run dev:firefox` (for Firefox) to run the
|
||||
|
||||
Known chromium bug: Extension is not loaded properly on first start. Visit `chrome://extensions/` and reload the extension.
|
||||
|
||||
For Firefox for Android, use `npm run dev:firefox-android -- --adb-device <ip-address of the device>`. See the [Firefox documentation](https://extensionworkshop.com/documentation/develop/developing-extensions-for-firefox-for-android/#debug-your-extension) for more information.
|
||||
For Firefox for Android, use `npm run dev:firefox-android -- --adb-device <ip-address of the device>`. See the [Firefox documentation](https://extensionworkshop.com/documentation/develop/developing-extensions-for-firefox-for-android/#debug-your-extension) for more information. You may need to edit package.json and add the parameters directly there.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
["www.youtubekids.com","inv.bp.projectsegfau.lt","inv.tux.pizza","inv.zzls.xyz","invidious.0011.lt","invidious.lunar.icu","invidious.privacydev.net","invidious.tiekoetter.com","iv.ggtyler.dev","iv.melmac.space","vid.priv.au","vid.puffyan.us","yewtu.be","yt.artemislena.eu"]
|
||||
["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,6 +1,7 @@
|
||||
{
|
||||
"optional_permissions": [
|
||||
"declarativeContent"
|
||||
"declarativeContent",
|
||||
"webNavigation"
|
||||
],
|
||||
"background": {
|
||||
"persistent": false
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
{
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "sponsorBlocker@ajay.app"
|
||||
}
|
||||
"id": "sponsorBlocker@ajay.app",
|
||||
"strict_min_version": "48.0"
|
||||
},
|
||||
"gecko_android": {
|
||||
"strict_min_version": "79.0"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"persistent": false
|
||||
},
|
||||
"permissions": [
|
||||
"scripting"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_area": "navbar"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "SponsorBlock",
|
||||
"version": "5.4.15",
|
||||
"version": "5.4.27",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"homepage_url": "https://sponsor.ajay.app",
|
||||
@@ -17,9 +17,7 @@
|
||||
],
|
||||
"css": [
|
||||
"content.css",
|
||||
"shared.css",
|
||||
"./libs/Source+Sans+Pro.css",
|
||||
"popup.css"
|
||||
"shared.css"
|
||||
]
|
||||
}],
|
||||
"web_accessible_resources": [
|
||||
@@ -84,8 +82,7 @@
|
||||
"https://sponsor.ajay.app/*"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"*://*/*",
|
||||
"webNavigation"
|
||||
"*://*/*"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_title": "SponsorBlock",
|
||||
@@ -116,6 +113,21 @@
|
||||
"light": "icons/IconSponsorBlocker128px.png",
|
||||
"dark": "icons/IconSponsorBlocker128px.png",
|
||||
"size": 128
|
||||
},
|
||||
{
|
||||
"light": "icons/IconSponsorBlocker256px.png",
|
||||
"dark": "icons/IconSponsorBlocker256px.png",
|
||||
"size": 256
|
||||
},
|
||||
{
|
||||
"light": "icons/IconSponsorBlocker512px.png",
|
||||
"dark": "icons/IconSponsorBlocker512px.png",
|
||||
"size": 512
|
||||
},
|
||||
{
|
||||
"light": "icons/IconSponsorBlocker1024px.png",
|
||||
"dark": "icons/IconSponsorBlocker1024px.png",
|
||||
"size": 1024
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
"background": {
|
||||
"persistent": false
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"scripting"
|
||||
],
|
||||
"optional_permissions": [
|
||||
"webNavigation"
|
||||
]
|
||||
}
|
||||
|
||||
Submodule maze-utils updated: 66fa2dc624...6bdc9402c6
@@ -62,7 +62,7 @@
|
||||
"webext-content-scripts": {
|
||||
"ignore": false,
|
||||
"name": "webext-content-scripts",
|
||||
"version": "2.5.2",
|
||||
"version": "2.5.5",
|
||||
"authors": "Federico Brigante <me@fregante.com> (https://fregante.com)",
|
||||
"url": "https://github.com/fregante/webext-content-scripts",
|
||||
"license": "MIT",
|
||||
@@ -80,7 +80,7 @@
|
||||
"webext-polyfill-kinda": {
|
||||
"ignore": false,
|
||||
"name": "webext-polyfill-kinda",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"authors": "Federico Brigante <me@fregante.com> (https://fregante.com)",
|
||||
"url": "https://github.com/fregante/webext-polyfill-kinda",
|
||||
"license": "MIT",
|
||||
|
||||
33
package-lock.json
generated
33
package-lock.json
generated
@@ -12961,12 +12961,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webext-content-scripts": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/webext-content-scripts/-/webext-content-scripts-2.5.2.tgz",
|
||||
"integrity": "sha512-N1Xq/E8dx0lVAOyPquuo+2Vj9Fx1GoqCFo79lWeJHbemaBJ53N3BHBmbJJYsQ8FOP1xiwN4bPRQY2dpSjHAD3Q==",
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/webext-content-scripts/-/webext-content-scripts-2.5.5.tgz",
|
||||
"integrity": "sha512-CIq1LA/nHIXE43v8qlpqNPcbsSzGuQBkeykbqOWvKJ1Rx/q7zgdZsLgxwyoonWiQcJczslVmGWCfdBY04JwIyw==",
|
||||
"dependencies": {
|
||||
"webext-patterns": "^1.3.0",
|
||||
"webext-polyfill-kinda": "^1.0.0"
|
||||
"webext-polyfill-kinda": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fregante"
|
||||
@@ -12995,9 +12998,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webext-polyfill-kinda": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webext-polyfill-kinda/-/webext-polyfill-kinda-1.0.0.tgz",
|
||||
"integrity": "sha512-Py/d3w/bC0KntuO60ePSWHsdrebZ3uYBLeFUjyPkDV3yTEQib0MRFvPh57t8XjImu4ylBoEAsFjzh/r22UtxMw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webext-polyfill-kinda/-/webext-polyfill-kinda-1.0.2.tgz",
|
||||
"integrity": "sha512-rqQUKeBTOicej0tjDJWDQlOTnDcm9yYJTzgI+7rMdyYV4QHmYMRm+yjkcVgECkg/Wu9MboZ4lYeBPdp1Ep9WgQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fregante"
|
||||
}
|
||||
@@ -16739,7 +16742,7 @@
|
||||
"resolved": "https://registry.npmjs.org/content-scripts-register-polyfill/-/content-scripts-register-polyfill-4.0.2.tgz",
|
||||
"integrity": "sha512-8hDm+tu3BkxHZP7EUIIIo/495F6QNXF7cI9Lwr4PQaiohw2wWmi9k2SE4W4kNrAaLnFw6RZ2ev8EmrQb+sCoGQ==",
|
||||
"requires": {
|
||||
"webext-content-scripts": "^2.5.2",
|
||||
"webext-content-scripts": "v2.5.5",
|
||||
"webext-patterns": "^1.3.0",
|
||||
"webext-polyfill-kinda": "^1.0.0"
|
||||
}
|
||||
@@ -23096,12 +23099,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"webext-content-scripts": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/webext-content-scripts/-/webext-content-scripts-2.5.2.tgz",
|
||||
"integrity": "sha512-N1Xq/E8dx0lVAOyPquuo+2Vj9Fx1GoqCFo79lWeJHbemaBJ53N3BHBmbJJYsQ8FOP1xiwN4bPRQY2dpSjHAD3Q==",
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/webext-content-scripts/-/webext-content-scripts-2.5.5.tgz",
|
||||
"integrity": "sha512-CIq1LA/nHIXE43v8qlpqNPcbsSzGuQBkeykbqOWvKJ1Rx/q7zgdZsLgxwyoonWiQcJczslVmGWCfdBY04JwIyw==",
|
||||
"requires": {
|
||||
"webext-patterns": "^1.3.0",
|
||||
"webext-polyfill-kinda": "^1.0.0"
|
||||
"webext-polyfill-kinda": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"webext-patterns": {
|
||||
@@ -23120,9 +23123,9 @@
|
||||
}
|
||||
},
|
||||
"webext-polyfill-kinda": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webext-polyfill-kinda/-/webext-polyfill-kinda-1.0.0.tgz",
|
||||
"integrity": "sha512-Py/d3w/bC0KntuO60ePSWHsdrebZ3uYBLeFUjyPkDV3yTEQib0MRFvPh57t8XjImu4ylBoEAsFjzh/r22UtxMw=="
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webext-polyfill-kinda/-/webext-polyfill-kinda-1.0.2.tgz",
|
||||
"integrity": "sha512-rqQUKeBTOicej0tjDJWDQlOTnDcm9yYJTzgI+7rMdyYV4QHmYMRm+yjkcVgECkg/Wu9MboZ4lYeBPdp1Ep9WgQ=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"overrides": {
|
||||
"content-scripts-register-polyfill": {
|
||||
"webext-content-scripts": "v2.5.5"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chrome": "^0.0.220",
|
||||
"@types/firefox-webext-browser": "^111.0.0",
|
||||
|
||||
Submodule public/_locales updated: 9e1f32d7c6...b31f76dd29
@@ -7,7 +7,7 @@
|
||||
--sb-dark-red-outline: rgb(130,0,0,0.9);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
.sbhidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,13 @@ div:hover > #previewbar.sbNotInvidious {
|
||||
}
|
||||
|
||||
.previewbar.requiredSegment {
|
||||
transform: scaleY(3)
|
||||
transform: scaleY(3);
|
||||
}
|
||||
|
||||
.previewbar.selectedSegment {
|
||||
opacity: 1 !important;
|
||||
z-index: 100;
|
||||
transform: scaleY(1.5);
|
||||
}
|
||||
|
||||
/* Make sure settings are upfront */
|
||||
@@ -145,7 +151,7 @@ div:hover > .sponsorBlockChapterBar {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.playerButton.hidden:not(.autoHiding) {
|
||||
.playerButton.sbhidden:not(.autoHiding) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -163,13 +169,13 @@ div:hover > .sponsorBlockChapterBar {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.autoHiding:not(.hidden) {
|
||||
.autoHiding:not(.sbhidden) {
|
||||
transform: translateX(0%) scale(1);
|
||||
/* opacity is from YouTube page */
|
||||
transition: transform 0.2s, width 0.2s, opacity .1s cubic-bezier(0.4,0.0,1,1) !important;
|
||||
}
|
||||
|
||||
.autoHiding.hidden {
|
||||
.autoHiding.sbhidden {
|
||||
transform: translateX(100%) scale(0);
|
||||
/* opacity is from YouTube page */
|
||||
transition: transform 0.2s, width 0.2s, opacity .1s cubic-bezier(0.4,0.0,1,1) !important;
|
||||
@@ -177,7 +183,7 @@ div:hover > .sponsorBlockChapterBar {
|
||||
width: 0px !important;
|
||||
}
|
||||
|
||||
.autoHiding.hidden.autoHideLeft {
|
||||
.autoHiding.sbhidden.autoHideLeft {
|
||||
transform: translateX(-100%) scale(0);
|
||||
}
|
||||
|
||||
@@ -243,11 +249,6 @@ div:hover > .sponsorBlockChapterBar {
|
||||
border-collapse: unset;
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeParent {
|
||||
min-width: 350px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.sponsorSkipNotice {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -271,7 +272,7 @@ div:hover > .sponsorBlockChapterBar {
|
||||
max-width: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.sponsorSkipNotice .hidden {
|
||||
.sponsorSkipNotice .sbhidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -570,8 +571,8 @@ div:hover > .sponsorBlockChapterBar {
|
||||
.sponsorTimeEditButton {
|
||||
text-decoration: underline;
|
||||
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
margin-left: 13px;
|
||||
margin-right: 13px;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
@@ -690,7 +691,7 @@ input::-webkit-inner-spin-button {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.skipButtonControlBarContainer.hidden {
|
||||
.skipButtonControlBarContainer.sbhidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -741,6 +742,7 @@ input::-webkit-inner-spin-button {
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
z-index: 10000;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.sponsorBlockTooltip a {
|
||||
@@ -763,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;
|
||||
}
|
||||
|
||||
@@ -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>.
|
||||
</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 class="smallText">
|
||||
|
||||
@@ -323,3 +323,32 @@ svg {
|
||||
background-color: var(--disabled);
|
||||
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;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ input[type='number'] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
.hidden, .sbhidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -718,3 +718,15 @@ svg {
|
||||
width: 40px;
|
||||
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;
|
||||
}
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div id="deArrowPromotion" class="promotion-container" class="hidden">
|
||||
<div id="deArrowPromotion" class="promotion-container hidden">
|
||||
<a class="dearrow-link"
|
||||
href="https://dearrow.ajay.app"
|
||||
target="_blank"
|
||||
@@ -75,6 +75,8 @@
|
||||
<span class="promotion-description">
|
||||
__MSG_DeArrowPromotionMessage__
|
||||
</span>
|
||||
|
||||
<img src="/icons/close.png" class="close-button"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -357,6 +359,18 @@
|
||||
<div class="small-description">__MSG_showTimeWithSkipsDescription__</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 class="switch-container">
|
||||
<label class="switch">
|
||||
@@ -414,6 +428,11 @@
|
||||
<div class="inline"></div>
|
||||
</div>
|
||||
|
||||
<div data-type="keybind-change" data-sync="skipToHighlightKeybind">
|
||||
<label class="optionLabel">__MSG_skip_to_category__:</label>
|
||||
<div class="inline"></div>
|
||||
</div>
|
||||
|
||||
<div data-type="keybind-change" data-sync="startSponsorKeybind">
|
||||
<label class="optionLabel">__MSG_setStartSponsorShortcut__:</label>
|
||||
<div class="inline"></div>
|
||||
|
||||
@@ -164,7 +164,7 @@ SOFTWARE.
|
||||
******************************
|
||||
|
||||
webext-content-scripts
|
||||
2.5.2 <https://github.com/fregante/webext-content-scripts>
|
||||
2.5.5 <https://github.com/fregante/webext-content-scripts>
|
||||
MIT License
|
||||
|
||||
Copyright (c) Federico Brigante <me@fregante.com> (https://fregante.com)
|
||||
@@ -194,7 +194,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
******************************
|
||||
|
||||
webext-polyfill-kinda
|
||||
1.0.0 <https://github.com/fregante/webext-polyfill-kinda>
|
||||
1.0.2 <https://github.com/fregante/webext-polyfill-kinda>
|
||||
MIT License
|
||||
|
||||
Copyright (c) Federico Brigante <me@fregante.com> (https://fregante.com)
|
||||
|
||||
@@ -19,7 +19,7 @@ body {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
.hidden, .sbhidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#sponsorBlockPopupBody .hidden {
|
||||
#sponsorBlockPopupBody .hidden, #sponsorBlockPopupBody .sbhidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
opacity: 0.5;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sbCloseButton:hover {
|
||||
@@ -260,19 +261,6 @@
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
__MSG_betaServerWarning__
|
||||
</div>
|
||||
|
||||
<header class="sbPopupLogo">
|
||||
<header id="sbPopupLogo" class="sbPopupLogo">
|
||||
<img src="icons/IconSponsorBlocker256px.png" alt="SponsorBlock" width="40" height="40" id="sponsorBlockPopupLogo">
|
||||
<p class="u-mZ">SponsorBlock</p>
|
||||
</header>
|
||||
@@ -111,7 +111,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Your Work box -->
|
||||
<div class="sbYourWorkBox">
|
||||
<div id="sbYourWorkBox" class="sbYourWorkBox">
|
||||
<h1 class="sbHeader" style="padding: 8px 15px;">
|
||||
__MSG_yourWork__
|
||||
</h1>
|
||||
@@ -195,7 +195,7 @@
|
||||
<a href="https://github.com/ajayyy/SponsorBlock" target="_blank" rel="noopener">GitHub</a>
|
||||
<a href="https://discord.gg/SponsorBlock" target="_blank" rel="noopener">Discord</a>
|
||||
<a href="https://matrix.to/#/#sponsor:ajay.app?via=ajay.app&via=matrix.org&via=mozilla.org" target="_blank" rel="noopener">Matrix</a>
|
||||
<a href="https://sponsor.ajay.app/donate" target="_blank" rel="noopener" id="sbDonate">$</a>
|
||||
<a href="https://sponsor.ajay.app/donate" target="_blank" rel="noopener" id="sbDonate">__MSG_Donate__</a>
|
||||
</footer>
|
||||
|
||||
<button id="showNoticeAgain" style="display: none">__MSG_showNotice__</button>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.sponsorSkipNoticeParent {
|
||||
min-width: 350px;
|
||||
min-width: 390px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
max-width: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.sponsorSkipNotice .hidden {
|
||||
.sponsorSkipNotice .sbhidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -217,3 +217,16 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buttons that appear under a segment on click
|
||||
*/
|
||||
.voteButton {
|
||||
height: 20px;
|
||||
padding: 0 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.voteButton:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { getExtensionIdsToImportFrom } from "./utils/crossExtension";
|
||||
import { isFirefoxOrSafari } from "../maze-utils/src";
|
||||
import { injectUpdatedScripts } from "../maze-utils/src/cleanup";
|
||||
import { logWarn } from "./utils/logger";
|
||||
import { chromeP } from "../maze-utils/src/browserApi";
|
||||
const utils = new Utils({
|
||||
registerFirefoxContentScript,
|
||||
unregisterFirefoxContentScript
|
||||
@@ -27,7 +28,7 @@ const popupPort: Record<string, chrome.runtime.Port> = {};
|
||||
const contentScriptRegistrations = {};
|
||||
|
||||
// Register content script if needed
|
||||
utils.wait(() => Config.config !== null).then(function() {
|
||||
utils.wait(() => Config.isReady()).then(function() {
|
||||
if (Config.config.supportInvidious) utils.setupExtraSiteContentScripts();
|
||||
});
|
||||
|
||||
@@ -75,7 +76,11 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
||||
case "infoUpdated":
|
||||
case "videoChanged":
|
||||
if (sender.tab) {
|
||||
try {
|
||||
popupPort[sender.tab.id]?.postMessage(request);
|
||||
} catch (e) {
|
||||
// This can happen if the popup is closed
|
||||
}
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
@@ -148,29 +153,62 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
*
|
||||
* @param {JSON} options
|
||||
*/
|
||||
function registerFirefoxContentScript(options: Registration) {
|
||||
const oldRegistration = contentScriptRegistrations[options.id];
|
||||
if (oldRegistration) oldRegistration.unregister();
|
||||
async function registerFirefoxContentScript(options: Registration) {
|
||||
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||
const existingRegistrations = await chromeP.scripting.getRegisteredContentScripts({
|
||||
ids: [options.id]
|
||||
}).catch(() => []);
|
||||
|
||||
chrome.contentScripts.register({
|
||||
if (existingRegistrations.length > 0
|
||||
&& existingRegistrations[0].matches.every((match) => options.matches.includes(match))) {
|
||||
// No need to register another script, already registered
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await unregisterFirefoxContentScript(options.id);
|
||||
|
||||
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||
await chromeP.scripting.registerContentScripts([{
|
||||
id: options.id,
|
||||
runAt: "document_start",
|
||||
matches: options.matches,
|
||||
allFrames: options.allFrames,
|
||||
js: options.js,
|
||||
css: options.css,
|
||||
persistAcrossSessions: true,
|
||||
}]);
|
||||
} else {
|
||||
chrome.contentScripts.register({
|
||||
allFrames: options.allFrames,
|
||||
js: options.js?.map?.(file => ({file})),
|
||||
css: options.css?.map?.(file => ({file})),
|
||||
matches: options.matches
|
||||
}).then((registration) => void (contentScriptRegistrations[options.id] = registration));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Only works on Firefox.
|
||||
* Firefox requires that this is handled by the background script
|
||||
*
|
||||
*/
|
||||
function unregisterFirefoxContentScript(id: string) {
|
||||
async function unregisterFirefoxContentScript(id: string) {
|
||||
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||
try {
|
||||
await chromeP.scripting.unregisterContentScripts({
|
||||
ids: [id]
|
||||
});
|
||||
} catch (e) {
|
||||
// Not registered yet
|
||||
}
|
||||
} else {
|
||||
if (contentScriptRegistrations[id]) {
|
||||
contentScriptRegistrations[id].unregister();
|
||||
delete contentScriptRegistrations[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function submitVote(type: number, UUID: string, category: string) {
|
||||
let userID = Config.config.userID;
|
||||
|
||||
@@ -44,7 +44,7 @@ class ChapterVoteComponent extends React.Component<ChapterVoteProps, ChapterVote
|
||||
<>
|
||||
{/* Upvote Button */}
|
||||
<button id={"sponsorTimesDownvoteButtonsContainerUpvoteChapter"}
|
||||
className={"playerButton sbPlayerUpvote ytp-button " + (!this.state.show ? "hidden" : "")}
|
||||
className={"playerButton sbPlayerUpvote ytp-button " + (!this.state.show ? "sbhidden" : "")}
|
||||
draggable="false"
|
||||
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||
onClick={(e) => this.vote(e, 1)}>
|
||||
@@ -55,7 +55,7 @@ class ChapterVoteComponent extends React.Component<ChapterVoteProps, ChapterVote
|
||||
|
||||
{/* Downvote Button */}
|
||||
<button id={"sponsorTimesDownvoteButtonsContainerDownvoteChapter"}
|
||||
className={"playerButton sbPlayerDownvote ytp-button " + (!this.state.show ? "hidden" : "")}
|
||||
className={"playerButton sbPlayerDownvote ytp-button " + (!this.state.show ? "sbhidden" : "")}
|
||||
draggable="false"
|
||||
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -196,21 +196,21 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
||||
<span
|
||||
id={"skipNoticeTimerText" + this.idSuffix}
|
||||
key="skipNoticeTimerText"
|
||||
className={this.state.countdownMode !== CountdownMode.Timer ? "hidden" : ""} >
|
||||
className={this.state.countdownMode !== CountdownMode.Timer ? "sbhidden" : ""} >
|
||||
{chrome.i18n.getMessage("NoticeTimeAfterSkip").replace("{seconds}", this.state.countdownTime.toString())}
|
||||
</span>
|
||||
),(
|
||||
<img
|
||||
id={"skipNoticeTimerPaused" + this.idSuffix}
|
||||
key="skipNoticeTimerPaused"
|
||||
className={this.state.countdownMode !== CountdownMode.Paused ? "hidden" : ""}
|
||||
className={this.state.countdownMode !== CountdownMode.Paused ? "sbhidden" : ""}
|
||||
src={chrome.runtime.getURL("icons/pause.svg")}
|
||||
alt={chrome.i18n.getMessage("paused")} />
|
||||
),(
|
||||
<img
|
||||
id={"skipNoticeTimerStopped" + this.idSuffix}
|
||||
key="skipNoticeTimerStopped"
|
||||
className={this.state.countdownMode !== CountdownMode.Stopped ? "hidden" : ""}
|
||||
className={this.state.countdownMode !== CountdownMode.Stopped ? "sbhidden" : ""}
|
||||
src={chrome.runtime.getURL("icons/stop.svg")}
|
||||
alt={chrome.i18n.getMessage("manualPaused")} />
|
||||
)];
|
||||
|
||||
@@ -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;
|
||||
@@ -128,6 +126,14 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
style={timeDisplayStyle}
|
||||
className="sponsorTimeDisplay">
|
||||
|
||||
{sponsorTime.actionType !== ActionType.Poi ? (
|
||||
<span id={"startButton" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeTo(0, 0)}>
|
||||
{chrome.i18n.getMessage("bracketStart")}
|
||||
</span>
|
||||
): ""}
|
||||
|
||||
<span id={"nowButton0" + this.idSuffix}
|
||||
className="sponsorNowButton"
|
||||
onClick={() => this.setTimeToNow(0)}>
|
||||
@@ -138,6 +144,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
type="text"
|
||||
style={{color: "inherit", backgroundColor: "inherit"}}
|
||||
value={this.state.sponsorTimeEdits[0]}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onKeyUp={(e) => e.stopPropagation()}
|
||||
onChange={(e) => this.handleOnChange(0, e, sponsorTime, e.target.value)}
|
||||
onWheel={(e) => this.changeTimesWhenScrolling(0, e, sponsorTime)}>
|
||||
</input>
|
||||
@@ -153,6 +161,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
type="text"
|
||||
style={{color: "inherit", backgroundColor: "inherit"}}
|
||||
value={this.state.sponsorTimeEdits[1]}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onKeyUp={(e) => e.stopPropagation()}
|
||||
onChange={(e) => this.handleOnChange(1, e, sponsorTime, e.target.value)}
|
||||
onWheel={(e) => this.changeTimesWhenScrolling(1, e, sponsorTime)}>
|
||||
</input>
|
||||
@@ -238,6 +248,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
ref={this.descriptionOptionRef}
|
||||
type="text"
|
||||
value={this.state.description}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onKeyUp={(e) => e.stopPropagation()}
|
||||
onContextMenu={(e) => e.stopPropagation()}
|
||||
onChange={(e) => this.descriptionUpdate(e.target.value)}
|
||||
onFocus={() => this.setState({chapterNameSelectorOpen: true})}>
|
||||
@@ -282,11 +294,10 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
</span>
|
||||
): ""}
|
||||
|
||||
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType))
|
||||
&& sponsorTime.actionType === ActionType.Chapter ? (
|
||||
<span id={"sponsorTimePreviewButton" + this.idSuffix}
|
||||
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
|
||||
<span id={"sponsorTimePreviewEndButton" + this.idSuffix}
|
||||
className="sponsorTimeEditButton"
|
||||
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey)}>
|
||||
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey, true)}>
|
||||
{chrome.i18n.getMessage("End")}
|
||||
</span>
|
||||
): ""}
|
||||
@@ -577,7 +588,24 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
getFormattedTime(sponsorTime.segment[1], true)];
|
||||
}
|
||||
|
||||
lastEditTime = 0;
|
||||
editTimeTimeout: NodeJS.Timeout | null = null;
|
||||
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 category = this.categoryOptionRef.current.value as Category
|
||||
|
||||
@@ -624,7 +652,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
: CompileConfig.categorySupport[category]?.[0] ?? ActionType.Skip
|
||||
}
|
||||
|
||||
previewTime(ctrlPressed = false, shiftPressed = false): void {
|
||||
previewTime(ctrlPressed = false, shiftPressed = false, skipToEndTime = false): void {
|
||||
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
const index = this.props.index;
|
||||
let seekTime = 2;
|
||||
@@ -633,13 +661,11 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
|
||||
const startTime = sponsorTimes[index].segment[0];
|
||||
const endTime = sponsorTimes[index].segment[1];
|
||||
const isChapter = sponsorTimes[index].actionType === ActionType.Chapter;
|
||||
|
||||
// If segment starts at 0:00, start playback at the end of the segment
|
||||
const skipToEndTime = startTime === 0 || isChapter;
|
||||
const skipTime = skipToEndTime ? endTime : (startTime - (seekTime * this.props.contentContainer().v.playbackRate));
|
||||
const skipTime = (startTime === 0 || skipToEndTime) ? endTime : (startTime - (seekTime * this.props.contentContainer().v.playbackRate));
|
||||
|
||||
this.props.contentContainer().previewTime(skipTime, !isChapter);
|
||||
this.props.contentContainer().previewTime(skipTime, !skipToEndTime);
|
||||
}
|
||||
|
||||
inspectTime(): void {
|
||||
@@ -699,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
|
||||
});
|
||||
|
||||
@@ -38,6 +38,8 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
|
||||
guidelinesReminder: GenericNotice;
|
||||
|
||||
lastSegmentCount: number;
|
||||
|
||||
constructor(props: SubmissionNoticeProps) {
|
||||
super(props);
|
||||
this.noticeRef = React.createRef();
|
||||
@@ -47,12 +49,14 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
|
||||
const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
|
||||
|
||||
this.lastSegmentCount = this.props.contentContainer().sponsorTimesSubmitting.length;
|
||||
|
||||
// Setup state
|
||||
this.state = {
|
||||
noticeTitle,
|
||||
messages: [],
|
||||
idSuffix: "SubmissionNotice"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
const sortButton =
|
||||
<img id={"sponsorSkipSortButton" + this.state.idSuffix}
|
||||
|
||||
@@ -75,7 +75,10 @@ interface SBConfig {
|
||||
allowScrollingToEdit: boolean;
|
||||
deArrowInstalled: boolean;
|
||||
showDeArrowPromotion: boolean;
|
||||
showDeArrowInSettings: boolean;
|
||||
shownDeArrowPromotion: boolean;
|
||||
showZoomToFillError2: boolean;
|
||||
cleanPopup: boolean;
|
||||
|
||||
// Used to cache calculated text color info
|
||||
categoryPillColors: {
|
||||
@@ -86,6 +89,7 @@ interface SBConfig {
|
||||
};
|
||||
|
||||
skipKeybind: Keybind;
|
||||
skipToHighlightKeybind: Keybind;
|
||||
startSponsorKeybind: Keybind;
|
||||
submitKeybind: Keybind;
|
||||
nextChapterKeybind: Keybind;
|
||||
@@ -316,7 +320,10 @@ const syncDefaults = {
|
||||
allowScrollingToEdit: true,
|
||||
deArrowInstalled: false,
|
||||
showDeArrowPromotion: true,
|
||||
showDeArrowInSettings: true,
|
||||
shownDeArrowPromotion: false,
|
||||
showZoomToFillError2: true,
|
||||
cleanPopup: false,
|
||||
|
||||
categoryPillColors: {},
|
||||
|
||||
@@ -328,6 +335,7 @@ const syncDefaults = {
|
||||
* TODO: Find a way to skip having to update these checks. Maybe storing keybinds in a Map?
|
||||
*/
|
||||
skipKeybind: { key: "Enter" },
|
||||
skipToHighlightKeybind: { key: "Enter", ctrl: true },
|
||||
startSponsorKeybind: { key: ";" },
|
||||
submitKeybind: { key: "'" },
|
||||
nextChapterKeybind: { key: "ArrowRight", ctrl: true },
|
||||
|
||||
147
src/content.ts
147
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,47 +57,12 @@ 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 > 100 || !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();
|
||||
});
|
||||
|
||||
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
|
||||
let sponsorDataFound = false;
|
||||
@@ -110,6 +75,7 @@ const skipNotices: SkipNotice[] = [];
|
||||
let activeSkipKeybindElement: ToggleSkippable = null;
|
||||
let retryFetchTimeout: NodeJS.Timeout = null;
|
||||
let shownSegmentFailedToFetchWarning = false;
|
||||
let selectedSegment: SegmentUUID | null = null;
|
||||
|
||||
// JSON video info
|
||||
let videoInfo: VideoInfo = null;
|
||||
@@ -300,6 +266,9 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
||||
case "reskip":
|
||||
reskipSponsorTime(sponsorTimes.find((segment) => segment.UUID === request.UUID), true);
|
||||
break;
|
||||
case "selectSegment":
|
||||
selectSegment(request.UUID);
|
||||
break;
|
||||
case "submitVote":
|
||||
vote(request.type, request.UUID).then((response) => sendResponse(response));
|
||||
return true;
|
||||
@@ -434,6 +403,8 @@ function resetValues() {
|
||||
for (let i = 0; i < skipNotices.length; i++) {
|
||||
skipNotices.pop()?.close();
|
||||
}
|
||||
|
||||
hideDeArrowPromotion();
|
||||
}
|
||||
|
||||
function videoIDChange(): void {
|
||||
@@ -474,6 +445,8 @@ function videoIDChange(): void {
|
||||
// Clear unsubmitted segments from the previous video
|
||||
sponsorTimesSubmitting = [];
|
||||
updateSponsorTimesSubmitting();
|
||||
|
||||
tryShowingDeArrowPromotion().catch(logWarn);
|
||||
}
|
||||
|
||||
function handleMobileControlsMutations(): void {
|
||||
@@ -618,7 +591,8 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
|
||||
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 currentSkip = skipInfo.array[skipInfo.index];
|
||||
@@ -697,8 +671,12 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
forcedSkipTime = skipTime[0] + 0.001;
|
||||
} else {
|
||||
forcedSkipTime = skipTime[1];
|
||||
forcedIncludeIntersectingSegments = true;
|
||||
forcedIncludeNonIntersectingSegments = false;
|
||||
|
||||
// Only if not at the end of the video
|
||||
if (Math.abs(skipTime[1] - getVideo().duration) > endTimeSkipBuffer) {
|
||||
forcedIncludeIntersectingSegments = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
forcedSkipTime = forceVideoTime + 0.001;
|
||||
@@ -819,6 +797,8 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
|
||||
}
|
||||
}
|
||||
|
||||
let playbackRateCheckInterval: NodeJS.Timeout | null = null;
|
||||
let lastPlaybackSpeed = 1;
|
||||
let setupVideoListenersFirstTime = true;
|
||||
function setupVideoListeners() {
|
||||
//wait until it is loaded
|
||||
@@ -838,6 +818,16 @@ function setupVideoListeners() {
|
||||
let startedWaiting = false;
|
||||
let lastPausedAtZero = true;
|
||||
|
||||
const rateChangeListener = () => {
|
||||
updateVirtualTime();
|
||||
clearWaitingTime();
|
||||
|
||||
startSponsorSchedule();
|
||||
};
|
||||
getVideo().addEventListener('ratechange', rateChangeListener);
|
||||
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
|
||||
getVideo().addEventListener('videoSpeed_ratechange', rateChangeListener);
|
||||
|
||||
const playListener = () => {
|
||||
// If it is not the first event, then the only way to get to 0 is if there is a seek event
|
||||
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
||||
@@ -868,7 +858,6 @@ function setupVideoListeners() {
|
||||
|
||||
startSponsorSchedule();
|
||||
}
|
||||
|
||||
};
|
||||
getVideo().addEventListener('play', playListener);
|
||||
|
||||
@@ -898,6 +887,27 @@ function setupVideoListeners() {
|
||||
|
||||
startSponsorSchedule();
|
||||
}
|
||||
|
||||
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||
lastPlaybackSpeed = getVideo().playbackRate;
|
||||
|
||||
// Video speed controller compatibility
|
||||
// That extension makes rate change events not propagate
|
||||
if (document.body.classList.contains("vsc-initialized")) {
|
||||
playbackRateCheckInterval = setInterval(() => {
|
||||
if ((!getVideoID() || getVideo().paused) && playbackRateCheckInterval) {
|
||||
// Video is gone, stop checking
|
||||
clearInterval(playbackRateCheckInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getVideo().playbackRate !== lastPlaybackSpeed) {
|
||||
lastPlaybackSpeed = getVideo().playbackRate;
|
||||
|
||||
rateChangeListener();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
getVideo().addEventListener('playing', playingListener);
|
||||
|
||||
@@ -928,21 +938,13 @@ function setupVideoListeners() {
|
||||
};
|
||||
getVideo().addEventListener('seeking', seekingListener);
|
||||
|
||||
const rateChangeListener = () => {
|
||||
updateVirtualTime();
|
||||
clearWaitingTime();
|
||||
|
||||
startSponsorSchedule();
|
||||
};
|
||||
getVideo().addEventListener('ratechange', () => rateChangeListener);
|
||||
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
|
||||
getVideo().addEventListener('videoSpeed_ratechange', rateChangeListener);
|
||||
|
||||
const stoppedPlayback = () => {
|
||||
// Reset lastCheckVideoTime
|
||||
lastCheckVideoTime = -1;
|
||||
lastCheckTime = 0;
|
||||
|
||||
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||
|
||||
lastKnownVideoTime.videoTime = null;
|
||||
lastKnownVideoTime.preciseTime = null;
|
||||
updateWaitingTime();
|
||||
@@ -974,6 +976,8 @@ function setupVideoListeners() {
|
||||
getVideo().removeEventListener('videoSpeed_ratechange', rateChangeListener);
|
||||
getVideo().removeEventListener('pause', pauseListener);
|
||||
getVideo().removeEventListener('waiting', waitingListener);
|
||||
|
||||
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1040,6 +1044,7 @@ function setupSkipButtonControlBar() {
|
||||
openNotice: true,
|
||||
forceAutoSkip: true
|
||||
}),
|
||||
selectSegment,
|
||||
onMobileYouTube: isOnMobileYouTube()
|
||||
});
|
||||
}
|
||||
@@ -1074,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}`,
|
||||
@@ -1214,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 {
|
||||
@@ -1318,6 +1323,11 @@ function updatePreviewBarPositionMobile(parent: HTMLElement) {
|
||||
}
|
||||
}
|
||||
|
||||
function selectSegment(UUID: SegmentUUID): void {
|
||||
selectedSegment = UUID;
|
||||
updatePreviewBar();
|
||||
}
|
||||
|
||||
function updatePreviewBar(): void {
|
||||
if (previewBar === null) return;
|
||||
|
||||
@@ -1343,7 +1353,8 @@ function updatePreviewBar(): void {
|
||||
showLarger: segment.actionType === ActionType.Poi,
|
||||
description: segment.description,
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1602,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1628,6 +1639,9 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
||||
// MacOS will loop otherwise #1027
|
||||
// Sometimes playlists loop too #1804
|
||||
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 {
|
||||
if (inMuteSegment(skipTime[1], true)) {
|
||||
// Make sure not to mute if skipping into a mute segment
|
||||
@@ -2095,7 +2109,7 @@ async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNo
|
||||
//success (treat rate limits as a success)
|
||||
skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
|
||||
} else if (response.successType == -1) {
|
||||
if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a warning from a moderator.")) {
|
||||
if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a tip from a moderator.")) {
|
||||
openWarningDialog(skipNoticeContentContainer);
|
||||
} else {
|
||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)(getErrorMessage(response.statusCode, response.responseText))
|
||||
@@ -2235,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,
|
||||
@@ -2285,7 +2299,7 @@ async function sendSubmitMessage() {
|
||||
playerButtons.submit.button.style.animation = "unset";
|
||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
||||
|
||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
|
||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a tip from a moderator.")) {
|
||||
openWarningDialog(skipNoticeContentContainer);
|
||||
} else {
|
||||
alert(getErrorMessage(response.status, response.responseText));
|
||||
@@ -2400,14 +2414,23 @@ function hotkeyListener(e: KeyboardEvent): void {
|
||||
};
|
||||
|
||||
const skipKey = Config.config.skipKeybind;
|
||||
const skipToHighlightKey = Config.config.skipToHighlightKeybind;
|
||||
const startSponsorKey = Config.config.startSponsorKeybind;
|
||||
const submitKey = Config.config.submitKeybind;
|
||||
const nextChapterKey = Config.config.nextChapterKeybind;
|
||||
const previousChapterKey = Config.config.previousChapterKeybind;
|
||||
|
||||
if (keybindEquals(key, skipKey)) {
|
||||
if (activeSkipKeybindElement)
|
||||
if (activeSkipKeybindElement) {
|
||||
activeSkipKeybindElement.toggleSkip.call(activeSkipKeybindElement);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (keybindEquals(key, skipToHighlightKey)) {
|
||||
if (skipButtonControlBar) {
|
||||
skipButtonControlBar.toggleSkip.call(skipButtonControlBar);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (keybindEquals(key, startSponsorKey)) {
|
||||
startOrEndTimingNewSegment();
|
||||
|
||||
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 { waitFor } from "../maze-utils/src";
|
||||
import { isDeArrowInstalled } from "./utils/crossExtension";
|
||||
|
||||
if (document.readyState === "complete") {
|
||||
init();
|
||||
@@ -10,6 +11,32 @@ if (document.readyState === "complete") {
|
||||
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() {
|
||||
localizeHtmlPage();
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface PreviewBarSegment {
|
||||
description: string;
|
||||
source: SponsorSourceType;
|
||||
requiredSegment?: boolean;
|
||||
selectedSegment?: boolean;
|
||||
}
|
||||
|
||||
interface ChapterGroup extends SegmentContainer {
|
||||
@@ -332,6 +333,7 @@ class PreviewBar {
|
||||
const bar = document.createElement('li');
|
||||
bar.classList.add('previewbar');
|
||||
if (barSegment.requiredSegment) bar.classList.add("requiredSegment");
|
||||
if (barSegment.selectedSegment) bar.classList.add("selectedSegment");
|
||||
bar.innerHTML = showLarger ? ' ' : ' ';
|
||||
|
||||
const fullCategoryName = (unsubmitted ? 'preview-' : '') + category;
|
||||
@@ -784,11 +786,13 @@ class PreviewBar {
|
||||
if (!Config.config.showSegmentNameInChapterBar
|
||||
|| ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) {
|
||||
const chaptersContainer = this.getChaptersContainer();
|
||||
if (chaptersContainer) {
|
||||
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
|
||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||
|
||||
chapterTitle.style.removeProperty("display");
|
||||
chaptersContainer.classList.remove("sponsorblock-chapter-visible");
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import Config from "../config";
|
||||
import { SponsorTime } from "../types";
|
||||
import { SegmentUUID, SponsorTime } from "../types";
|
||||
import { getSkippingText } from "../utils/categoryUtils";
|
||||
import { AnimationUtils } from "../utils/animationUtils";
|
||||
import { keybindToString } from "../../maze-utils/src/config";
|
||||
|
||||
export interface SkipButtonControlBarProps {
|
||||
skip: (segment: SponsorTime) => void;
|
||||
selectSegment: (UUID: SegmentUUID) => void;
|
||||
onMobileYouTube: boolean;
|
||||
}
|
||||
|
||||
@@ -41,7 +42,7 @@ export class SkipButtonControlBar {
|
||||
|
||||
this.container = document.createElement("div");
|
||||
this.container.classList.add("skipButtonControlBarContainer");
|
||||
this.container.classList.add("hidden");
|
||||
this.container.classList.add("sbhidden");
|
||||
if (this.onMobileYouTube) this.container.classList.add("mobile");
|
||||
|
||||
this.skipIcon = document.createElement("img");
|
||||
@@ -54,8 +55,18 @@ export class SkipButtonControlBar {
|
||||
this.container.appendChild(this.skipIcon);
|
||||
this.container.appendChild(this.textContainer);
|
||||
this.container.addEventListener("click", () => this.toggleSkip());
|
||||
this.container.addEventListener("mouseenter", () => this.stopTimer());
|
||||
this.container.addEventListener("mouseleave", () => this.startTimer());
|
||||
this.container.addEventListener("mouseenter", () => {
|
||||
this.stopTimer();
|
||||
|
||||
if (this.segment) {
|
||||
props.selectSegment(this.segment.UUID);
|
||||
}
|
||||
});
|
||||
this.container.addEventListener("mouseleave", () => {
|
||||
this.startTimer();
|
||||
|
||||
props.selectSegment(null);
|
||||
});
|
||||
if (this.onMobileYouTube) {
|
||||
this.container.addEventListener("touchstart", (e) => this.handleTouchStart(e));
|
||||
this.container.addEventListener("touchmove", (e) => this.handleTouchMove(e));
|
||||
@@ -103,7 +114,7 @@ export class SkipButtonControlBar {
|
||||
|
||||
this.refreshText();
|
||||
this.container?.classList?.remove("textDisabled");
|
||||
this.textContainer?.classList?.remove("hidden");
|
||||
this.textContainer?.classList?.remove("sbhidden");
|
||||
AnimationUtils.disableAutoHideAnimation(this.skipIcon);
|
||||
|
||||
this.startTimer();
|
||||
@@ -111,8 +122,8 @@ export class SkipButtonControlBar {
|
||||
|
||||
refreshText(): void {
|
||||
if (this.segment) {
|
||||
this.chapterText?.classList?.add("hidden");
|
||||
this.container.classList.remove("hidden");
|
||||
this.chapterText?.classList?.add("sbhidden");
|
||||
this.container.classList.remove("sbhidden");
|
||||
this.textContainer.innerText = this.getTitle();
|
||||
this.skipIcon.setAttribute("title", this.getTitle());
|
||||
}
|
||||
@@ -134,10 +145,10 @@ export class SkipButtonControlBar {
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
this.container.classList.add("hidden");
|
||||
this.container.classList.add("sbhidden");
|
||||
|
||||
this.chapterText?.classList?.remove("hidden");
|
||||
this.getChapterPrefix()?.classList?.remove("hidden");
|
||||
this.chapterText?.classList?.remove("sbhidden");
|
||||
this.getChapterPrefix()?.classList?.remove("sbhidden");
|
||||
|
||||
this.enabled = false;
|
||||
}
|
||||
@@ -147,9 +158,11 @@ export class SkipButtonControlBar {
|
||||
}
|
||||
|
||||
toggleSkip(): void {
|
||||
if (this.segment && this.enabled) {
|
||||
this.skip(this.segment);
|
||||
this.disableText();
|
||||
}
|
||||
}
|
||||
|
||||
disableText(): void {
|
||||
if (Config.config.hideSkipButtonPlayerControls) {
|
||||
@@ -158,10 +171,10 @@ export class SkipButtonControlBar {
|
||||
}
|
||||
|
||||
this.container.classList.add("textDisabled");
|
||||
this.textContainer?.classList?.add("hidden");
|
||||
this.chapterText?.classList?.remove("hidden");
|
||||
this.textContainer?.classList?.add("sbhidden");
|
||||
this.chapterText?.classList?.remove("sbhidden");
|
||||
|
||||
this.getChapterPrefix()?.classList?.add("hidden");
|
||||
this.getChapterPrefix()?.classList?.add("sbhidden");
|
||||
|
||||
AnimationUtils.enableAutoHideAnimation(this.skipIcon);
|
||||
if (this.onMobileYouTube) {
|
||||
@@ -182,7 +195,7 @@ export class SkipButtonControlBar {
|
||||
}
|
||||
|
||||
private getTitle(): string {
|
||||
return getSkippingText([this.segment], false) + (this.showKeybindHint ? " (" + keybindToString(Config.config.skipKeybind) + ")" : "");
|
||||
return getSkippingText([this.segment], false) + (this.showKeybindHint ? " (" + keybindToString(Config.config.skipToHighlightKeybind) + ")" : "");
|
||||
}
|
||||
|
||||
private getChapterPrefix(): HTMLElement {
|
||||
|
||||
@@ -31,7 +31,7 @@ interface IsInfoFoundMessage {
|
||||
}
|
||||
|
||||
interface SkipMessage {
|
||||
message: "unskip" | "reskip";
|
||||
message: "unskip" | "reskip" | "selectSegment";
|
||||
UUID: SegmentUUID;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -74,17 +75,29 @@ async function init() {
|
||||
}
|
||||
|
||||
// DeArrow promotion
|
||||
if (Config.config.showNewFeaturePopups && Config.config.showUpsells) {
|
||||
if (Config.config.showNewFeaturePopups && Config.config.showUpsells && Config.config.showDeArrowInSettings) {
|
||||
isDeArrowInstalled().then((installed) => {
|
||||
if (!installed) {
|
||||
const deArrowPromotion = document.getElementById("deArrowPromotion");
|
||||
deArrowPromotion.classList.remove("hidden");
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const skipToHighlightKeybind = document.querySelector(`[data-sync="skipToHighlightKeybind"] .optionLabel`) as HTMLElement;
|
||||
skipToHighlightKeybind.innerText = `${chrome.i18n.getMessage("skip_to_category").replace("{0}", chrome.i18n.getMessage("category_poi_highlight")).replace("?", "")}:`;
|
||||
|
||||
// Set all of the toggle options to the correct option
|
||||
const optionsContainer = document.getElementById("options");
|
||||
const optionsElements = optionsContainer.querySelectorAll("*");
|
||||
@@ -555,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) => {
|
||||
|
||||
35
src/popup.ts
35
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();
|
||||
|
||||
@@ -108,6 +109,10 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
const PageElements: PageElements = {};
|
||||
|
||||
[
|
||||
"sbPopupLogo",
|
||||
"sbYourWorkBox",
|
||||
"videoInfo",
|
||||
"sbFooter",
|
||||
"sponsorBlockPopupBody",
|
||||
"sponsorblockPopup",
|
||||
"sponsorStart",
|
||||
@@ -198,6 +203,16 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
}
|
||||
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) {
|
||||
PageElements.sbBetaServerWarning.classList.remove("hidden");
|
||||
PageElements.sbBetaServerWarning.addEventListener("click", function () {
|
||||
@@ -281,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) => {
|
||||
@@ -687,6 +702,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
: chrome.i18n.getMessage("skipSegment");
|
||||
skipButton.addEventListener("click", () => skipSegment(actionType, UUID, skipButton));
|
||||
votingButtons.addEventListener("dblclick", () => skipSegment(actionType, UUID));
|
||||
votingButtons.addEventListener("dblclick", () => skipSegment(actionType, UUID));
|
||||
votingButtons.addEventListener("mouseenter", () => selectSegment(UUID));
|
||||
|
||||
//add thumbs up, thumbs down and uuid copy buttons to the container
|
||||
voteButtonsContainer.appendChild(upvoteButton);
|
||||
@@ -718,6 +735,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
|
||||
container.appendChild(votingButtons);
|
||||
}
|
||||
|
||||
container.addEventListener("mouseleave", () => selectSegment(null));
|
||||
}
|
||||
|
||||
function submitTimes() {
|
||||
@@ -800,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";
|
||||
@@ -968,6 +987,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function selectSegment(UUID: SegmentUUID | null): void {
|
||||
sendTabMessage({
|
||||
message: "selectSegment",
|
||||
UUID: UUID
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Should skipping be disabled (visuals stay)
|
||||
*/
|
||||
@@ -1041,9 +1067,10 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
*/
|
||||
function getFormattedHours(minutes) {
|
||||
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;
|
||||
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) {
|
||||
|
||||
@@ -99,8 +99,8 @@ export interface Registration {
|
||||
message: string;
|
||||
id: string;
|
||||
allFrames: boolean;
|
||||
js: browser.extensionTypes.ExtensionFileOrCode[];
|
||||
css: browser.extensionTypes.ExtensionFileOrCode[];
|
||||
js: string[];
|
||||
css: string[];
|
||||
matches: string[];
|
||||
}
|
||||
|
||||
|
||||
69
src/utils.ts
69
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 {
|
||||
@@ -47,9 +45,13 @@ export default class Utils {
|
||||
* @param {CallableFunction} callback
|
||||
*/
|
||||
setupExtraSitePermissions(callback: (granted: boolean) => void): void {
|
||||
let permissions = ["webNavigation"];
|
||||
if (!isSafari()) permissions.push("declarativeContent");
|
||||
if (isFirefoxOrSafari() && !isSafari()) permissions = [];
|
||||
const permissions = [];
|
||||
if (!isFirefoxOrSafari()) {
|
||||
permissions.push("declarativeContent");
|
||||
}
|
||||
if (!isFirefoxOrSafari() || isSafari()) {
|
||||
permissions.push("webNavigation");
|
||||
}
|
||||
|
||||
chrome.permissions.request({
|
||||
origins: this.getPermissionRegex(),
|
||||
@@ -73,21 +75,12 @@ export default class Utils {
|
||||
* For now, it is just SB.config.invidiousInstances.
|
||||
*/
|
||||
setupExtraSiteContentScripts(): void {
|
||||
const firefoxJS = [];
|
||||
for (const file of this.js) {
|
||||
firefoxJS.push({file});
|
||||
}
|
||||
const firefoxCSS = [];
|
||||
for (const file of this.css) {
|
||||
firefoxCSS.push({file});
|
||||
}
|
||||
|
||||
const registration: Registration = {
|
||||
message: "registerContentScript",
|
||||
id: "invidious",
|
||||
allFrames: true,
|
||||
js: firefoxJS,
|
||||
css: firefoxCSS,
|
||||
js: this.js,
|
||||
css: this.css,
|
||||
matches: this.getPermissionRegex()
|
||||
};
|
||||
|
||||
@@ -245,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
|
||||
|
||||
@@ -27,7 +27,7 @@ function applyLoadingAnimation(element: HTMLElement, time: number, callback?: ()
|
||||
|
||||
function setupCustomHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): { hide: () => void; show: () => void } {
|
||||
if (enabled) element.classList.add("autoHiding");
|
||||
element.classList.add("hidden");
|
||||
element.classList.add("sbhidden");
|
||||
element.classList.add("animationDone");
|
||||
if (!rightSlide) element.classList.add("autoHideLeft");
|
||||
|
||||
@@ -37,7 +37,7 @@ function setupCustomHideAnimation(element: Element, container: Element, enabled
|
||||
hide: () => {
|
||||
mouseEntered = false;
|
||||
if (element.classList.contains("autoHiding")) {
|
||||
element.classList.add("hidden");
|
||||
element.classList.add("sbhidden");
|
||||
}
|
||||
},
|
||||
show: () => {
|
||||
@@ -46,7 +46,7 @@ function setupCustomHideAnimation(element: Element, container: Element, enabled
|
||||
|
||||
// Wait for next event loop
|
||||
setTimeout(() => {
|
||||
if (mouseEntered) element.classList.remove("hidden")
|
||||
if (mouseEntered) element.classList.remove("sbhidden")
|
||||
}, 10);
|
||||
}
|
||||
};
|
||||
@@ -61,12 +61,12 @@ function setupAutoHideAnimation(element: Element, container: Element, enabled =
|
||||
|
||||
function enableAutoHideAnimation(element: Element): void {
|
||||
element.classList.add("autoHiding");
|
||||
element.classList.add("hidden");
|
||||
element.classList.add("sbhidden");
|
||||
}
|
||||
|
||||
function disableAutoHideAnimation(element: Element): void {
|
||||
element.classList.remove("autoHiding");
|
||||
element.classList.remove("hidden");
|
||||
element.classList.remove("sbhidden");
|
||||
}
|
||||
|
||||
export const AnimationUtils = {
|
||||
|
||||
@@ -106,5 +106,5 @@ export function exportTimesAsHashParam(segments: SponsorTime[]): string {
|
||||
|
||||
|
||||
export function normalizeChapterName(description: string): string {
|
||||
return description.toLowerCase().replace(/\.|:|-/g, "").replace(/\s+/g, " ");
|
||||
return description.toLowerCase().replace(/[.:'’`‛‘"‟”-]/ug, "").replace(/\s+/g, " ");
|
||||
}
|
||||
@@ -4,19 +4,16 @@ function getLuminance(color: string): number {
|
||||
return Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
|
||||
}
|
||||
|
||||
/* From https://stackoverflow.com/a/5624139 */
|
||||
function hexToRgb(hex: string): {r: number; g: number; b: number} {
|
||||
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
|
||||
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
||||
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
|
||||
return r + r + g + g + b + b;
|
||||
});
|
||||
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
/* Converts hex color to rgb color */
|
||||
const hexChars = "0123456789abcdef";
|
||||
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||||
if (hex.length == 4)
|
||||
hex = "#" + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
|
||||
return /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
? {
|
||||
r: hexChars.indexOf(hex[1]) * 16 + hexChars.indexOf(hex[2]),
|
||||
g: hexChars.indexOf(hex[3]) * 16 + hexChars.indexOf(hex[4]),
|
||||
b: hexChars.indexOf(hex[5]) * 16 + hexChars.indexOf(hex[6]),
|
||||
}: null;
|
||||
}
|
||||
|
||||
|
||||
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 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] = {
|
||||
|
||||
@@ -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,20 +12,20 @@ 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);
|
||||
|
||||
let notice: GenericNotice = null;
|
||||
const options: NoticeOptions = {
|
||||
title: chrome.i18n.getMessage("warningTitle"),
|
||||
title: chrome.i18n.getMessage("deArrowMessageRecieved"),
|
||||
textBoxes: [{
|
||||
text: chrome.i18n.getMessage("warningChatInfo"),
|
||||
icon: null
|
||||
@@ -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
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user