Compare commits

...

98 Commits

Author SHA1 Message Date
Ajay
52bd85b850 bump version 2023-11-08 18:24:51 -05:00
Ajay
09e7c41479 update translations 2023-11-08 18:24:44 -05:00
Ajay
bdcb2d05c7 Make submission and skip notices slightly larger 2023-11-08 18:23:37 -05:00
Ajay
a522e3065c Fix regex being treated as a range 2023-11-08 18:22:41 -05:00
Ajay
72c62d0fa4 Deduplicate different quotes when comparing chapter names 2023-11-08 18:19:28 -05:00
Ajay
133ea360d7 Fix error when submitting with required segments 2023-11-08 18:13:38 -05:00
Ajay
e722ded58a Add dearrow promo based on title and remove old one
Also refactor requests out to seperate file
2023-11-08 16:07:59 -05:00
Ajay
6d37180d00 Add option to hide clutter in popup 2023-11-07 21:05:39 -05:00
Ajay
14d50b9e70 Add dearrow link to install page and add close button 2023-11-07 20:49:25 -05:00
Ajay
cfe314742d Rename Preview/Recap to Preview/Recap/Hook 2023-11-07 15:34:53 -05:00
Ajay
9a71e8bb8c Disable dearrow promotion 2023-11-06 19:33:05 -05:00
Ajay
dc2c7cc425 bump version 2023-11-02 14:34:51 -04:00
Ajay
7bf3237b72 update translations 2023-11-02 14:34:09 -04:00
Ajay
b48c854926 Another fix for flashing on Firefox when skipping some segments at the end of the video 2023-11-02 14:25:29 -04:00
Ajay
0bb7bef52c Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-11-01 16:08:53 -04:00
Ajay
4ffa019c68 Scroll down when new segment added to notice 2023-11-01 16:08:52 -04:00
Ajay
9c2007e0cf Rate limit saving times to make scrolling the edit box less laggy 2023-11-01 15:53:40 -04:00
Ajay Ramachandran
9176854d56 Merge pull request #1903 from ajayyy/ci/update_invidious_list
Update Invidious List
2023-10-31 21:02:16 -04:00
github-actions[bot]
65c72d38ea Update Invidious List 2023-11-01 00:19:59 +00:00
Ajay
6f54c8a731 Make highlight bigger on timeline when skip to highlight button hovered 2023-10-31 01:16:36 -04:00
Ajay
ca7eb50a82 bump version 2023-10-23 16:03:10 -04:00
Ajay
a7030fab9f update translations 2023-10-23 16:02:59 -04:00
Ajay
0bb3528cde Add minimum version of Firefox to manifest 2023-10-23 16:00:10 -04:00
Ajay
c8c141f5c9 Round end time when skipping on firefox if close
Fix #1883
2023-10-18 12:13:11 -04:00
Ajay Ramachandran
88cfa023c9 Merge pull request #1880 from Choromanski/feature/remove-duplicate-id-sponsorTimePreviewButton
Renamed duplicate span id
2023-10-17 20:44:29 -04:00
Brian Choromanski
41a2fc2cb3 Assigned a new ID 'sponsorTimePreviewEndButton' to the newly added 'End' button 2023-10-17 17:06:41 -04:00
Ajay Ramachandran
0f0e404920 Merge pull request #1878 from Choromanski/feature/add-years-to-time-saved
Added years to time saved display
2023-10-15 21:36:05 -04:00
Brian Choromanski
f34fe5a032 Added years to time saved display 2023-10-15 21:23:49 -04:00
Ajay
e4c9afecbd bump version 2023-10-14 20:36:51 -04:00
Ajay
79e855a038 update translations 2023-10-14 20:36:45 -04:00
Ajay
09a3a4e6d4 Fix vote buttons in category pill missing not being centered 2023-10-14 20:32:48 -04:00
Ajay
e271f2cbcc Add gecko android to minimum version 2023-10-11 13:50:45 -04:00
Ajay
1cc4c18665 Fix video id not updating when changing videos on cytube and watchtogether 2023-10-08 20:22:45 -04:00
Ajay
e650b7183a bump version 2023-10-08 16:44:12 -04:00
Ajay
4eb097b422 update translations 2023-10-08 16:44:07 -04:00
Ajay
04a9f82bdc Rename hidden css class to sbhidden to fix compatibility with Piped
Fixes #1865
2023-10-08 16:38:40 -04:00
Ajay
39cfdc7b6c bump version 2023-10-01 13:57:51 -04:00
Ajay
a8701b02a1 update translations 2023-10-01 13:57:41 -04:00
Ajay
3f1ad528c3 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-10-01 13:56:44 -04:00
Ajay
ae685baeef Fix tip ui broken for submissions 2023-10-01 13:56:34 -04:00
Ajay Ramachandran
d2ee67f3cf Merge pull request #1867 from ajayyy/ci/update_invidious_list
Update Invidious List
2023-09-30 20:57:53 -04:00
github-actions[bot]
d440a4d52a Update Invidious List 2023-10-01 00:21:32 +00:00
Ajay
7566b71ccd Properly hide dearrow button 2023-09-30 17:00:10 -04:00
Ajay
109b7ed5bc Don't show start button for highlight 2023-09-24 21:13:43 -04:00
Ajay
3eb853154f Remove unnecessary translation string 2023-09-24 19:01:41 -04:00
Ajay
ee3ce8aa46 Add seperate keybind for skip to highlight
Fixes #1187
2023-09-23 15:40:11 -04:00
Ajay
1557af5d2a update translations 2023-09-23 14:27:29 -04:00
Ajay
465e7065ca Update warning title 2023-09-23 13:46:12 -04:00
Ajay
a3f8419c49 update translations 2023-09-23 12:20:05 -04:00
Ajay
dde443ccec bump version 2023-09-23 12:18:32 -04:00
Ajay
01b1380b78 Update sponsorblock ui to tip 2023-09-23 12:17:46 -04:00
Ajay
c51b18465e Fix slightly different tv url format that appears on Firefox 2023-09-18 14:17:46 -04:00
Ajay
ad9888cf52 bump version 2023-09-17 17:31:36 -04:00
Ajay
7856791f90 update translations 2023-09-17 17:31:22 -04:00
Ajay
273ee63ec7 Prevent refreshes from triggering too often
Fixes #1838
2023-09-17 14:06:24 -04:00
Ajay
be36583aee Rename donate button 2023-09-17 13:11:52 -04:00
Ajay
433bbbf904 Remove popup css from content as iframes are used now
Fixes #1774
2023-09-17 12:46:25 -04:00
Ajay
6c2ee76198 Add start button to submission menu 2023-09-17 12:29:42 -04:00
Ajay
42f59898f3 Add end button for all segments 2023-09-17 12:12:00 -04:00
Ajay
8ab126f502 Highlight segment on hover 2023-09-17 12:06:33 -04:00
Ajay
4954abf9e3 Add extension icon default location on Firefox 2023-09-13 20:17:44 -04:00
Ajay
30a21d5ff5 Actually stop spacebar in chapters box from pausing video 2023-09-13 11:40:56 -04:00
Ajay
d1b2def47c bump version 2023-09-06 11:45:08 -04:00
Ajay Ramachandran
48cdabe2a5 Merge pull request #1534 from EthanBnntt/EthanBnntt-patch-1
Reduced execution time of hexToRgb function by ~70%
2023-09-05 01:28:23 -04:00
Ajay Ramachandran
bc2db0cf2c Merge branch 'master' into EthanBnntt-patch-1 2023-09-05 01:28:15 -04:00
Ajay Ramachandran
843ef37dcd Merge pull request #1814 from ajayyy/ci/update_invidious_list
Update Invidious List
2023-09-05 01:25:54 -04:00
Ajay
ed260a0667 Fix clicking a video from homepage on mobile YouTube
Fixes https://github.com/ajayyy/SponsorBlock/issues/1849
2023-09-05 01:19:50 -04:00
Ajay
2e131c2a95 Stop spacebar in chapters box from pausing video 2023-09-04 02:37:19 -04:00
Ajay
f5e884b6aa Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-09-03 21:29:02 -04:00
Ajay
a9929d0c93 Adjust dearrow message params 2023-09-03 21:29:00 -04:00
github-actions[bot]
3fb43d1c0e Update Invidious List 2023-09-01 00:19:28 +00:00
Ajay Ramachandran
a1b2855538 Merge pull request #1842 from ajayyy/ci/oss_attribution
Update OSS Attribution
2023-08-25 21:49:27 -04:00
github-actions[bot]
07236baed5 Update OSS Attribution 2023-08-26 01:30:48 +00:00
Ajay
f991435857 Switch back to upstream content-scripts-register-polyfill 2023-08-25 21:29:39 -04:00
Ajay
faa3259165 Fix invidious support on Safari for iOS 2023-08-25 20:22:58 -04:00
Ajay
c96bafb6f7 bump version 2023-08-25 18:39:52 -04:00
Ajay
9b7680f0e6 Fix invidious support on Safari 2023-08-25 16:38:50 -04:00
Ajay
16e01b7494 Fix comment 2023-08-25 16:34:08 -04:00
Ajay
6cd697dc32 bump version 2023-08-25 16:03:33 -04:00
Ajay
9946bd1af2 Use chromep in another spot 2023-08-25 16:03:17 -04:00
Ajay
3b06d72270 Fix invidious support of Firefox 2023-08-25 16:01:11 -04:00
Ajay
4bd0556464 Remove webnavigation optional permission from firefox 2023-08-23 22:44:41 -04:00
Ajay
7e12a914d5 bump translations 2023-08-22 23:29:01 -04:00
Ajay
25eaf4fa20 bump version 2023-08-22 23:26:21 -04:00
Ajay
b3efa1f787 Add compatibility with video speed controller extension 2023-08-22 15:23:04 -04:00
Ajay
9a18e70e34 Fix rate change listener not set up properly
Fixes #1820
2023-08-22 15:22:03 -04:00
Ajay
64ece9cb73 Fix chrome api being used in tests 2023-08-14 11:49:51 -04:00
Ajay
66c974b011 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-08-14 09:28:58 -04:00
Ajay
d8cc93c841 Fix for Firefox not offering promise based APIs in mv2 2023-08-14 09:28:56 -04:00
Ajay Ramachandran
de22accfda Merge pull request #1828 from mchangrh/contributing-translations
add translations to contributing
2023-08-13 15:13:24 -04:00
Michael C
e5b0b60dde add translations to contributing 2023-08-13 15:05:08 -04:00
Ajay Ramachandran
32d98e6544 Add more info about testing on android 2023-08-11 22:38:38 -04:00
Ajay
3dde05eda2 Add more theme icons for browser popup 2023-08-11 21:33:38 -04:00
Ajay
6aeefaae64 Don't reregister contentscripts if not necessary 2023-08-11 12:39:48 -04:00
Ajay
93d695e6c2 Fix error sending messages to closed popups 2023-08-11 12:15:05 -04:00
Ajay
160924feee Update maze utils to improve performance on Invidious, and fix preview bar error
thanks @raphj
2023-08-11 12:08:19 -04:00
Ajay
e3f3ed20e6 Enable non persistent background page on Firefox 2023-08-11 11:39:06 -04:00
Bennett
edaed61612 Reduced execution time of hexToRgb function by ~70%
Coded to use less regex.
On average, "indexOf" performs better than "parseInt."
2022-10-13 14:48:17 -04:00
43 changed files with 669 additions and 284 deletions

View File

@@ -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.

View File

@@ -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"]

View File

@@ -1,6 +1,7 @@
{
"optional_permissions": [
"declarativeContent"
"declarativeContent",
"webNavigation"
],
"background": {
"persistent": false

View File

@@ -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"
}
}

View File

@@ -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
}
]
},

View File

@@ -1,5 +1,11 @@
{
"background": {
"persistent": false
}
},
"permissions": [
"scripting"
],
"optional_permissions": [
"webNavigation"
]
}

View File

@@ -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
View File

@@ -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",

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -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">

View File

@@ -322,4 +322,33 @@ svg {
cursor: default;
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;
}

View File

@@ -257,7 +257,7 @@ input[type='number'] {
opacity: 0;
}
.hidden {
.hidden, .sbhidden {
display: none !important;
}
@@ -717,4 +717,16 @@ svg {
.dearrow-link > img {
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;
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -19,7 +19,7 @@ body {
font-weight: bold;
}
.hidden {
.hidden, .sbhidden {
display: none !important;
}

View File

@@ -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
*/

View File

@@ -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>

View File

@@ -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;
}
@@ -216,4 +216,17 @@
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;
}

View File

@@ -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) {
popupPort[sender.tab.id]?.postMessage(request);
try {
popupPort[sender.tab.id]?.postMessage(request);
} catch (e) {
// This can happen if the popup is closed
}
}
return false;
default:
@@ -148,27 +153,60 @@ 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(() => []);
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));
}
chrome.contentScripts.register({
allFrames: options.allFrames,
js: options.js,
css: options.css,
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) {
if (contentScriptRegistrations[id]) {
contentScriptRegistrations[id].unregister();
delete contentScriptRegistrations[id];
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];
}
}
}

View File

@@ -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) => {

View File

@@ -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")} />
)];

View File

@@ -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
});

View File

@@ -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}

View File

@@ -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 },

View File

@@ -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
View 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();
}

View File

@@ -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();

View File

@@ -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 ? '&nbsp;&nbsp;' : '&nbsp;';
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();
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
chapterTitle.style.removeProperty("display");
chaptersContainer.classList.remove("sponsorblock-chapter-visible");
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 [];
}

View File

@@ -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,8 +158,10 @@ export class SkipButtonControlBar {
}
toggleSkip(): void {
this.skip(this.segment);
this.disableText();
if (this.segment && this.enabled) {
this.skip(this.segment);
this.disableText();
}
}
disableText(): void {
@@ -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 {

View File

@@ -31,7 +31,7 @@ interface IsInfoFoundMessage {
}
interface SkipMessage {
message: "unskip" | "reskip";
message: "unskip" | "reskip" | "selectSegment";
UUID: SegmentUUID;
}

View File

@@ -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) => {

View File

@@ -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) {

View File

@@ -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[];
}

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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, " ");
}

View File

@@ -4,20 +4,17 @@ 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)
} : null;
/* 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;
}
/**
@@ -31,4 +28,4 @@ function indexesOf<T>(array: T[], value: T): number[] {
export const GenericUtils = {
getLuminance,
indexesOf
}
}

47
src/utils/requests.ts Normal file
View 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);
});
}

View File

@@ -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] = {

View File

@@ -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
});