Compare commits

...

93 Commits

Author SHA1 Message Date
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
Ajay
52149f4c0f bump version 2023-08-09 18:30:21 -04:00
Ajay
cbc586f9ac Potential fix for ctrl+click having a blank html video
Other potential fix for #1820
2023-08-09 18:30:05 -04:00
Ajay
fc8e20be0d Impove incorrect video check, and add better logging to it
Potential fix for #1820
2023-08-09 18:24:21 -04:00
Ajay
368059eb0d bump version 2023-08-01 22:45:53 -04:00
Ajay
ea77375fcc Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-08-01 22:45:37 -04:00
Ajay
16005e417d Remove maze utils symlink 2023-08-01 22:45:01 -04:00
Ajay Ramachandran
7c5b750264 use npm ci for consistency 2023-08-01 19:57:07 -04:00
Ajay Ramachandran
cc6b65c6c4 suggest using Linux 2023-08-01 15:52:50 -04:00
Ajay Ramachandran
d2c99c2d77 Merge pull request #1815 from mchangrh/contributing-update
[docs] move building from readme, formatting tweaks
2023-08-01 15:51:39 -04:00
Michael C
84c25e3042 [docs] move building to readme, formatting tweaks
- reformatted windows build issues
- updated contributing to reflect LGPL-3.0-or-later
- replaced Invidio.us with Invidious
- replaced API docs link
- updated Invidious API link
2023-08-01 15:20:24 -04:00
Ajay
8840dba90f bump version 2023-07-29 22:39:37 -04:00
Ajay
856dded58f update translations 2023-07-29 22:39:26 -04:00
Ajay
1d1bd2a003 Only add host permission on chrome 2023-07-29 01:49:27 -04:00
Ajay
dc91ee76ca Add support for live updating in chrome 2023-07-29 01:41:57 -04:00
Ajay
90bb9a4d02 Remove log 2023-07-28 20:44:19 -04:00
Ajay
d12d847f2f Fix it sometimes looping instead of going to next video when autoskipping at the end for playlists
Fix #1804
2023-07-28 20:42:06 -04:00
Ajay
31a9de252d Fix skip loop issue
Fixes #1811
2023-07-28 20:34:09 -04:00
Ajay
299cb485c3 Fix chapter name sometimes disappearing 2023-07-28 19:37:54 -04:00
Ajay
882d462849 Add category color to skip notice 2023-07-28 18:42:27 -04:00
Ajay
fc3710b37b Clear preview bar on update 2023-07-28 18:39:51 -04:00
Ajay
3ac170ad01 Fix skip to highlight button on live update 2023-07-28 18:34:05 -04:00
Ajay
4069545603 Support live updates on firefox 2023-07-28 16:30:28 -04:00
Ajay
db9fc11f13 bump version 2023-07-19 20:31:38 -04:00
Ajay
76667f68ec Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2023-07-19 20:30:18 -04:00
Ajay
e27a287a68 Better zoom to fill compatibility check 2023-07-19 20:30:16 -04:00
Ajay Ramachandran
ce3731774d Merge pull request #1803 from ajayyy/dependabot/npm_and_yarn/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4
2023-07-18 17:12:08 -04:00
dependabot[bot]
8cbf2bb50b Bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 20:27:55 +00:00
Ajay
6f8c44b2eb Fix category colors sometimes not working 2023-07-18 13:26:41 -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
55 changed files with 624 additions and 316 deletions

View File

@@ -1,15 +1,31 @@
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.
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.
# All Platforms
Make sure to pull and update all submodules
`git submodule update --init --recursive`
# Translations
https://crowdin.com/project/sponsorblock
"? property does not exist on type ConfigClass"
> Make sure to copy `config.json.example` to `config.json` and remove comments
# Building
## Building locally
0. You must have [Node.js 16 or later](https://nodejs.org/) and npm installed. Works best on Linux
1. Clone with submodules
```bash
git clone --recursive https://github.com/ajayyy/SponsorBlock
```
Or if you already cloned it, pull submodules with
```bash
git submodule update --init --recursive
```
2. Copy the file `config.json.example` to `config.json` and adjust configuration as desired.
- Comments are invalid in JSON, make sure they are all removed.
- You will need to repeat this step in the future if you get build errors related to `CompileConfig` or `property does not exist on type ConfigClass`. This can happen for example when a new category is added.
3. Run `npm ci` in the repository to install dependencies.
4. Run `npm run build:dev` (for Chrome) or `npm run build:dev:firefox` (for Firefox) to generate a development version of the extension with source maps.
- You can also run `npm run build` (for Chrome) or `npm run build:firefox` (for Firefox) to generate a production build.
5. The built extension is now in `dist/`. You can load this folder directly in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#manifest), or convert it to a zip file to load it as a [temporary extension](https://developer.mozilla.org/docs/Tools/about:debugging#loading_a_temporary_extension) in Firefox.
## Developing with a clean profile and hot reloading
Run `npm run dev` (for Chrome) or `npm run dev:firefox` (for Firefox) to run the extension using a clean browser profile with hot reloading. This uses [`web-ext run`](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#commands).
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. You may need to edit package.json and add the parameters directly there.
# Windows
"Cannot find module "../maze-utils"
- Enable "Developer Mode" in windows for symlinks
- `src/maze-utils` will not appear properly and builds will fail since it is is only rendered as a file
- Enable symlink support in git `git config --global core.symlinks true`
- run `git checkout -- src/maze-utils` in order to create a symlink instead of a text file

View File

@@ -38,7 +38,7 @@
SponsorBlock is an open-source crowdsourced browser extension to skip sponsor segments in YouTube videos. Users submit when a sponsor happens from the extension, and the extension automatically skips sponsors it knows about. It also supports skipping other categories, such as intros, outros and reminders to subscribe.
It also supports Invidio.us.
It also supports Invidious.
**Translate:** [![Crowdin](https://badges.crowdin.net/sponsorblock/localized.svg)](https://crowdin.com/project/sponsorblock)
@@ -56,47 +56,14 @@ The dataset and API are now being used in some [ports](https://github.com/ajayyy
# API
You can read the API docs [here](https://wiki.sponsor.ajay.app/index.php/API_Docs).
You can read the API docs [here](https://wiki.sponsor.ajay.app/w/API_Docs).
# Building
You must have [Node.js 16](https://nodejs.org/) and npm installed.
1. Clone with submodules
```bash
git clone --recursive https://github.com/ajayyy/SponsorBlock
```
Or if you already cloned it, pull submodules with
```bash
git submodule update --init --recursive
```
2. Copy the file `config.json.example` to `config.json` and adjust configuration as desired.
- You will need to repeat this step in the future if you get build errors related to `CompileConfig`. This can happen for example when a new category is added.
3. Run `npm install` in the repository to install dependencies.
4. Run `npm run build:dev` (for Chrome) or `npm run build:dev:firefox` (for Firefox) to generate a development version of the extension with source maps.
- You can also run `npm run build` (for Chrome) or `npm run build:firefox` (for Firefox) to generate a production build.
5. The built extension is now in `dist/`. You can load this folder directly in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#manifest), or convert it to a zip file to load it as a [temporary extension](https://developer.mozilla.org/en-US/docs/Tools/about:debugging#loading_a_temporary_extension) in Firefox.
### Developing with a clean profile and hot reloading
Run `npm run dev` (for Chrome) or `npm run dev:firefox` (for Firefox) to run the extension using a clean browser profile with hot reloading. This uses [`web-ext run`](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#commands).
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.
See [CONTRIBUTING.md](CONTRIBUTING.md)
# Credit
The awesome [Invidious API](https://docs.invidious.io/API.md) was previously used, and the server is now using [NewLeaf](https://git.sr.ht/~cadence/NewLeaf) as a to get video info from YouTube.
The awesome [Invidious API](https://docs.invidious.io/) was previously used, and the server is now using [NewLeaf](https://git.sr.ht/~cadence/NewLeaf) as a to get video info from YouTube.
Originally forked from [YTSponsorSkip](https://github.com/NDevTK/YTSponsorSkip), but very little code remains.

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.bp.projectsegfau.lt","inv.makerlab.tech","inv.pistasjis.net","inv.tux.pizza","inv.zzls.xyz","invidious.asir.dev","invidious.flokinet.to","invidious.io.lol","invidious.lunar.icu","invidious.no-logs.com","invidious.privacydev.net","invidious.private.coffee","invidious.protokolla.fi","invidious.slipfox.xyz","invidious.tiekoetter.com","iv.ggtyler.dev","iv.melmac.space","iv.nboeck.de","onion.tube","vid.priv.au","vid.puffyan.us","yewtu.be","yt.artemislena.eu","yt.drgnz.club","yt.oelrichsgarcia.de"]

View File

@@ -1,8 +1,12 @@
{
"optional_permissions": [
"declarativeContent"
"declarativeContent",
"webNavigation"
],
"background": {
"persistent": false
}
},
"permissions": [
"https://*.youtube.com/*"
]
}

View File

@@ -3,5 +3,14 @@
"gecko": {
"id": "sponsorBlocker@ajay.app"
}
},
"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.11",
"version": "5.4.23",
"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",

43
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"
}
@@ -13332,8 +13335,9 @@
"dev": true
},
"node_modules/word-wrap": {
"version": "1.2.3",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -16738,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"
}
@@ -23095,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": {
@@ -23119,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",
@@ -23348,8 +23352,9 @@
"dev": true
},
"word-wrap": {
"version": "1.2.3",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
"dev": true
},
"wrap-ansi": {

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;
}
@@ -867,3 +868,7 @@ input::-webkit-inner-spin-button {
.sponsorThumbnailLabel:hover span {
display: inline;
}
.sponsorblock-chapter-visible {
display: inherit !important;
}

View File

@@ -257,7 +257,7 @@ input[type='number'] {
opacity: 0;
}
.hidden {
.hidden, .sbhidden {
display: none !important;
}

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"
@@ -414,6 +414,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;
}

View File

@@ -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: 375px;
max-width: 50%;
}
@@ -36,7 +36,7 @@
max-width: calc(100% - 50px);
}
.sponsorSkipNotice .hidden {
.sponsorSkipNotice .sbhidden {
display: none;
}

View File

@@ -3,9 +3,9 @@ import * as CompileConfig from "../config.json";
import Config from "./config";
import { Registration } from "./types";
import "content-scripts-register-polyfill";
import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "./maze-utils/background-request-proxy";
import { setupTabUpdates } from "./maze-utils/tab-updates";
import { generateUserID } from "./maze-utils/setup";
import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "../maze-utils/src/background-request-proxy";
import { setupTabUpdates } from "../maze-utils/src/tab-updates";
import { generateUserID } from "../maze-utils/src/setup";
// Make the config public for debugging purposes
@@ -13,6 +13,10 @@ window.SB = Config;
import Utils from "./utils";
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
@@ -24,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();
});
@@ -72,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:
@@ -132,6 +140,11 @@ chrome.runtime.onInstalled.addListener(function () {
}
}
}, 1500);
// Only do this once the old version understands how to clean itself up
if (!isFirefoxOrSafari() && chrome.runtime.getManifest().version !== "5.4.13") {
injectUpdatedScripts().catch(logWarn);
}
});
/**
@@ -140,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

@@ -8,7 +8,7 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { VoteResponse } from "../messageTypes";
import { AnimationUtils } from "../utils/animationUtils";
import { Tooltip } from "../render/Tooltip";
import { getErrorMessage } from "../maze-utils/formating";
import { getErrorMessage } from "../../maze-utils/src/formating";
export interface CategoryPillProps {
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;

View File

@@ -8,7 +8,7 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { VoteResponse } from "../messageTypes";
import { AnimationUtils } from "../utils/animationUtils";
import { Tooltip } from "../render/Tooltip";
import { getErrorMessage } from "../maze-utils/formating";
import { getErrorMessage } from "../../maze-utils/src/formating";
export interface ChapterVoteProps {
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
@@ -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

@@ -1,5 +1,6 @@
import * as React from "react";
import Config from "../config";
import SbSvg from "../svg-icons/sb_svg";
enum CountdownMode {
Timer,
@@ -28,6 +29,7 @@ export interface NoticeProps {
extraClass?: string;
hideLogo?: boolean;
hideRightInfo?: boolean;
logoFill?: string;
// Callback for when this is closed
closeListener: () => void;
@@ -122,10 +124,10 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
<td className="noticeLeftIcon">
{/* Logo */}
{!this.props.hideLogo &&
<img id={"sponsorSkipLogo" + this.idSuffix}
className="sponsorSkipLogo sponsorSkipObject"
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
</img>
<SbSvg
id={"sponsorSkipLogo" + this.idSuffix}
fill={this.props.logoFill}
className="sponsorSkipLogo sponsorSkipObject"/>
}
<span id={"sponsorSkipMessage" + this.idSuffix}
@@ -194,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

@@ -12,8 +12,8 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
import PencilSvg from "../svg-icons/pencil_svg";
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { generateUserID } from "../maze-utils/setup";
import { keybindToString } from "../maze-utils/config";
import { generateUserID } from "../../maze-utils/src/setup";
import { keybindToString } from "../../maze-utils/src/config";
enum SkipButtonState {
Undo, // Unskip
@@ -177,7 +177,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
) : null;
return (
<NoticeComponent noticeTitle={this.state.noticeTitle}
<NoticeComponent
noticeTitle={this.state.noticeTitle}
amountOfPreviousNotices={this.amountOfPreviousNotices}
showInSecondSlot={this.showInSecondSlot}
idSuffix={this.idSuffix}
@@ -191,6 +192,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
ref={this.noticeRef}
closeListener={() => this.closeListener()}
smaller={this.state.smaller}
logoFill={Config.config.barTypes[this.segments[0].category].color}
limitWidth={true}
firstColumn={firstColumn}
bottomRow={[...this.getMessageBoxes(), ...this.getBottomRow() ]}

View File

@@ -7,7 +7,7 @@ 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/formating";
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
const utils = new Utils();
@@ -128,6 +128,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 +146,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 +163,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 +250,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 +296,10 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
</span>
): ""}
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType))
&& sponsorTime.actionType === ActionType.Chapter ? (
{(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
<span id={"sponsorTimePreviewButton" + 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>
): ""}
@@ -624,7 +637,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 +646,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 {

View File

@@ -2,7 +2,7 @@ import * as React from "react";
import { createRoot, Root } from 'react-dom/client';
import Config from "../../config";
import KeybindDialogComponent from "./KeybindDialogComponent";
import { formatKey, Keybind, keybindEquals, keybindToString } from "../../maze-utils/config";
import { formatKey, Keybind, keybindEquals, keybindToString } from "../../../maze-utils/src/config";
export interface KeybindProps {
option: string;

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import { ChangeEvent } from "react";
import Config from "../../config";
import { Keybind, formatKey, keybindEquals } from "../../maze-utils/config";
import { Keybind, formatKey, keybindEquals } from "../../../maze-utils/src/config";
export interface KeybindDialogProps {
option: string;

View File

@@ -1,8 +1,8 @@
import * as CompileConfig from "../config.json";
import * as invidiousList from "../ci/invidiouslist.json";
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, VideoID, SponsorHideType } from "./types";
import { Keybind, ProtoConfig, keybindEquals } from "./maze-utils/config";
import { HashedValue } from "./maze-utils/hash";
import { Keybind, ProtoConfig, keybindEquals } from "../maze-utils/src/config";
import { HashedValue } from "../maze-utils/src/hash";
export interface Permission {
canSubmit: boolean;
@@ -75,7 +75,7 @@ interface SBConfig {
allowScrollingToEdit: boolean;
deArrowInstalled: boolean;
showDeArrowPromotion: boolean;
showZoomToFillError: boolean;
showZoomToFillError2: boolean;
// Used to cache calculated text color info
categoryPillColors: {
@@ -86,6 +86,7 @@ interface SBConfig {
};
skipKeybind: Keybind;
skipToHighlightKeybind: Keybind;
startSponsorKeybind: Keybind;
submitKeybind: Keybind;
nextChapterKeybind: Keybind;
@@ -148,6 +149,10 @@ class ConfigClass extends ProtoConfig<SBConfig, SBStorage> {
}
function migrateOldSyncFormats(config: SBConfig) {
if (config["showZoomToFillError"]) {
chrome.storage.sync.remove("showZoomToFillError");
}
if (!config["chapterCategoryAdded"]) {
config["chapterCategoryAdded"] = true;
@@ -312,7 +317,7 @@ const syncDefaults = {
allowScrollingToEdit: true,
deArrowInstalled: false,
showDeArrowPromotion: true,
showZoomToFillError: true,
showZoomToFillError2: true,
categoryPillColors: {},
@@ -324,6 +329,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

@@ -24,7 +24,7 @@ import SubmissionNotice from "./render/SubmissionNotice";
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
import { getStartTimeFromUrl } from "./utils/urlParser";
import { getControls, getExistingChapters, getHashParams, isVisible } from "./utils/pageUtils";
import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isVisible } from "./utils/pageUtils";
import { CategoryPill } from "./render/CategoryPill";
import { AnimationUtils } from "./utils/animationUtils";
import { GenericUtils } from "./utils/genericUtils";
@@ -32,19 +32,23 @@ import { logDebug } from "./utils/logger";
import { importTimes } from "./utils/exporter";
import { ChapterVote } from "./render/ChapterVote";
import { openWarningDialog } from "./utils/warnings";
import { isFirefoxOrSafari, waitFor } from "./maze-utils";
import { getErrorMessage, getFormattedTime } from "./maze-utils/formating";
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube } from "./maze-utils/video";
import { Keybind, StorageChangesObject, isSafari, keybindEquals } from "./maze-utils/config";
import { findValidElement, waitForElement } from "./maze-utils/dom"
import { getHash, HashedValue } from "./maze-utils/hash";
import { generateUserID } from "./maze-utils/setup";
import { updateAll } from "./maze-utils/thumbnailManagement";
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 { 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";
cleanPage();
const utils = new Utils();
@@ -59,7 +63,7 @@ utils.wait(() => Config.isReady(), 5000, 10).then(() => {
&& Config.config.showDeArrowPromotion
&& Config.config.showUpsells
&& Config.config.showNewFeaturePopups
&& (Config.config.skipCount > 100 || !Config.config.trackViewCount)
&& (Config.config.skipCount > 30 || !Config.config.trackViewCount)
&& Math.random() < 0.05) {
if (!await isDeArrowInstalled()) {
@@ -106,6 +110,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;
@@ -296,6 +301,10 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
case "reskip":
reskipSponsorTime(sponsorTimes.find((segment) => segment.UUID === request.UUID), true);
break;
case "selectSegment":
selectedSegment = request.UUID;
updatePreviewBar();
break;
case "submitVote":
vote(request.type, request.UUID).then((response) => sendResponse(response));
return true;
@@ -473,6 +482,8 @@ function videoIDChange(): void {
}
function handleMobileControlsMutations(): void {
if (!chrome.runtime?.id) return;
updateVisibilityOfPlayerControlsButton();
skipButtonControlBar?.updateMobileControls();
@@ -703,7 +714,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
// Don't pretend to be earlier than we are, could result in loops
if (forcedSkipTime !== null && forceVideoTime > forcedSkipTime) {
forcedSkipTime = null;
forcedSkipTime = forceVideoTime;
}
startSponsorSchedule(forcedIncludeIntersectingSegments, forcedSkipTime, forcedIncludeNonIntersectingSegments);
@@ -797,11 +808,12 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
const currentVideoID = getYouTubeVideoID();
const recordedVideoID = videoID || getVideoID();
if (currentVideoID !== recordedVideoID || (sponsorTime
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment === sponsorTime.segment))
&& !sponsorTimesSubmitting.some((time) => time.segment === sponsorTime.segment))) {
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))
&& !sponsorTimesSubmitting.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))) {
// Something has really gone wrong
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
console.error("[SponsorBlock] SponsorTime", sponsorTime, "sponsorTimes", sponsorTimes, "sponsorTimesSubmitting", sponsorTimesSubmitting);
// Video ID change occured
checkVideoIDChange();
@@ -812,18 +824,38 @@ 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
getVideo().addEventListener('loadstart', videoOnReadyListener)
getVideo().addEventListener('durationchange', durationChangeListener);
if (setupVideoListenersFirstTime) {
addCleanupListener(() => {
getVideo().removeEventListener('loadstart', videoOnReadyListener);
getVideo().removeEventListener('durationchange', durationChangeListener);
});
}
if (!Config.config.disableSkipping) {
switchingVideos = false;
let startedWaiting = false;
let lastPausedAtZero = true;
getVideo().addEventListener('play', () => {
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
// gone back to the begining
@@ -853,9 +885,10 @@ function setupVideoListeners() {
startSponsorSchedule();
}
};
getVideo().addEventListener('play', playListener);
});
getVideo().addEventListener('playing', () => {
const playingListener = () => {
updateVirtualTime();
lastPausedAtZero = false;
@@ -881,8 +914,31 @@ function setupVideoListeners() {
startSponsorSchedule();
}
});
getVideo().addEventListener('seeking', () => {
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);
const seekingListener = () => {
lastKnownVideoTime.fromPause = false;
if (!getVideo().paused){
@@ -906,45 +962,54 @@ function setupVideoListeners() {
lastPausedAtZero = true;
}
}
});
getVideo().addEventListener('ratechange', () => {
updateVirtualTime();
clearWaitingTime();
startSponsorSchedule();
});
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
getVideo().addEventListener('videoSpeed_ratechange', () => {
updateVirtualTime();
clearWaitingTime();
startSponsorSchedule();
});
};
getVideo().addEventListener('seeking', seekingListener);
const stoppedPlayback = () => {
// Reset lastCheckVideoTime
lastCheckVideoTime = -1;
lastCheckTime = 0;
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
lastKnownVideoTime.videoTime = null;
lastKnownVideoTime.preciseTime = null;
updateWaitingTime();
cancelSponsorSchedule();
};
getVideo().addEventListener('pause', () => {
const pauseListener = () => {
lastKnownVideoTime.fromPause = true;
stoppedPlayback();
});
getVideo().addEventListener('waiting', () => {
};
getVideo().addEventListener('pause', pauseListener);
const waitingListener = () => {
logDebug("[SB] Not skipping due to buffering");
startedWaiting = true;
stoppedPlayback();
});
};
getVideo().addEventListener('waiting', waitingListener);
startSponsorSchedule();
if (setupVideoListenersFirstTime) {
addCleanupListener(() => {
getVideo().removeEventListener('play', playListener);
getVideo().removeEventListener('playing', playingListener);
getVideo().removeEventListener('seeking', seekingListener);
getVideo().removeEventListener('ratechange', rateChangeListener);
getVideo().removeEventListener('videoSpeed_ratechange', rateChangeListener);
getVideo().removeEventListener('pause', pauseListener);
getVideo().removeEventListener('waiting', waitingListener);
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
});
}
}
setupVideoListenersFirstTime = false;
}
function updateVirtualTime() {
@@ -1309,7 +1374,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
});
});
}
@@ -1356,18 +1422,20 @@ async function channelIDChange(channelIDInfo: ChannelIDInfo) {
}
function videoElementChange(newVideo: boolean): void {
if (newVideo) {
setupVideoListeners();
setupSkipButtonControlBar();
setupCategoryPill();
}
checkPreviewbarState();
// Incase the page is still transitioning, check again in a few seconds
setTimeout(checkPreviewbarState, 100);
setTimeout(checkPreviewbarState, 1000);
setTimeout(checkPreviewbarState, 5000);
waitFor(() => Config.isReady()).then(() => {
if (newVideo) {
setupVideoListeners();
setupSkipButtonControlBar();
setupCategoryPill();
}
checkPreviewbarState();
// Incase the page is still transitioning, check again in a few seconds
setTimeout(checkPreviewbarState, 100);
setTimeout(checkPreviewbarState, 1000);
setTimeout(checkPreviewbarState, 5000);
})
}
function checkPreviewbarState(): void {
@@ -1587,8 +1655,10 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
// for some reason you also can't skip to 1 second before the end
if (v.loop && v.duration > 1 && skipTime[1] >= v.duration - 1) {
v.currentTime = 0;
} else if (navigator.vendor === "Apple Computer, Inc." && v.duration > 1 && skipTime[1] >= v.duration) {
} else if (v.duration > 1 && skipTime[1] >= v.duration
&& (navigator.vendor === "Apple Computer, Inc." || isPlayingPlaylist())) {
// MacOS will loop otherwise #1027
// Sometimes playlists loop too #1804
v.currentTime = v.duration - 0.001;
} else {
if (inMuteSegment(skipTime[1], true)) {
@@ -1619,9 +1689,10 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
beep.play();
beep.addEventListener("ended", () => {
navigator.mediaSession.metadata = null;
setTimeout(() =>
navigator.mediaSession.metadata = oldMetadata
);
setTimeout(() => {
navigator.mediaSession.metadata = oldMetadata;
beep.remove();
});
})
}
@@ -2056,7 +2127,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))
@@ -2246,7 +2317,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));
@@ -2331,11 +2402,21 @@ function previousChapter(): void {
function addHotkeyListener(): void {
document.addEventListener("keydown", hotkeyListener);
document.addEventListener("DOMContentLoaded", () => {
const onLoad = () => {
// Allow us to stop propagation to YouTube by being deeper
document.removeEventListener("keydown", hotkeyListener);
document.body.addEventListener("keydown", hotkeyListener);
});
addCleanupListener(() => {
document.body.removeEventListener("keydown", hotkeyListener);
});
};
if (document.readyState === "complete") {
onLoad();
} else {
document.addEventListener("DOMContentLoaded", onLoad);
}
}
function hotkeyListener(e: KeyboardEvent): void {
@@ -2351,14 +2432,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();
@@ -2392,7 +2482,7 @@ function hotkeyListener(e: KeyboardEvent): void {
*/
function addCSS() {
if (!isFirefoxOrSafari() && Config.config.invidiousInstances.includes(new URL(document.URL).hostname)) {
window.addEventListener("DOMContentLoaded", () => {
const onLoad = () => {
const head = document.getElementsByTagName("head")[0];
for (const file of utils.css) {
@@ -2404,7 +2494,13 @@ function addCSS() {
head.appendChild(fileref);
}
});
};
if (document.readyState === "complete") {
onLoad();
} else {
document.addEventListener("DOMContentLoaded", onLoad);
}
}
}
@@ -2489,7 +2585,9 @@ function setCategoryColorCSSVariables() {
if (!styleContainer) {
styleContainer = document.createElement("style");
styleContainer.id = "sbCategoryColorStyle";
document.head.appendChild(styleContainer)
const head = (document.head || document.documentElement);
head.appendChild(styleContainer)
}
let css = ":root {"

View File

@@ -1,3 +1,3 @@
import { init } from "./maze-utils/injected/document";
import { init } from "../maze-utils/src/injected/document";
init();

View File

@@ -1,10 +1,14 @@
import { localizeHtmlPage } from "./maze-utils/setup";
import { localizeHtmlPage } from "../maze-utils/src/setup";
import Config from "./config";
import { showDonationLink } from "./utils/configUtils";
import { waitFor } from "./maze-utils";
import { waitFor } from "../maze-utils/src";
window.addEventListener('DOMContentLoaded', init);
if (document.readyState === "complete") {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
async function init() {
localizeHtmlPage();

View File

@@ -11,8 +11,9 @@ import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceT
import { partition } from "../utils/arrayUtils";
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
import { normalizeChapterName } from "../utils/exporter";
import { getFormattedTimeToSeconds } from "../maze-utils/formating";
import { findValidElement } from "../maze-utils/dom";
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { findValidElement } from "../../maze-utils/src/dom";
import { addCleanupListener } from "../../maze-utils/src/cleanup";
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
const MIN_CHAPTER_SIZE = 0.003;
@@ -26,6 +27,7 @@ export interface PreviewBarSegment {
description: string;
source: SponsorSourceType;
requiredSegment?: boolean;
selectedSegment?: boolean;
}
interface ChapterGroup extends SegmentContainer {
@@ -201,6 +203,10 @@ class PreviewBar {
childList: true,
subtree: true,
});
addCleanupListener(() => {
observer.disconnect();
});
}
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
@@ -327,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;
@@ -626,6 +633,11 @@ class PreviewBar {
childListObserver.observe(this.originalChapterBar, {
childList: true
});
addCleanupListener(() => {
attributeObserver.disconnect();
childListObserver.disconnect();
});
}
private updateChapterAllMutation(originalChapterBar: HTMLElement, progressBar: HTMLElement, firstUpdate = false): void {
@@ -774,9 +786,12 @@ class PreviewBar {
if (!Config.config.showSegmentNameInChapterBar
|| ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) {
const chaptersContainer = this.getChaptersContainer();
const chapterButton = this.getChapterButton(chaptersContainer);
if (chapterButton && chapterButton.classList.contains("ytp-chapter-container-disabled")) {
chaptersContainer.style.display = "none";
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 [];
@@ -802,7 +817,7 @@ class PreviewBar {
if (chaptersContainer) {
if (segments.length > 0) {
chaptersContainer.style.removeProperty("display");
chaptersContainer.classList.add("sponsorblock-chapter-visible");
const chosenSegment = segments.sort((a, b) => {
if (a.actionType === ActionType.Chapter && b.actionType !== ActionType.Chapter) {
@@ -819,11 +834,11 @@ class PreviewBar {
chapterButton.disabled = false;
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
chapterTitle.innerText = "";
chapterTitle.style.display = "none";
const chapterCustomText = (chapterTitle.querySelector(".sponsorChapterText") || (() => {
const chapterCustomText = (chapterTitle.parentElement.querySelector(".sponsorChapterText") || (() => {
const elem = document.createElement("div");
chapterTitle.appendChild(elem);
chapterTitle.parentElement.insertBefore(elem, chapterTitle);
elem.classList.add("sponsorChapterText");
return elem;
})()) as HTMLDivElement;
@@ -854,11 +869,10 @@ class PreviewBar {
} else {
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
if (chapterTitle.innerText === "") {
chaptersContainer.style.display = "none";
} else {
chaptersContainer.style.removeProperty("display");
}
chapterTitle.style.removeProperty("display");
chaptersContainer.classList.remove("sponsorblock-chapter-visible");
this.chapterVote.setVisibility(false);
}
}

View File

@@ -2,7 +2,7 @@ import Config from "../config";
import { SponsorTime } from "../types";
import { getSkippingText } from "../utils/categoryUtils";
import { AnimationUtils } from "../utils/animationUtils";
import { keybindToString } from "../maze-utils/config";
import { keybindToString } from "../../maze-utils/src/config";
export interface SkipButtonControlBarProps {
skip: (segment: SponsorTime) => void;
@@ -41,7 +41,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");
@@ -103,7 +103,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 +111,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 +134,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 +147,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 +160,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 +184,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

@@ -1 +0,0 @@
../maze-utils/src/

View File

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

View File

@@ -13,10 +13,10 @@ import CategoryChooser from "./render/CategoryChooser";
import UnsubmittedVideos from "./render/UnsubmittedVideos";
import KeybindComponent from "./components/options/KeybindComponent";
import { showDonationLink } from "./utils/configUtils";
import { localizeHtmlPage } from "./maze-utils/setup";
import { StorageChangesObject } from "./maze-utils/config";
import { getHash } from "./maze-utils/hash";
import { isFirefoxOrSafari } from "./maze-utils";
import { localizeHtmlPage } from "../maze-utils/src/setup";
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";
const utils = new Utils();
let embed = false;
@@ -24,7 +24,11 @@ let embed = false;
const categoryChoosers: CategoryChooser[] = [];
const unsubmittedVideos: UnsubmittedVideos[] = [];
window.addEventListener('DOMContentLoaded', init);
if (document.readyState === "complete") {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
async function init() {
localizeHtmlPage();
@@ -81,6 +85,9 @@ async function init() {
});
}
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("*");

View File

@@ -1,13 +1,17 @@
import Config from "./config";
import Utils from "./utils";
import { localizeHtmlPage } from "./maze-utils/setup";
import { localizeHtmlPage } from "../maze-utils/src/setup";
const utils = new Utils();
// This is needed, if Config is not imported before Utils, things break.
// Probably due to cyclic dependencies
Config.config;
window.addEventListener('DOMContentLoaded', init);
if (document.readyState === "complete") {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
async function init() {
localizeHtmlPage();

View File

@@ -21,12 +21,12 @@ import {
import { showDonationLink } from "./utils/configUtils";
import { AnimationUtils } from "./utils/animationUtils";
import { shortCategoryName } from "./utils/categoryUtils";
import { localizeHtmlPage } from "./maze-utils/setup";
import { localizeHtmlPage } from "../maze-utils/src/setup";
import { exportTimes } from "./utils/exporter";
import GenericNotice from "./render/GenericNotice";
import { getErrorMessage, getFormattedTime } from "./maze-utils/formating";
import { StorageChangesObject } from "./maze-utils/config";
import { getHash } from "./maze-utils/hash";
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
import { StorageChangesObject } from "../maze-utils/src/config";
import { getHash } from "../maze-utils/src/hash";
const utils = new Utils();
@@ -687,6 +687,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 +720,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
container.appendChild(votingButtons);
}
container.addEventListener("mouseleave", () => selectSegment(null));
}
function submitTimes() {
@@ -968,6 +972,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)
*/

View File

@@ -5,8 +5,9 @@ import Config from "../config";
import { VoteResponse } from "../messageTypes";
import { Category, SegmentUUID, SponsorTime } from "../types";
import { Tooltip } from "./Tooltip";
import { waitFor } from "../maze-utils";
import { getYouTubeTitleNode } from "../maze-utils/elements";
import { waitFor } from "../../maze-utils/src";
import { getYouTubeTitleNode } from "../../maze-utils/src/elements";
import { addCleanupListener } from "../../maze-utils/src/cleanup";
const id = "categoryPill";
@@ -24,6 +25,12 @@ export class CategoryPill {
constructor() {
this.ref = React.createRef();
addCleanupListener(() => {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
});
}
async attachToPage(onMobileYouTube: boolean, onInvidious: boolean,

View File

@@ -47,6 +47,7 @@ export default class GenericNotice {
const referenceNode = options.referenceNode ?? utils.findReferenceNode();
this.noticeElement = document.createElement("div");
this.noticeElement.className = "sponsorSkipNoticeContainer";
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
referenceNode.prepend(this.noticeElement);

View File

@@ -38,6 +38,7 @@ class SkipNotice {
idSuffix += amountOfPreviousNotices;
this.noticeElement = document.createElement("div");
this.noticeElement.className = "sponsorSkipNoticeContainer";
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
referenceNode.prepend(this.noticeElement);

55
src/svg-icons/sb_svg.tsx Normal file
View File

@@ -0,0 +1,55 @@
import * as React from "react";
export interface SbIconProps {
id?: string;
fill?: string;
className?: string;
width?: string;
height?: string;
onClick?: () => void;
}
export default function SbSvg({
id = "",
fill = "#ff0000",
className = "",
onClick
}: SbIconProps): JSX.Element {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 565.15 568"
id={id}
className={className}
onClick={() => onClick?.() } >
<g
id="Layer_2"
data-name="Layer 2">
<g
id="Layer_1-2"
data-name="Layer 1"
style={{
fill
}}>
<path
d="M282.58,568a65,65,0,0,1-34.14-9.66C95.41,463.94,2.54,300.46,0,121A64.91,64.91,0,0,1,34,62.91a522.56,522.56,0,0,1,497.16,0,64.91,64.91,0,0,1,34,58.12c-2.53,179.43-95.4,342.91-248.42,437.3A65,65,0,0,1,282.58,568Zm0-548.31A502.24,502.24,0,0,0,43.4,80.22a45.27,45.27,0,0,0-23.7,40.53c2.44,172.67,91.81,330,239.07,420.83a46.19,46.19,0,0,0,47.61,0C453.64,450.73,543,293.42,545.45,120.75a45.26,45.26,0,0,0-23.7-40.54A502.26,502.26,0,0,0,282.58,19.69Z"
id="path8"
style={{
fill
}} />
<path
style={{
fill
}}
d="M 284.70508 42.693359 A 479.9 479.9 0 0 0 54.369141 100.41992 A 22.53 22.53 0 0 0 42.669922 120.41992 C 45.069922 290.25992 135.67008 438.63977 270.83008 522.00977 A 22.48 22.48 0 0 0 294.32031 522.00977 C 429.48031 438.63977 520.08047 290.25992 522.48047 120.41992 A 22.53 22.53 0 0 0 510.7793 100.41992 A 479.9 479.9 0 0 0 284.70508 42.693359 z M 220.41016 145.74023 L 411.2793 255.93945 L 220.41016 366.14062 L 220.41016 145.74023 z "
id="path10" />
</g>
</g>
<polygon style={{
fill: "#fff"
}}
points="411.28 255.94 220.41 145.74 220.41 366.14 411.28 255.94"
/>
</svg>
);
}

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

@@ -1,12 +1,12 @@
import Config, { VideoDownvotes } from "./config";
import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types";
import { getHash, HashedValue } from "./maze-utils/hash";
import { getHash, HashedValue } from "../maze-utils/src/hash";
import * as CompileConfig from "../config.json";
import { isFirefoxOrSafari, waitFor } from "./maze-utils";
import { findValidElementFromSelector } from "./maze-utils/dom";
import { FetchResponse, sendRequestToCustomServer } from "./maze-utils/background-request-proxy"
import { isSafari } from "./maze-utils/config";
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 +47,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 +77,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()
};

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

@@ -1,7 +1,7 @@
import Config from "../config";
export function runCompatibilityChecks() {
if (Config.config.showZoomToFillError) {
if (Config.config.showZoomToFillError2 && document.URL.includes("watch?v=")) {
setTimeout(() => {
const zoomToFill = document.querySelector(".zoomtofillBtn");
@@ -9,7 +9,7 @@ export function runCompatibilityChecks() {
alert(chrome.i18n.getMessage("zoomToFillUnsupported"));
}
Config.config.showZoomToFillError = false;
Config.config.showZoomToFillError2 = false;
}, 10000);
}
}

View File

@@ -1,8 +1,8 @@
import * as CompileConfig from "../../config.json";
import Config from "../config";
import { isSafari } from "../maze-utils/config";
import { isFirefoxOrSafari } from "../maze-utils";
import { isSafari } from "../../maze-utils/src/config";
import { isFirefoxOrSafari } from "../../maze-utils/src";
export function isDeArrowInstalled(): Promise<boolean> {
if (Config.config.deArrowInstalled) {

View File

@@ -1,8 +1,8 @@
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../types";
import { shortCategoryName } from "./categoryUtils";
import * as CompileConfig from "../../config.json";
import { getFormattedTime, getFormattedTimeToSeconds } from "../maze-utils/formating";
import { generateUserID } from "../maze-utils/setup";
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { generateUserID } from "../../maze-utils/src/setup";
const inTest = typeof chrome === "undefined";

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

8
src/utils/pageCleaner.ts Normal file
View File

@@ -0,0 +1,8 @@
export function cleanPage() {
// For live-updates
if (document.readyState === "complete") {
for (const element of document.querySelectorAll("#categoryPillParent, .playerButton, .sponsorThumbnailLabel, #submissionNoticeContainer, .sponsorSkipNoticeContainer, #sponsorBlockPopupContainer, .skipButtonControlBarContainer, #previewbar, .sponsorBlockChapterBar")) {
element.remove();
}
}
}

View File

@@ -1,5 +1,5 @@
import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types";
import { getFormattedTimeToSeconds } from "../maze-utils/formating";
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
export function getControls(): HTMLElement {
const controlsSelectors = [
@@ -93,4 +93,8 @@ export function getExistingChapters(currentVideoID: VideoID, duration: number):
}
return chapters;
}
export function isPlayingPlaylist() {
return !!document.URL.includes("&list=");
}

View File

@@ -1,7 +1,7 @@
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../maze-utils/video";
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
import Config from "../config";
import { getVideoLabel } from "./videoLabels";
import { setThumbnailListener } from "../maze-utils/thumbnailManagement";
import { setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
export async function labelThumbnails(thumbnails: HTMLImageElement[]): Promise<void> {
await Promise.all(thumbnails.map((t) => labelThumbnail(t)));

View File

@@ -1,5 +1,5 @@
import { Category, CategorySkipOption, VideoID } from "../types";
import { getHash } from "../maze-utils/hash";
import { getHash } from "../../maze-utils/src/hash";
import Utils from "../utils";
import { logWarn } from "./logger";

View File

@@ -1,5 +1,5 @@
import { objectToURI } from "../maze-utils";
import { getHash } from "../maze-utils/hash";
import { objectToURI } from "../../maze-utils/src";
import { getHash } from "../../maze-utils/src/hash";
import Config from "../config";
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
import { ContentContainer } from "../types";
@@ -26,7 +26,7 @@ export async function openWarningDialog(contentContainer: ContentContainer): Pro
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