Compare commits

...

134 Commits

Author SHA1 Message Date
Ajay
ab5291702a bump version 2024-09-27 02:06:20 -04:00
Ajay
bec183d595 Fix not allowing you to type ' in live chats 2024-09-27 02:06:08 -04:00
Ajay
a2f4f34dfe bump version 2024-09-26 19:27:30 -04:00
Ajay
7ba6fee9e5 Add more room for server-side rendering check 2024-09-26 19:26:01 -04:00
Ajay
9d9713ee82 Attempt to fix issue with autoplay disabled on some browser breaking skipping
Fix #2074 and Fix #2078
2024-09-26 19:18:30 -04:00
Ajay
91556f981a Remove unnecessary log
Fix #2111
2024-09-23 16:00:17 -04:00
Ajay
9f86aa6740 bump version 2024-09-23 04:07:50 -04:00
Ajay
466573e2a4 update translations 2024-09-23 04:07:36 -04:00
Ajay
4c417b10fe Make sure old segment fetch request doesn't get used
May fix #2101

See dbf80b4929 (r147058589)
2024-09-23 04:05:20 -04:00
Ajay
36549e7898 Fix chapter box title selector 2024-09-23 03:58:11 -04:00
Ajay
0d2d5f2eb0 try to improve chapters importing 2024-09-23 03:46:19 -04:00
Ajay
e089b83797 Improve timeout waiting error 2024-09-23 03:32:33 -04:00
Ajay
dbd0d157a8 Fix preview bar not appearing on mobile due to new layout
Fixes #2109
2024-09-23 01:37:11 -04:00
Ajay
aef2b113f8 Remove legacy keybdind check 2024-09-12 19:45:35 -04:00
Ajay
e610e03e46 Make submission menu keyboard of ff not open find menu 2024-09-12 19:44:35 -04:00
Ajay
17cbd068b9 Add size limit to description for edge 2024-09-08 01:32:58 -04:00
Ajay
7064d3fe82 bump version 2024-09-08 01:30:09 -04:00
Ajay
abfe0b3f38 Fix minimum firefox version in manifest
Failing web-ext linter
2024-09-08 01:29:55 -04:00
Ajay
045607b146 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2024-09-08 01:24:48 -04:00
Ajay
be90af7440 bump version 2024-09-08 01:24:47 -04:00
Ajay Ramachandran
0a7497be27 Merge pull request #1886 from Choromanski/master
Overriding default webpack performance config to reduce warnings
2024-09-08 01:23:19 -04:00
Ajay
fcf096c66d Fix lint issue 2024-09-08 01:21:59 -04:00
Ajay
12e8c7aad8 Add way to copy debug logs from popup 2024-09-08 01:20:25 -04:00
Ajay Ramachandran
2a2896786c Merge pull request #2095 from mchangrh/inv-ci
fully typed updown response
2024-09-07 01:00:23 -04:00
Michael C
20d538b6e9 fully typed updown response 2024-09-07 00:17:03 -04:00
Ajay Ramachandran
36a5bc105b Merge pull request #2040 from ajayyy/dependabot/npm_and_yarn/braces-3.0.3
Bump braces from 3.0.2 to 3.0.3
2024-09-06 16:32:37 -04:00
Ajay Ramachandran
e6c39c099c Merge pull request #2091 from ajayyy/ci/update_invidious_list
Update Invidious List
2024-09-06 02:48:56 -04:00
github-actions[bot]
d74ece2785 Update Invidious List 2024-09-06 06:46:40 +00:00
Ajay
e7eeee1b9d Fix invidious CI 2024-09-06 02:45:32 -04:00
Ajay Ramachandran
c0a579e84b Merge pull request #2083 from ajayyy/dependabot/npm_and_yarn/webpack-5.94.0
Bump webpack from 5.75.0 to 5.94.0
2024-09-06 02:23:51 -04:00
Ajay
e0a602eaf4 Fix compatibility with Vinegar extension on Safari
Also fixes miniplayer detection
2024-09-06 02:16:35 -04:00
dependabot[bot]
536ac01476 Bump webpack from 5.75.0 to 5.94.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.75.0 to 5.94.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.75.0...v5.94.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 02:53:06 +00:00
Ajay Ramachandran
18a0cf2953 Merge pull request #2089 from ajayyy/dependabot/npm_and_yarn/multi-4c1fdd2521
Bump ws and web-ext
2024-09-05 22:51:48 -04:00
Ajay Ramachandran
7ae29f967b Merge pull request #2088 from ajayyy/dependabot/npm_and_yarn/axios-1.7.7
Bump axios from 1.6.1 to 1.7.7
2024-09-05 22:51:42 -04:00
Ajay Ramachandran
2509cf9919 Merge pull request #2090 from ajayyy/dependabot/npm_and_yarn/multi-bc3fc687d2
Bump tough-cookie and web-ext
2024-09-05 22:51:34 -04:00
dependabot[bot]
e8c65fb21b Bump tough-cookie and web-ext
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) to 4.1.4 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together.


Updates `tough-cookie` from 4.1.2 to 4.1.4
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.1.2...v4.1.4)

Updates `web-ext` from 7.10.0 to 8.2.0
- [Release notes](https://github.com/mozilla/web-ext/releases)
- [Commits](https://github.com/mozilla/web-ext/compare/7.10.0...8.2.0)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
- dependency-name: web-ext
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 02:38:48 +00:00
dependabot[bot]
5d155f6c74 Bump ws and web-ext
Bumps [ws](https://github.com/websockets/ws) to 8.17.1 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together.


Updates `ws` from 8.13.0 to 8.17.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.13.0...8.17.1)

Updates `web-ext` from 7.10.0 to 8.2.0
- [Release notes](https://github.com/mozilla/web-ext/releases)
- [Commits](https://github.com/mozilla/web-ext/compare/7.10.0...8.2.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
- dependency-name: web-ext
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 02:38:40 +00:00
dependabot[bot]
dca6780979 Bump axios from 1.6.1 to 1.7.7
Bumps [axios](https://github.com/axios/axios) from 1.6.1 to 1.7.7.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.1...v1.7.7)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 02:38:38 +00:00
Ajay Ramachandran
65b7d6589a Fix web ext signing 2024-09-05 22:37:33 -04:00
Ajay
dbf80b4929 Add pre-fetching on hover with a small segment data cache 2024-09-02 03:22:33 -04:00
Ajay
e181c64775 Use consistent request url for better caching 2024-09-02 02:04:32 -04:00
Ajay
ac9b2d12fe Decrease sponsor frame flashing when skipping segments at the start 2024-09-01 03:51:37 -04:00
Ajay
3738b180dd Don't play notification if video is muted
Fixes #1888
2024-07-17 13:17:50 -04:00
Ajay
3ec1e85712 Don't play audio notification when previewing 2024-07-17 13:14:52 -04:00
Ajay
e9a77355bb bump version 2024-06-23 00:33:39 +05:30
Ajay
116190f2b3 Warn if server-side ad wasn't detected and submitting a segment 2024-06-23 00:31:11 +05:30
Ajay
4bbd59b988 Support server side rendered ads
Fixes #2035
2024-06-23 00:03:03 +05:30
Ajay
5c279d80df Show submission count from server 2024-06-22 17:42:48 +05:30
Ajay
f177b95a5a Fix submission menu after video ends and unskip button after video ends 2024-06-22 17:06:29 +05:30
Ajay Ramachandran
d86dd37ec0 Merge pull request #2046 from ajayyy/mv3
Support mv3
2024-06-22 16:29:43 +05:30
Ajay
fae7d921a7 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into mv3 2024-06-22 16:27:18 +05:30
Ajay
60a2eff40a Fix linting issue 2024-06-19 23:16:29 +05:30
Ajay
98a4a076bc Support invidious in mv3 2024-06-19 23:14:27 +05:30
Ajay
a95020dda3 Manifest v3 without invidious working 2024-06-19 22:37:29 +05:30
dependabot[bot]
3c2bf20321 Bump braces from 3.0.2 to 3.0.3
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-18 01:45:58 +00:00
Ajay Ramachandran
5e179cf9ff Merge pull request #2036 from mini-bomba/refresh-fix
Fix refreshing
2024-06-13 09:07:57 +05:30
mini-bomba
0b946d5ef7 Don't fetch segments when videoID is null
Also log an error to the console if we ever try to do this in the future
2024-06-13 01:18:02 +02:00
mini-bomba
f48c57b1c5 Don't inject into the cookie rotate iframe
There's nothing there, it only breaks the refreshing.
2024-06-13 01:09:27 +02:00
Ajay
98ac2fc618 bump version 2024-06-06 19:53:08 +05:30
Ajay
36e3333e39 Fix submission menu disappearing when video ends 2024-06-06 19:52:57 +05:30
Ajay
15fcc971f6 bump version 2024-05-24 03:26:02 -04:00
Ajay
32adbb9581 update translations 2024-05-24 03:22:36 -04:00
Ajay
4ecd02510b Fix warnings 2024-05-24 03:22:07 -04:00
Ajay
0dbb6474b8 Fix preview bar on mobile
Fixes #2021
2024-05-24 03:20:21 -04:00
Ajay
c974c2a6d6 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2024-05-24 03:15:59 -04:00
Ajay
a6695254b6 Fix skipping and preview bar on "add to queue", miniplayer and hover preview
Fixes #1486
2024-05-24 03:15:58 -04:00
Ajay
e8a82eddca Change chapters sorting 2024-05-09 19:09:37 -04:00
Ajay Ramachandran
d3684dbedd Merge pull request #2017 from stdedos/fix/disabled-comment-typo
Fix comment typo
2024-05-08 17:54:10 -04:00
Stavros Ntentos
6c9a403731 Fix comment typo
Signed-off-by: Stavros Ntentos <133706+stdedos@users.noreply.github.com>
2024-05-08 23:44:01 +03:00
Ajay
3be51c89a9 Fix popup sometimes saying "No video found"
Fixes #2006
Fixes #2013
Fixes #1999
2024-04-30 20:54:54 -04:00
Ajay
5b845a56e1 Fix unsubmitted segments sometimes manual skipping if manual skip enabled for category
Fixes #2002
2024-04-28 22:59:02 -04:00
Ajay
7b572c3c0e Fix previewed segment check
Fixes #1996
2024-04-28 21:51:35 -04:00
Ajay
56bb22d03d Allow importing segments with no category
Also fixes case when importing chapters when having no permission to submit chapters
2024-04-26 16:44:20 -04:00
Ajay
e1d656f43f Make notice visibility option not appear when notice disabled 2024-04-04 13:54:47 -04:00
Ajay
e14b3c76f9 Improve submission box padding 2024-03-27 16:14:10 -04:00
Ajay
de830cda48 bump version 2024-03-18 21:03:23 -04:00
Ajay
d8057fb547 bump translations 2024-03-18 21:03:09 -04:00
Ajay Ramachandran
2dd69d443b Merge pull request #1983 from HanYaodong/dev
[Fix #1979] Stop Refresh Animation on Popup When Content Script is Not Injected
2024-03-18 20:59:19 -04:00
Ajay
6732850b41 Fix chapters not changing when skipping is disabled 2024-03-18 20:58:07 -04:00
Ajay
287233785e Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2024-03-18 19:45:36 -04:00
Ajay
de094cb11f Add option to store downvotes when done in private tabs 2024-03-18 19:45:35 -04:00
Ajay Ramachandran
2307691044 Merge pull request #1986 from ajayyy/dependabot/npm_and_yarn/follow-redirects-1.15.6
Bump follow-redirects from 1.15.4 to 1.15.6
2024-03-16 21:07:43 -04:00
dependabot[bot]
2dca0d7526 Bump follow-redirects from 1.15.4 to 1.15.6
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-16 23:28:04 +00:00
HanYaodong
a1505bcf20 Refractor popup infoFound() empty message detection 2024-03-09 23:47:48 +08:00
HanYaodong
25b53754ba Stop refresh animation on popup when content script is not injected 2024-03-09 23:42:17 +08:00
Ajay
6c71036356 update translations 2024-03-07 17:22:22 -05:00
Ajay
f55f2606a7 bump version 2024-03-07 17:21:50 -05:00
Ajay
e9b7abd219 Fix rate-limiting when setting options from options page 2024-03-07 17:20:23 -05:00
Ajay Ramachandran
63d3309605 Merge pull request #1948 from ajayyy/dependabot/npm_and_yarn/follow-redirects-1.15.4
Bump follow-redirects from 1.15.3 to 1.15.4
2024-02-29 03:35:22 -05:00
Ajay Ramachandran
b4615d7d79 Merge pull request #1950 from ajayyy/dependabot/npm_and_yarn/postcss-and-web-ext-8.4.33
Bump postcss and web-ext
2024-02-29 03:35:13 -05:00
Ajay Ramachandran
ec7eee650b Merge pull request #1979 from HanYaodong/dev_refresh
[Fix] Stop Refresh Animation on Popup When Triggered on Non-YouTube Pages
2024-02-29 03:34:45 -05:00
HanYaodong
154bb1749d Stop refresh animation on popup when not on youtube page 2024-02-29 12:36:06 +08:00
Ajay
742eb7ef57 bump version 2024-02-28 01:13:57 -05:00
Ajay
88526aa46e Fix missing key in tooltip and update maze utils 2024-02-27 22:16:53 -05:00
Ajay
1909e66c87 Move category pill to top if on description on right layout 2024-02-27 22:13:58 -05:00
Ajay
5f8447ec6b bump version 2024-02-24 18:54:19 -05:00
Ajay
e6db5b43ff update translations 2024-02-24 18:54:11 -05:00
Ajay
6566c129c7 Fix issue with BlockTube version 0.4.1 2024-02-24 18:53:26 -05:00
Ajay
e7f4be2d57 Don't require preview for starting segments 2024-02-21 14:32:44 -05:00
Ajay
9d04482d19 bump version 2024-02-13 22:35:44 -05:00
Ajay
7c661f8e67 update translations 2024-02-13 22:33:11 -05:00
Ajay
0b7a2fd197 Fix adding custom Invidious instances on Firefox
Fixes #815
2024-02-13 22:32:37 -05:00
Ajay
3382d8a500 Fix chapter names not appearing 2024-02-13 21:52:10 -05:00
Ajay
5d871d5fe7 Fix hidden mute segments sometimes still muting 2024-02-13 21:32:23 -05:00
Ajay Ramachandran
b7c5737a95 Merge pull request #1967 from HanYaodong/dev
Fix license info in README
2024-02-02 19:17:14 -05:00
HanYaodong
0cca1c3566 Fix license info in README 2024-02-02 19:32:31 +08:00
Ajay
e0fe0fad67 Don't close submission menu if submission didn't go through
Fxies submission menu closing for warning about previewing a segment
2024-02-01 13:31:19 -05:00
Ajay
c0bc068a18 Handle exceptions for voting
Maybe fixes #1961
2024-01-31 19:06:16 -05:00
Ajay
7cb413db15 Don't display preview bar while scrubbing on mobile
Should help with #1962
2024-01-31 18:58:06 -05:00
Ajay
fdf1a6acf9 bump version 2024-01-30 23:12:36 -05:00
Ajay
b533c6c1c8 update translations 2024-01-30 23:12:28 -05:00
Ajay
926423db5c Fix compatibility with vinegar on Safari 2024-01-30 23:12:03 -05:00
Ajay
e7d55d2bac Fix preview bar end time sometimes being inaccurate 2024-01-25 19:38:10 -05:00
Ajay
16f27e5c5c Move animation utils to maze utils 2024-01-24 16:21:31 -05:00
Ajay
88dc8db6e7 bump version 2024-01-23 14:27:05 -05:00
Ajay
c69a574379 update translations 2024-01-23 14:27:01 -05:00
Ajay
516d624f16 Don't require preview for segments ending at end of the video
Fixes #1959
2024-01-23 14:25:07 -05:00
Ajay
a662c3e04f Reloading after creating segments shouldn't require previewing 2024-01-23 14:22:54 -05:00
Ajay
985910cbf6 Count previewed unsubmitted segments previewed manually 2024-01-23 14:20:24 -05:00
Ajay
feae86f6ea Don't throw errors on extension live-update 2024-01-21 20:42:40 -05:00
Ajay
1f96e3b117 Improve precision of hover preview
Also fixes issue with YouTube "most-replayed" messing it up
2024-01-21 20:35:00 -05:00
Ajay
b3b5d46e4e bump version 2024-01-21 15:57:35 -05:00
Ajay
c996680a58 Fix category selection changes not saving 2024-01-21 15:57:22 -05:00
Ajay
ade4654ae0 bump version 2024-01-21 14:26:27 -05:00
Ajay
4e9e6282f6 update translations 2024-01-21 14:26:16 -05:00
Ajay
783326afca Use runtime.getURL 2024-01-21 13:51:32 -05:00
Ajay
8dfd06919b Only require a preview for skip and mute segments 2024-01-21 13:40:01 -05:00
Ajay
50ee690717 Only prevent scrolling while editing 2024-01-21 13:37:29 -05:00
Ajay
74aebd32a7 Prevent zooming when trying to scroll to edit times 2024-01-21 13:31:45 -05:00
Ajay
e489d0f913 Fix german title not being shortened 2024-01-16 18:39:29 -05:00
Ajay
814df46521 Don't warn about using the word intro in chapter names 2024-01-16 18:21:44 -05:00
dependabot[bot]
ba92e6e386 Bump postcss and web-ext
Bumps [postcss](https://github.com/postcss/postcss) to 8.4.33 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together.


Updates `postcss` from 8.4.21 to 8.4.33
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.21...8.4.33)

Updates `web-ext` from 7.6.2 to 7.10.0
- [Release notes](https://github.com/mozilla/web-ext/releases)
- [Commits](https://github.com/mozilla/web-ext/compare/7.6.2...7.10.0)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
- dependency-name: web-ext
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-12 18:16:09 +00:00
dependabot[bot]
bdbe8b60cc Bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 03:55:58 +00:00
Brian Choromanski
2a5b76ddfb Overriding default webpack performance config to reduce warnings 2023-10-19 15:09:19 -04:00
48 changed files with 3244 additions and 2770 deletions

View File

@@ -75,4 +75,4 @@ Icons made by:
### License ### License
This project is licensed under GNU LGPL v3 or any later version This project is licensed under GNU GPL v3 or any later version

View File

@@ -7,8 +7,7 @@ This file should not be shipped with the extension
/* /*
Criteria for inclusion: Criteria for inclusion:
Invidious Invidious
- 30d uptime >= 90% - uptime >= 80%
- available for at least 80/90 days
- must have been up for at least 90 days - must have been up for at least 90 days
- HTTPS only - HTTPS only
- url includes name (this is to avoid redirects) - url includes name (this is to avoid redirects)

View File

@@ -1,31 +1,34 @@
import { InvidiousInstance, instanceMap } from "./invidiousType" import { InvidiousInstance, monitor } from "./invidiousType"
import * as data from "../ci/invidious_instances.json"; import * as data from "../ci/invidious_instances.json";
// only https servers // only https servers
const mapped: instanceMap = data const mapped = (data as InvidiousInstance[])
.filter((i: InvidiousInstance) => i[1]?.type === "https") .filter((i) =>
.map((instance: InvidiousInstance) => { i[1]?.type === "https"
&& i[1]?.monitor?.enabled
)
.map((instance) => {
const monitor = instance[1].monitor as monitor;
return { return {
name: instance[0], name: instance[0],
url: instance[1].uri, url: instance[1].uri,
dailyRatios: instance[1].monitor.dailyRatios, uptime: monitor.uptime || 0,
thirtyDayUptime: instance[1]?.monitor["30dRatio"].ratio, down: monitor.down ?? false,
created_at: monitor.created_at,
} }
}); });
// reliability and sanity checks // reliability and sanity checks
const reliableCheck = mapped const reliableCheck = mapped
.filter(instance => { .filter(instance => {
// 30d uptime >= 90% const uptime = instance.uptime > 80 && !instance.down;
const thirtyDayUptime = Number(instance.thirtyDayUptime) >= 90; const nameIncluded = instance.url.includes(instance.name);
// available for at least 80/90 days const ninetyDays = 90 * 24 * 60 * 60 * 1000;
const dailyRatioCheck = instance.dailyRatios.filter(status => status.label !== "black"); const ninetyDaysAgo = new Date(Date.now() - ninetyDays);
return thirtyDayUptime && dailyRatioCheck.length >= 80; const createdAt = new Date(instance.created_at).getTime() < ninetyDaysAgo.getTime();
return uptime && nameIncluded && createdAt;
}) })
// url includes name
.filter(instance => instance.url.includes(instance.name));
export function getInvidiousList(): string[] { export const getInvidiousList = (): string[] =>
return reliableCheck.map(instance => instance.name).sort() reliableCheck.map(instance => instance.name).sort()
}

View File

@@ -1,26 +1,56 @@
type ratio = {
ratio: string;
label: string;
}
export type instanceMap = {
name: string;
url: string;
dailyRatios: {ratio: string; label: string }[];
thirtyDayUptime: string;
}[]
export type InvidiousInstance = [ export type InvidiousInstance = [
string, string,
{ {
flag: string; flag: string;
region: string; region: string;
stats: null | { stats: null | ivStats;
cors: null | boolean;
api: null | boolean;
type: "https" | "http" | "onion" | "i2p";
uri: string;
monitor: null | monitor;
}
]
export type monitor = {
token: string;
url: string;
alias: string;
last_status: number;
uptime: number;
down: boolean;
down_since: null | string;
up_since: null | string;
error: null | string;
period: number;
apdex_t: number;
string_match: string;
enabled: boolean;
published: boolean;
disabled_locations: string[];
recipients: string[];
last_check_at: string;
next_check_at: string;
created_at: string;
mute_until: null | string;
favicon_url: string;
custom_headers: Record<string, string>;
http_verb: string;
http_body: string;
ssl: {
tested_at: string;
expires_at: string;
valid: boolean;
error: null | string;
};
}
export type ivStats = {
version: string; version: string;
software: { software: {
name: string; name: "invidious" | string;
version: string; version: string;
branch: string; branch: "master" | string;
}; };
openRegistrations: boolean; openRegistrations: boolean;
usage: { usage: {
@@ -34,21 +64,9 @@ export type InvidiousInstance = [
updatedAt: number; updatedAt: number;
lastChannelRefreshedAt: number; lastChannelRefreshedAt: number;
}; };
playback: {
totalRequests: number;
successfulRequests: number;
ratio: number;
}; };
cors: boolean | null; }
api: boolean | null;
type: "https" | "http" | "onion" | "i2p";
uri: string;
monitor: null | {
monitorId: number;
createdAt: number;
statusClass: string;
name: string;
url: string | null;
type: "HTTP(s)";
dailyRatios: ratio[];
"90dRatio": ratio;
"30dRatio": ratio;
};
}
]

View File

@@ -1 +1 @@
["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"] ["www.youtubekids.com","inv.nadeko.net","inv.tux.pizza","invidious.adminforge.de","invidious.jing.rocks","invidious.nerdvpn.de","invidious.perennialte.ch","invidious.privacyredirect.com","invidious.reallyaweso.me","invidious.yourdevice.ch","iv.ggtyler.dev","iv.nboeck.de","yewtu.be"]

View File

@@ -1,12 +1,156 @@
{ {
"optional_permissions": [ "host_permissions": [
"declarativeContent", "https://*.youtube.com/*",
"webNavigation" "https://sponsor.ajay.app/*"
], ],
"background": { "optional_host_permissions": [
"persistent": false "*://*/*"
],
"web_accessible_resources": [{
"resources": [
"icons/LogoSponsorBlocker256px.png",
"icons/IconSponsorBlocker256px.png",
"icons/PlayerStartIconSponsorBlocker.svg",
"icons/PlayerStopIconSponsorBlocker.svg",
"icons/PlayerUploadIconSponsorBlocker.svg",
"icons/PlayerUploadFailedIconSponsorBlocker.svg",
"icons/PlayerCancelSegmentIconSponsorBlocker.svg",
"icons/clipboard.svg",
"icons/settings.svg",
"icons/pencil.svg",
"icons/check.svg",
"icons/check-smaller.svg",
"icons/upvote.png",
"icons/downvote.png",
"icons/thumbs_down.svg",
"icons/thumbs_down_locked.svg",
"icons/thumbs_up.svg",
"icons/help.svg",
"icons/report.png",
"icons/close.png",
"icons/skipIcon.svg",
"icons/refresh.svg",
"icons/beep.ogg",
"icons/pause.svg",
"icons/stop.svg",
"icons/skip.svg",
"icons/heart.svg",
"icons/visible.svg",
"icons/not_visible.svg",
"icons/sort.svg",
"icons/money.svg",
"icons/segway.png",
"icons/close-smaller.svg",
"icons/right-arrow.svg",
"icons/campaign.svg",
"icons/star.svg",
"icons/lightbulb.svg",
"icons/bolt.svg",
"icons/stopwatch.svg",
"icons/music-note.svg",
"icons/import.svg",
"icons/export.svg",
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",
"icons/dearrow.svg",
"popup.html",
"popup.css",
"content.css",
"shared.css",
"js/document.js",
"libs/Source+Sans+Pro.css",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2"
],
"matches": ["<all_urls>"]
}],
"content_scripts": [
{
"world": "MAIN",
"js": [
"./js/document.js"
],
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
],
"exclude_matches": [
"https://accounts.youtube.com/RotateCookiesPage*"
],
"all_frames": true,
"run_at": "document_start"
}, },
"permissions": [ {
"https://*.youtube.com/*" "world": "ISOLATED",
"js": [
"./js/content.js"
],
"css": [
"content.css",
"shared.css"
],
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
],
"exclude_matches": [
"https://accounts.youtube.com/RotateCookiesPage*"
],
"all_frames": true,
"run_at": "document_start"
}
],
"action": {
"default_title": "SponsorBlock",
"default_popup": "popup.html",
"default_icon": {
"16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png",
"64": "icons/IconSponsorBlocker64px.png",
"128": "icons/IconSponsorBlocker128px.png"
},
"theme_icons": [
{
"light": "icons/IconSponsorBlocker16px.png",
"dark": "icons/IconSponsorBlocker16px.png",
"size": 16
},
{
"light": "icons/IconSponsorBlocker32px.png",
"dark": "icons/IconSponsorBlocker32px.png",
"size": 32
},
{
"light": "icons/IconSponsorBlocker64px.png",
"dark": "icons/IconSponsorBlocker64px.png",
"size": 64
},
{
"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
}
] ]
},
"background": {
"service_worker": "./js/background.js"
},
"manifest_version": 3
} }

View File

@@ -2,7 +2,7 @@
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "sponsorBlocker@ajay.app", "id": "sponsorBlocker@ajay.app",
"strict_min_version": "56.0" "strict_min_version": "102.0"
}, },
"gecko_android": { "gecko_android": {
"strict_min_version": "113.0" "strict_min_version": "113.0"
@@ -11,9 +11,6 @@
"background": { "background": {
"persistent": false "persistent": false
}, },
"permissions": [
"scripting"
],
"browser_action": { "browser_action": {
"default_area": "navbar" "default_area": "navbar"
} }

View File

@@ -0,0 +1,136 @@
{
"web_accessible_resources": [
"icons/LogoSponsorBlocker256px.png",
"icons/IconSponsorBlocker256px.png",
"icons/PlayerStartIconSponsorBlocker.svg",
"icons/PlayerStopIconSponsorBlocker.svg",
"icons/PlayerUploadIconSponsorBlocker.svg",
"icons/PlayerUploadFailedIconSponsorBlocker.svg",
"icons/PlayerCancelSegmentIconSponsorBlocker.svg",
"icons/clipboard.svg",
"icons/settings.svg",
"icons/pencil.svg",
"icons/check.svg",
"icons/check-smaller.svg",
"icons/upvote.png",
"icons/downvote.png",
"icons/thumbs_down.svg",
"icons/thumbs_down_locked.svg",
"icons/thumbs_up.svg",
"icons/help.svg",
"icons/report.png",
"icons/close.png",
"icons/skipIcon.svg",
"icons/refresh.svg",
"icons/beep.ogg",
"icons/pause.svg",
"icons/stop.svg",
"icons/skip.svg",
"icons/heart.svg",
"icons/visible.svg",
"icons/not_visible.svg",
"icons/sort.svg",
"icons/money.svg",
"icons/segway.png",
"icons/close-smaller.svg",
"icons/right-arrow.svg",
"icons/campaign.svg",
"icons/star.svg",
"icons/lightbulb.svg",
"icons/bolt.svg",
"icons/stopwatch.svg",
"icons/music-note.svg",
"icons/import.svg",
"icons/export.svg",
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",
"icons/dearrow.svg",
"popup.html",
"popup.css",
"content.css",
"shared.css",
"js/document.js",
"libs/Source+Sans+Pro.css",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2"
],
"permissions": [
"https://sponsor.ajay.app/*"
],
"optional_permissions": [
"*://*/*"
],
"browser_action": {
"default_title": "SponsorBlock",
"default_popup": "popup.html",
"default_icon": {
"16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png",
"64": "icons/IconSponsorBlocker64px.png",
"128": "icons/IconSponsorBlocker128px.png"
},
"theme_icons": [
{
"light": "icons/IconSponsorBlocker16px.png",
"dark": "icons/IconSponsorBlocker16px.png",
"size": 16
},
{
"light": "icons/IconSponsorBlocker32px.png",
"dark": "icons/IconSponsorBlocker32px.png",
"size": 32
},
{
"light": "icons/IconSponsorBlocker64px.png",
"dark": "icons/IconSponsorBlocker64px.png",
"size": 64
},
{
"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
}
]
},
"background": {
"scripts":[
"./js/background.js"
]
},
"content_scripts": [{
"run_at": "document_start",
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
],
"exclude_matches": [
"https://accounts.youtube.com/RotateCookiesPage*"
],
"all_frames": true,
"js": [
"./js/content.js"
],
"css": [
"content.css",
"shared.css"
]
}],
"manifest_version": 2
}

View File

@@ -1,141 +1,10 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "SponsorBlock", "short_name": "SponsorBlock",
"version": "5.5", "version": "5.9.2",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_Description__", "description": "__MSG_Description__",
"homepage_url": "https://sponsor.ajay.app", "homepage_url": "https://sponsor.ajay.app",
"content_scripts": [{
"run_at": "document_start",
"matches": [
"https://*.youtube.com/*",
"https://www.youtube-nocookie.com/embed/*"
],
"all_frames": true,
"js": [
"./js/content.js"
],
"css": [
"content.css",
"shared.css"
]
}],
"web_accessible_resources": [
"icons/LogoSponsorBlocker256px.png",
"icons/IconSponsorBlocker256px.png",
"icons/PlayerStartIconSponsorBlocker.svg",
"icons/PlayerStopIconSponsorBlocker.svg",
"icons/PlayerUploadIconSponsorBlocker.svg",
"icons/PlayerUploadFailedIconSponsorBlocker.svg",
"icons/PlayerCancelSegmentIconSponsorBlocker.svg",
"icons/clipboard.svg",
"icons/settings.svg",
"icons/pencil.svg",
"icons/check.svg",
"icons/check-smaller.svg",
"icons/upvote.png",
"icons/downvote.png",
"icons/thumbs_down.svg",
"icons/thumbs_down_locked.svg",
"icons/thumbs_up.svg",
"icons/help.svg",
"icons/report.png",
"icons/close.png",
"icons/skipIcon.svg",
"icons/refresh.svg",
"icons/beep.ogg",
"icons/pause.svg",
"icons/stop.svg",
"icons/skip.svg",
"icons/heart.svg",
"icons/visible.svg",
"icons/not_visible.svg",
"icons/sort.svg",
"icons/money.svg",
"icons/segway.png",
"icons/close-smaller.svg",
"icons/right-arrow.svg",
"icons/campaign.svg",
"icons/star.svg",
"icons/lightbulb.svg",
"icons/bolt.svg",
"icons/stopwatch.svg",
"icons/music-note.svg",
"icons/import.svg",
"icons/export.svg",
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",
"icons/dearrow.svg",
"popup.html",
"popup.css",
"content.css",
"shared.css",
"js/document.js",
"libs/Source+Sans+Pro.css",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2"
],
"permissions": [
"storage",
"https://sponsor.ajay.app/*"
],
"optional_permissions": [
"*://*/*"
],
"browser_action": {
"default_title": "SponsorBlock",
"default_popup": "popup.html",
"default_icon": {
"16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png",
"64": "icons/IconSponsorBlocker64px.png",
"128": "icons/IconSponsorBlocker128px.png"
},
"theme_icons": [
{
"light": "icons/IconSponsorBlocker16px.png",
"dark": "icons/IconSponsorBlocker16px.png",
"size": 16
},
{
"light": "icons/IconSponsorBlocker32px.png",
"dark": "icons/IconSponsorBlocker32px.png",
"size": 32
},
{
"light": "icons/IconSponsorBlocker64px.png",
"dark": "icons/IconSponsorBlocker64px.png",
"size": 64
},
{
"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
}
]
},
"background": {
"scripts":[
"./js/background.js"
]
},
"icons": { "icons": {
"16": "icons/IconSponsorBlocker16px.png", "16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png", "32": "icons/IconSponsorBlocker32px.png",
@@ -145,9 +14,12 @@
"512": "icons/IconSponsorBlocker512px.png", "512": "icons/IconSponsorBlocker512px.png",
"1024": "icons/IconSponsorBlocker1024px.png" "1024": "icons/IconSponsorBlocker1024px.png"
}, },
"permissions": [
"storage",
"scripting"
],
"options_ui": { "options_ui": {
"page": "options/options.html", "page": "options/options.html",
"open_in_tab": true "open_in_tab": true
}, }
"manifest_version": 2
} }

View File

@@ -2,9 +2,6 @@
"background": { "background": {
"persistent": false "persistent": false
}, },
"permissions": [
"scripting"
],
"optional_permissions": [ "optional_permissions": [
"webNavigation" "webNavigation"
] ]

3944
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -38,14 +38,14 @@
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "4.9", "typescript": "4.9",
"web-ext": "^7.6.2", "web-ext": "^8.2.0",
"webpack": "^5.75.0", "webpack": "^5.94.0",
"webpack-cli": "^4.10.0", "webpack-cli": "^4.10.0",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"scripts": { "scripts": {
"web-run": "npm run web-run:chrome", "web-run": "npm run web-run:chrome",
"web-sign": "web-ext sign -s dist", "web-sign": "web-ext sign --channel unlisted -s dist",
"web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/", "web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/",
"web-run:firefox-android": "cd dist && web-ext run -t firefox-android --firefox-apk org.mozilla.fenix", "web-run:firefox-android": "cd dist && web-ext run -t firefox-android --firefox-apk org.mozilla.fenix",
"web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium", "web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium",

View File

@@ -26,6 +26,11 @@
transition: transform .1s cubic-bezier(0,0,0.2,1); transition: transform .1s cubic-bezier(0,0,0.2,1);
} }
/* May 2024 hover preview */
.YtPlayerProgressBarProgressBar #previewbar {
transform: none;
}
.ytp-big-mode #previewbar { .ytp-big-mode #previewbar {
transform: scaleY(0.625) translateY(-30%) translateY(1.5px); transform: scaleY(0.625) translateY(-30%) translateY(1.5px);
} }
@@ -780,6 +785,18 @@ input::-webkit-inner-spin-button {
line-height: 1.5em; line-height: 1.5em;
} }
/* Description on right layout */
#title > #categoryPillParent {
font-size: 2rem;
font-weight: bold;
display: flex;
justify-content: center;
line-height: 2.8rem;
}
#title > #categoryPillParent > #categoryPill.cbPillOpen {
margin-bottom: 5px;
}
#categoryPillParent { #categoryPillParent {
height: fit-content; height: fit-content;
margin-top: auto; margin-top: auto;

View File

@@ -219,9 +219,10 @@
__MSG_showSkipNotice__ __MSG_showSkipNotice__
</label> </label>
</div> </div>
</div>
<div data-type="selector" data-sync="noticeVisibilityMode"> <div data-type="selector" data-sync="noticeVisibilityMode" data-dependent-on="dontShowNotice">
<br/>
<label class="optionLabel" for="noticeVisibilityMode">__MSG_noticeVisibilityLabel__:</label> <label class="optionLabel" for="noticeVisibilityMode">__MSG_noticeVisibilityLabel__:</label>
<select id="noticeVisibilityMode" class="selector-element optionsSelector" > <select id="noticeVisibilityMode" class="selector-element optionsSelector" >
@@ -232,6 +233,7 @@
<option value="4">__MSG_noticeVisibilityMode4__</option> <option value="4">__MSG_noticeVisibilityMode4__</option>
</select> </select>
</div> </div>
</div>
<div data-type="toggle" data-sync="showCategoryGuidelines"> <div data-type="toggle" data-sync="showCategoryGuidelines">
<div class="switch-container"> <div class="switch-container">
@@ -635,6 +637,18 @@
<div class="small-description">__MSG_whatTrackDownvotes__</div> <div class="small-description">__MSG_whatTrackDownvotes__</div>
</div> </div>
<div data-type="toggle" data-sync="trackDownvotesInPrivate" data-confirm-on="false">
<div class="switch-container">
<label class="switch">
<input id="trackDownvotesInPrivate" type="checkbox" checked>
<span class="slider round"></span>
</label>
<label class="switch-label" for="trackDownvotesInPrivate">
__MSG_enableTrackDownvotesInPrivate__
</label>
</div>
</div>
<div data-type="button-press" data-sync="copyDebugInformation" data-confirm-message="copyDebugInformation"> <div data-type="button-press" data-sync="copyDebugInformation" data-confirm-message="copyDebugInformation">
<div class="option-button trigger-button"> <div class="option-button trigger-button">
__MSG_copyDebugInformation__ __MSG_copyDebugInformation__

View File

@@ -196,6 +196,8 @@
<a href="https://discord.gg/SponsorBlock" target="_blank" rel="noopener">Discord</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://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">__MSG_Donate__</a> <a href="https://sponsor.ajay.app/donate" target="_blank" rel="noopener" id="sbDonate">__MSG_Donate__</a>
<br />
<a id="debugLogs">__MSG_copyDebugLogs__</a>
</footer> </footer>
<button id="showNoticeAgain" style="display: none">__MSG_showNotice__</button> <button id="showNoticeAgain" style="display: none">__MSG_showNotice__</button>

View File

@@ -7,13 +7,9 @@ import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "../m
import { setupTabUpdates } from "../maze-utils/src/tab-updates"; import { setupTabUpdates } from "../maze-utils/src/tab-updates";
import { generateUserID } from "../maze-utils/src/setup"; import { generateUserID } from "../maze-utils/src/setup";
// Make the config public for debugging purposes
window.SB = Config;
import Utils from "./utils"; import Utils from "./utils";
import { getExtensionIdsToImportFrom } from "./utils/crossExtension"; import { getExtensionIdsToImportFrom } from "./utils/crossExtension";
import { isFirefoxOrSafari } from "../maze-utils/src"; import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
import { injectUpdatedScripts } from "../maze-utils/src/cleanup"; import { injectUpdatedScripts } from "../maze-utils/src/cleanup";
import { logWarn } from "./utils/logger"; import { logWarn } from "./utils/logger";
import { chromeP } from "../maze-utils/src/browserApi"; import { chromeP } from "../maze-utils/src/browserApi";
@@ -123,7 +119,7 @@ chrome.runtime.onInstalled.addListener(function () {
// If there is no userID, then it is the first install. // If there is no userID, then it is the first install.
if (!userID && !Config.local.alreadyInstalled){ if (!userID && !Config.local.alreadyInstalled){
//open up the install page //open up the install page
chrome.tabs.create({url: chrome.extension.getURL("/help/index.html")}); chrome.tabs.create({url: chrome.runtime.getURL("/help/index.html")});
//generate a userID //generate a userID
const newUserID = generateUserID(); const newUserID = generateUserID();
@@ -137,14 +133,21 @@ chrome.runtime.onInstalled.addListener(function () {
if (Config.config.supportInvidious) { if (Config.config.supportInvidious) {
if (!(await utils.containsInvidiousPermission())) { if (!(await utils.containsInvidiousPermission())) {
chrome.tabs.create({url: chrome.extension.getURL("/permissions/index.html")}); chrome.tabs.create({url: chrome.runtime.getURL("/permissions/index.html")});
} }
} }
}, 1500); }, 1500);
// Only do this once the old version understands how to clean itself up if (!isFirefoxOrSafari()) {
if (!isFirefoxOrSafari() && chrome.runtime.getManifest().version !== "5.4.13") {
injectUpdatedScripts().catch(logWarn); injectUpdatedScripts().catch(logWarn);
waitFor(() => Config.isReady()).then(() => {
if (Config.config.supportInvidious) {
injectUpdatedScripts([
utils.getExtraSiteRegistration()
])
}
}).catch(logWarn);
} }
}); });
@@ -160,8 +163,8 @@ async function registerFirefoxContentScript(options: Registration) {
ids: [options.id] ids: [options.id]
}).catch(() => []); }).catch(() => []);
if (existingRegistrations.length > 0 if (existingRegistrations && existingRegistrations.length > 0
&& existingRegistrations[0].matches.every((match) => options.matches.includes(match))) { && options.matches.every((match) => existingRegistrations[0].matches.includes(match))) {
// No need to register another script, already registered // No need to register another script, already registered
return; return;
} }
@@ -222,7 +225,7 @@ async function submitVote(type: number, UUID: string, category: string) {
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category; const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
//publish this vote try {
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection); const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
if (response.ok) { if (response.ok) {
@@ -245,6 +248,14 @@ async function submitVote(type: number, UUID: string, category: string) {
responseText: await response.text() responseText: await response.text()
}; };
} }
} catch (e) {
console.error(e);
return {
successType: -1,
statusCode: -1,
responseText: ""
};
}
} }

View File

@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg"; import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils"; import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { VoteResponse } from "../messageTypes"; import { VoteResponse } from "../messageTypes";
import { AnimationUtils } from "../utils/animationUtils"; import { AnimationUtils } from "../../maze-utils/src/animationUtils";
import { Tooltip } from "../render/Tooltip"; import { Tooltip } from "../render/Tooltip";
import { getErrorMessage } from "../../maze-utils/src/formating"; import { getErrorMessage } from "../../maze-utils/src/formating";
@@ -23,12 +23,14 @@ export interface CategoryPillState {
} }
class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryPillState> { class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryPillState> {
mainRef: React.MutableRefObject<HTMLSpanElement>;
tooltip?: Tooltip; tooltip?: Tooltip;
constructor(props: CategoryPillProps) { constructor(props: CategoryPillProps) {
super(props); super(props);
this.mainRef = React.createRef();
this.state = { this.state = {
segment: null, segment: null,
show: false, show: false,
@@ -43,17 +45,21 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
color: this.getTextColor(), color: this.getTextColor(),
} }
// To be able to remove the margin from the parent
this.mainRef?.current?.parentElement?.classList?.toggle("cbPillOpen", this.state.show);
return ( return (
<span style={style} <span style={style}
className={"sponsorBlockCategoryPill" + (!this.props.showTextByDefault ? " sbPillNoText" : "")} className={"sponsorBlockCategoryPill" + (!this.props.showTextByDefault ? " sbPillNoText" : "")}
aria-label={this.getTitleText()} aria-label={this.getTitleText()}
onClick={(e) => this.toggleOpen(e)} onClick={(e) => this.toggleOpen(e)}
onMouseEnter={() => this.openTooltip()} onMouseEnter={() => this.openTooltip()}
onMouseLeave={() => this.closeTooltip()}> onMouseLeave={() => this.closeTooltip()}
ref={this.mainRef}>
<span className="sponsorBlockCategoryPillTitleSection"> <span className="sponsorBlockCategoryPillTitleSection">
<img className="sponsorSkipLogo sponsorSkipObject" <img className="sponsorSkipLogo sponsorSkipObject"
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}> src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
</img> </img>
{ {
@@ -86,7 +92,7 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
)} )}
{/* Close Button */} {/* Close Button */}
<img src={chrome.extension.getURL("icons/close.png")} <img src={chrome.runtime.getURL("icons/close.png")}
className="categoryPillClose" className="categoryPillClose"
onClick={() => { onClick={() => {
this.setState({ show: false }); this.setState({ show: false });

View File

@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg"; import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils"; import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { VoteResponse } from "../messageTypes"; import { VoteResponse } from "../messageTypes";
import { AnimationUtils } from "../utils/animationUtils"; import { AnimationUtils } from "../../maze-utils/src/animationUtils";
import { Tooltip } from "../render/Tooltip"; import { Tooltip } from "../render/Tooltip";
import { getErrorMessage } from "../../maze-utils/src/formating"; import { getErrorMessage } from "../../maze-utils/src/formating";

View File

@@ -207,7 +207,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
{/* Close button */} {/* Close button */}
<img src={chrome.extension.getURL("icons/close.png")} <img src={chrome.runtime.getURL("icons/close.png")}
className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton" className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
+ (this.props.biggerCloseButton ? " biggerCloseButton" : "")} + (this.props.biggerCloseButton ? " biggerCloseButton" : "")}
onClick={() => this.close()}> onClick={() => this.close()}>

View File

@@ -15,6 +15,7 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
import { generateUserID } from "../../maze-utils/src/setup"; import { generateUserID } from "../../maze-utils/src/setup";
import { keybindToString } from "../../maze-utils/src/config"; import { keybindToString } from "../../maze-utils/src/config";
import { getFormattedTime } from "../../maze-utils/src/formating"; import { getFormattedTime } from "../../maze-utils/src/formating";
import { getCurrentTime, getVideo } from "../../maze-utils/src/video";
enum SkipButtonState { enum SkipButtonState {
Undo, // Unskip Undo, // Unskip
@@ -685,7 +686,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
getFullDurationCountdown(index: number): () => number { getFullDurationCountdown(index: number): () => number {
return () => { return () => {
const sponsorTime = this.segments[index]; const sponsorTime = this.segments[index];
const duration = Math.round((sponsorTime.segment[1] - this.contentContainer().v.currentTime) * (1 / this.contentContainer().v.playbackRate)); const duration = Math.round((sponsorTime.segment[1] - getCurrentTime()) * (1 / getVideo().playbackRate));
return Math.max(duration, Config.config.skipNoticeDuration); return Math.max(duration, Config.config.skipNoticeDuration);
}; };

View File

@@ -9,6 +9,7 @@ import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating"; import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { asyncRequestToServer } from "../utils/requests"; import { asyncRequestToServer } from "../utils/requests";
import { defaultPreviewTime } from "../utils/constants"; import { defaultPreviewTime } from "../utils/constants";
import { getVideo, getVideoDuration } from "../../maze-utils/src/video";
export interface SponsorTimeEditProps { export interface SponsorTimeEditProps {
index: number; index: number;
@@ -34,7 +35,7 @@ export interface SponsorTimeEditState {
chapterNameSelectorHovering: boolean; chapterNameSelectorHovering: boolean;
} }
const categoryNamesGrams: string[] = [].concat(...CompileConfig.categoryList.filter((name) => name !== "chapter") const categoryNamesGrams: string[] = [].concat(...CompileConfig.categoryList.filter((name) => !["chapter", "intro"].includes(name))
.map((name) => chrome.i18n.getMessage("category_" + name).split(/\/|\s|-/))); .map((name) => chrome.i18n.getMessage("category_" + name).split(/\/|\s|-/)));
class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> { class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> {
@@ -81,13 +82,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
componentDidMount(): void { componentDidMount(): void {
// Prevent inputs from triggering key events // Prevent inputs from triggering key events
document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', function (event) { document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', (e) => {
event.stopPropagation(); e.stopPropagation();
}); });
// Prevent scrolling while changing times // Prevent scrolling while changing times
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', function (event) { document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', (e) => {
event.preventDefault(); if (this.state.editing) {
e.preventDefault();
}
}, {passive: false}); }, {passive: false});
// Add as a config listener // Add as a config listener
@@ -221,7 +224,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
target="_blank" rel="noreferrer"> target="_blank" rel="noreferrer">
<img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix} <img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix}
className="helpButton" className="helpButton"
src={chrome.extension.getURL("icons/help.svg")} src={chrome.runtime.getURL("icons/help.svg")}
title={chrome.i18n.getMessage("categoryGuidelines")} /> title={chrome.i18n.getMessage("categoryGuidelines")} />
</a> </a>
</div> </div>
@@ -270,10 +273,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
</div> </div>
): ""} ): ""}
<br/>
{/* Editing Tools */} {/* Editing Tools */}
<div style={{ marginTop: "3px" }}>
<span id={"sponsorTimeDeleteButton" + this.idSuffix} <span id={"sponsorTimeDeleteButton" + this.idSuffix}
className="sponsorTimeEditButton" className="sponsorTimeEditButton"
onClick={this.deleteTime.bind(this)}> onClick={this.deleteTime.bind(this)}>
@@ -313,6 +315,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
</span> </span>
): ""} ): ""}
</div> </div>
</div>
); );
} }
@@ -398,7 +402,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
checkToShowFullVideoWarning(): void { checkToShowFullVideoWarning(): void {
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
const segmentDuration = sponsorTime.segment[1] - sponsorTime.segment[0]; const segmentDuration = sponsorTime.segment[1] - sponsorTime.segment[0];
const videoPercentage = segmentDuration / this.props.contentContainer().v.duration; const videoPercentage = segmentDuration / getVideoDuration();
if (videoPercentage > 0.6 && !this.fullVideoWarningShown if (videoPercentage > 0.6 && !this.fullVideoWarningShown
&& (sponsorTime.category === "sponsor" || sponsorTime.category === "selfpromo" || sponsorTime.category === "chooseACategory")) { && (sponsorTime.category === "sponsor" || sponsorTime.category === "selfpromo" || sponsorTime.category === "chooseACategory")) {
@@ -550,7 +554,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
setTimeToEnd(): void { setTimeToEnd(): void {
this.setTimeTo(1, this.props.contentContainer().v.duration); this.setTimeTo(1, getVideoDuration());
} }
/** /**
@@ -637,7 +641,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
sponsorTimesSubmitting[this.props.index].segment[0] = startTime; sponsorTimesSubmitting[this.props.index].segment[0] = startTime;
} }
} else if (this.state.sponsorTimeEdits[1] === null && category === "outro" && !sponsorTimesSubmitting[this.props.index].segment[1]) { } else if (this.state.sponsorTimeEdits[1] === null && category === "outro" && !sponsorTimesSubmitting[this.props.index].segment[1]) {
sponsorTimesSubmitting[this.props.index].segment[1] = this.props.contentContainer().v.duration; sponsorTimesSubmitting[this.props.index].segment[1] = getVideoDuration();
this.props.contentContainer().updateEditButtonsOnPlayer(); this.props.contentContainer().updateEditButtonsOnPlayer();
} }
@@ -680,7 +684,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
const endTime = sponsorTimes[index].segment[1]; const endTime = sponsorTimes[index].segment[1];
// If segment starts at 0:00, start playback at the end of the segment // If segment starts at 0:00, start playback at the end of the segment
const skipTime = (startTime === 0 || skipToEndTime) ? endTime : (startTime - (seekTime * this.props.contentContainer().v.playbackRate)); const skipTime = (startTime === 0 || skipToEndTime) ? endTime : (startTime - (seekTime * getVideo().playbackRate));
this.props.contentContainer().previewTime(skipTime, !skipToEndTime); this.props.contentContainer().previewTime(skipTime, !skipToEndTime);
} }

View File

@@ -9,12 +9,13 @@ import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
import SponsorTimeEditComponent from "./SponsorTimeEditComponent"; import SponsorTimeEditComponent from "./SponsorTimeEditComponent";
import { getGuidelineInfo } from "../utils/constants"; import { getGuidelineInfo } from "../utils/constants";
import { exportTimes } from "../utils/exporter"; import { exportTimes } from "../utils/exporter";
import { getVideo, isCurrentTimeWrong } from "../../maze-utils/src/video";
export interface SubmissionNoticeProps { export interface SubmissionNoticeProps {
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
callback: () => unknown; callback: () => Promise<boolean>;
closeListener: () => void; closeListener: () => void;
} }
@@ -66,9 +67,16 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.forceUpdate(); this.forceUpdate();
}); });
this.videoObserver.observe(this.contentContainer().v, { this.videoObserver.observe(getVideo(), {
attributes: true attributes: true
}); });
// Prevent zooming while changing times
document.getElementById("sponsorSkipNoticeMiddleRow" + this.state.idSuffix).addEventListener('wheel', function (event) {
if (event.ctrlKey) {
event.preventDefault();
}
}, {passive: false});
} }
componentWillUnmount(): void { componentWillUnmount(): void {
@@ -100,7 +108,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
onClick={() => this.sortSegments()} onClick={() => this.sortSegments()}
title={chrome.i18n.getMessage("sortSegments")} title={chrome.i18n.getMessage("sortSegments")}
key="sortButton" key="sortButton"
src={chrome.extension.getURL("icons/sort.svg")}> src={chrome.runtime.getURL("icons/sort.svg")}>
</img>; </img>;
const exportButton = const exportButton =
<img id={"sponsorSkipExportButton" + this.state.idSuffix} <img id={"sponsorSkipExportButton" + this.state.idSuffix}
@@ -108,7 +116,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
onClick={() => this.exportSegments()} onClick={() => this.exportSegments()}
title={chrome.i18n.getMessage("exportSegments")} title={chrome.i18n.getMessage("exportSegments")}
key="exportButton" key="exportButton"
src={chrome.extension.getURL("icons/export.svg")}> src={chrome.runtime.getURL("icons/export.svg")}>
</img>; </img>;
return ( return (
<NoticeComponent noticeTitle={this.state.noticeTitle} <NoticeComponent noticeTitle={this.state.noticeTitle}
@@ -124,7 +132,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
{/* Sponsor Time List */} {/* Sponsor Time List */}
<tr id={"sponsorSkipNoticeMiddleRow" + this.state.idSuffix} <tr id={"sponsorSkipNoticeMiddleRow" + this.state.idSuffix}
className="sponsorTimeMessagesRow" className="sponsorTimeMessagesRow"
style={{maxHeight: (this.contentContainer().v.offsetHeight - 200) + "px"}} style={{maxHeight: (getVideo()?.offsetHeight - 200) + "px"}}
onMouseDown={(e) => e.stopPropagation()}> onMouseDown={(e) => e.stopPropagation()}>
<td style={{width: "100%"}}> <td style={{width: "100%"}}>
{this.getSponsorTimeMessages()} {this.getSponsorTimeMessages()}
@@ -208,6 +216,11 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
submit(): void { submit(): void {
if (isCurrentTimeWrong()) {
alert(chrome.i18n.getMessage("submissionFailedServerSideAds"));
return;
}
// save all items // save all items
for (const ref of this.timeEditRefs) { for (const ref of this.timeEditRefs) {
ref.current.saveEditTimes(); ref.current.saveEditTimes();
@@ -232,10 +245,12 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
} }
this.props.callback(); this.props.callback().then((success) => {
if (success) {
this.cancel(); this.cancel();
} }
});
}
sortSegments(): void { sortSegments(): void {
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting; let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
@@ -274,7 +289,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
categoryChangeListener(index: number, category: Category): void { categoryChangeListener(index: number, category: Category): void {
const dialogWidth = this.noticeRef?.current?.getElement()?.current?.offsetWidth; const dialogWidth = this.noticeRef?.current?.getElement()?.current?.offsetWidth;
if (category !== "chooseACategory" && Config.config.showCategoryGuidelines if (category !== "chooseACategory" && Config.config.showCategoryGuidelines
&& this.contentContainer().v.offsetWidth > dialogWidth * 2) { && getVideo().offsetWidth > dialogWidth * 2) {
const options = { const options = {
title: chrome.i18n.getMessage(`category_${category}`), title: chrome.i18n.getMessage(`category_${category}`),
textBoxes: getGuidelineInfo(category), textBoxes: getGuidelineInfo(category),

View File

@@ -158,7 +158,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
}); });
} }
Config.forceLocalUpdate("categorySelections"); Config.forceSyncUpdate("categorySelections");
} }
getCategorySkipOptions(): JSX.Element[] { getCategorySkipOptions(): JSX.Element[] {

View File

@@ -29,6 +29,7 @@ interface SBConfig {
trackViewCount: boolean; trackViewCount: boolean;
trackViewCountInPrivate: boolean; trackViewCountInPrivate: boolean;
trackDownvotes: boolean; trackDownvotes: boolean;
trackDownvotesInPrivate: boolean;
dontShowNotice: boolean; dontShowNotice: boolean;
noticeVisibilityMode: NoticeVisbilityMode; noticeVisibilityMode: NoticeVisbilityMode;
hideVideoPlayerControls: boolean; hideVideoPlayerControls: boolean;
@@ -139,7 +140,7 @@ interface SBStorage {
downvotedSegments: Record<VideoID & HashedValue, VideoDownvotes>; downvotedSegments: Record<VideoID & HashedValue, VideoDownvotes>;
navigationApiAvailable: boolean; navigationApiAvailable: boolean;
// Used when sync storage disbaled // Used when sync storage disabled
alreadyInstalled: boolean; alreadyInstalled: boolean;
/* Contains unsubmitted segments that the user has created. */ /* Contains unsubmitted segments that the user has created. */
@@ -290,6 +291,7 @@ const syncDefaults = {
trackViewCount: true, trackViewCount: true,
trackViewCountInPrivate: true, trackViewCountInPrivate: true,
trackDownvotes: true, trackDownvotes: true,
trackDownvotesInPrivate: false,
dontShowNotice: false, dontShowNotice: false,
noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip, noticeVisibilityMode: NoticeVisbilityMode.FadedForAutoSkip,
hideVideoPlayerControls: false, hideVideoPlayerControls: false,
@@ -476,3 +478,25 @@ const localDefaults = {
const Config = new ConfigClass(syncDefaults, localDefaults, migrateOldSyncFormats); const Config = new ConfigClass(syncDefaults, localDefaults, migrateOldSyncFormats);
export default Config; export default Config;
export function generateDebugDetails(): string {
// Build output debug information object
const output = {
debug: {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
extensionVersion: chrome.runtime.getManifest().version
},
config: JSON.parse(JSON.stringify(Config.cachedSyncConfig)) // Deep clone config object
};
// Sanitise sensitive user config values
delete output.config.userID;
output.config.serverAddress = (output.config.serverAddress === CompileConfig.serverAddress)
? "Default server address" : "Custom server address";
output.config.invidiousInstances = output.config.invidiousInstances.length;
output.config.whitelistedChannels = output.config.whitelistedChannels.length;
return JSON.stringify(output, null, 4);
}

File diff suppressed because it is too large Load Diff

15
src/globals.d.ts vendored
View File

@@ -1,19 +1,4 @@
import { SBObject } from "./config"; import { SBObject } from "./config";
declare global { declare global {
interface Window { SB: SBObject } interface Window { SB: SBObject }
// Remove this once the API becomes stable and types are shipped in @types/chrome
namespace chrome {
namespace declarativeContent {
export interface RequestContentScriptOptions {
allFrames?: boolean;
css?: string[];
instanceType?: "declarativeContent.RequestContentScript";
js?: string[];
matchAboutBlanck?: boolean;
}
export class RequestContentScript {
constructor(options: RequestContentScriptOptions);
}
}
}
} }

View File

@@ -11,9 +11,9 @@ import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceT
import { partition } from "../utils/arrayUtils"; import { partition } from "../utils/arrayUtils";
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils"; import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
import { normalizeChapterName } from "../utils/exporter"; import { normalizeChapterName } from "../utils/exporter";
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
import { findValidElement } from "../../maze-utils/src/dom"; import { findValidElement } from "../../maze-utils/src/dom";
import { addCleanupListener } from "../../maze-utils/src/cleanup"; import { addCleanupListener } from "../../maze-utils/src/cleanup";
import { isVisible } from "../utils/pageUtils";
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible'; const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
const MIN_CHAPTER_SIZE = 0.003; const MIN_CHAPTER_SIZE = 0.003;
@@ -125,34 +125,11 @@ class PreviewBar {
mouseOnSeekBar = false; mouseOnSeekBar = false;
}); });
const observer = new MutationObserver((mutations) => { seekBar.addEventListener("mousemove", (e: MouseEvent) => {
if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer) return; if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer || !chrome.runtime?.id) return;
// Only care about mutations to time tooltip let noYoutubeChapters = !!tooltipTextWrapper.querySelector(".ytp-tooltip-text.ytp-tooltip-text-no-title");
if (!mutations.some((mutation) => (mutation.target as HTMLElement).classList.contains("ytp-tooltip-text"))) { const timeInSeconds = this.decimalToTime((e.clientX - seekBar.getBoundingClientRect().x) / seekBar.clientWidth);
return;
}
const tooltipTextElements = tooltipTextWrapper.querySelectorAll(".ytp-tooltip-text");
let timeInSeconds: number | null = null;
let noYoutubeChapters = false;
for (const tooltipTextElement of tooltipTextElements) {
if (tooltipTextElement.classList.contains('ytp-tooltip-text-no-title')) noYoutubeChapters = true;
const tooltipText = tooltipTextElement.textContent;
if (tooltipText === null || tooltipText.length === 0) continue;
timeInSeconds = getFormattedTimeToSeconds(tooltipText);
if (timeInSeconds !== null) break;
}
if (timeInSeconds === null) {
originalTooltip.style.removeProperty("display");
return;
}
// Find the segment at that location, using the shortest if multiple found // Find the segment at that location, using the shortest if multiple found
const [normalSegments, chapterSegments] = const [normalSegments, chapterSegments] =
@@ -198,15 +175,6 @@ class PreviewBar {
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign; this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
} }
}); });
observer.observe(tooltipTextWrapper, {
childList: true,
subtree: true,
});
addCleanupListener(() => {
observer.disconnect();
});
} }
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void { private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
@@ -229,7 +197,6 @@ class PreviewBar {
if (this.onMobileYouTube) { if (this.onMobileYouTube) {
this.container.style.transform = "none"; this.container.style.transform = "none";
this.container.style.height = "var(--yt-progress-bar-height)";
} else if (!this.onInvidious) { } else if (!this.onInvidious) {
this.container.classList.add("sbNotInvidious"); this.container.classList.add("sbNotInvidious");
} }
@@ -258,10 +225,12 @@ class PreviewBar {
this.segments = segments ?? []; this.segments = segments ?? [];
this.videoDuration = videoDuration ?? 0; this.videoDuration = videoDuration ?? 0;
this.updatePageElements(); this.updatePageElements();
// Sometimes video duration is inaccurate, pull from accessibility info // Sometimes video duration is inaccurate, pull from accessibility info
const ariaDuration = parseInt(this.progressBar?.getAttribute('aria-valuemax')) ?? 0; const ariaDuration = parseInt(this.progressBar?.getAttribute('aria-valuemax')) ?? 0;
if (ariaDuration && Math.abs(ariaDuration - this.videoDuration) > 3) { const multipleActiveVideos = [...document.querySelectorAll("video")].filter((v) => isVisible(v)).length > 1;
if (!multipleActiveVideos && ariaDuration && Math.abs(ariaDuration - this.videoDuration) > 3) {
this.videoDuration = ariaDuration; this.videoDuration = ariaDuration;
} }
@@ -269,7 +238,7 @@ class PreviewBar {
} }
private updatePageElements(): void { private updatePageElements(): void {
const allProgressBars = document.querySelectorAll('.ytp-progress-bar') as NodeListOf<HTMLElement>; const allProgressBars = document.querySelectorAll(".ytp-progress-bar") as NodeListOf<HTMLElement>;
this.progressBar = findValidElement(allProgressBars) ?? allProgressBars?.[0]; this.progressBar = findValidElement(allProgressBars) ?? allProgressBars?.[0];
if (this.progressBar) { if (this.progressBar) {
@@ -307,6 +276,7 @@ class PreviewBar {
return (b[1] - b[0]) - (a[1] - a[0]); return (b[1] - b[0]) - (a[1] - a[0]);
}); });
for (const segment of sortedSegments) { for (const segment of sortedSegments) {
if (segment.actionType === ActionType.Chapter) continue;
const bar = this.createBar(segment); const bar = this.createBar(segment);
this.container.appendChild(bar); this.container.appendChild(bar);
@@ -346,7 +316,7 @@ class PreviewBar {
bar.style.left = this.timeToPercentage(startTime); bar.style.left = this.timeToPercentage(startTime);
if (duration > 0) { if (duration > 0) {
bar.style.right = this.timeToPercentage(this.videoDuration - endTime); bar.style.right = this.timeToRightPercentage(endTime);
} }
if (this.chapterFilter(barSegment) && segment[1] < this.videoDuration) { if (this.chapterFilter(barSegment) && segment[1] < this.videoDuration) {
bar.style.marginRight = `${this.chapterMargin}px`; bar.style.marginRight = `${this.chapterMargin}px`;
@@ -780,6 +750,7 @@ class PreviewBar {
updateChapterText(segments: SponsorTime[], submittingSegments: SponsorTime[], currentTime: number): SponsorTime[] { updateChapterText(segments: SponsorTime[], submittingSegments: SponsorTime[], currentTime: number): SponsorTime[] {
if (!Config.config.showSegmentNameInChapterBar if (!Config.config.showSegmentNameInChapterBar
|| Config.config.disableSkipping
|| ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) { || ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) {
const chaptersContainer = this.getChaptersContainer(); const chaptersContainer = this.getChaptersContainer();
if (chaptersContainer) { if (chaptersContainer) {
@@ -919,7 +890,22 @@ class PreviewBar {
return `${this.timeToDecimal(time) * 100}%` return `${this.timeToDecimal(time) * 100}%`
} }
timeToRightPercentage(time: number): string {
return `${(1 - this.timeToDecimal(time)) * 100}%`
}
timeToDecimal(time: number): number { timeToDecimal(time: number): number {
return this.decimalTimeConverter(time, true);
}
decimalToTime(decimal: number): number {
return this.decimalTimeConverter(decimal, false);
}
/**
* Decimal to time or time to decimal
*/
decimalTimeConverter(value: number, isTime: boolean): number {
if (this.originalChapterBarBlocks?.length > 1 && this.existingChapters.length === this.originalChapterBarBlocks?.length) { if (this.originalChapterBarBlocks?.length > 1 && this.existingChapters.length === this.originalChapterBarBlocks?.length) {
// Parent element to still work when display: none // Parent element to still work when display: none
const totalPixels = this.originalChapterBar.parentElement.clientWidth; const totalPixels = this.originalChapterBar.parentElement.clientWidth;
@@ -929,8 +915,9 @@ class PreviewBar {
const chapterElement = this.originalChapterBarBlocks[i]; const chapterElement = this.originalChapterBarBlocks[i];
const widthPixels = parseFloat(chapterElement.style.width.replace("px", "")); const widthPixels = parseFloat(chapterElement.style.width.replace("px", ""));
if (time >= this.existingChapters[i].segment[1]) {
const marginPixels = chapterElement.style.marginRight ? parseFloat(chapterElement.style.marginRight.replace("px", "")) : 0; const marginPixels = chapterElement.style.marginRight ? parseFloat(chapterElement.style.marginRight.replace("px", "")) : 0;
if ((isTime && value >= this.existingChapters[i].segment[1])
|| (!isTime && value >= (pixelOffset + widthPixels + marginPixels) / totalPixels)) {
pixelOffset += widthPixels + marginPixels; pixelOffset += widthPixels + marginPixels;
lastCheckedChapter = i; lastCheckedChapter = i;
} else { } else {
@@ -944,13 +931,22 @@ class PreviewBar {
const latestWidth = parseFloat(this.originalChapterBarBlocks[lastCheckedChapter + 1].style.width.replace("px", "")); const latestWidth = parseFloat(this.originalChapterBarBlocks[lastCheckedChapter + 1].style.width.replace("px", ""));
const latestChapterDuration = latestChapter.segment[1] - latestChapter.segment[0]; const latestChapterDuration = latestChapter.segment[1] - latestChapter.segment[0];
const percentageInCurrentChapter = (time - latestChapter.segment[0]) / latestChapterDuration; if (isTime) {
const percentageInCurrentChapter = (value - latestChapter.segment[0]) / latestChapterDuration;
const sizeOfCurrentChapter = latestWidth / totalPixels; const sizeOfCurrentChapter = latestWidth / totalPixels;
return Math.min(1, ((pixelOffset / totalPixels) + (percentageInCurrentChapter * sizeOfCurrentChapter))); return Math.min(1, ((pixelOffset / totalPixels) + (percentageInCurrentChapter * sizeOfCurrentChapter)));
} else {
const percentageInCurrentChapter = (value * totalPixels - pixelOffset) / latestWidth;
return Math.max(0, latestChapter.segment[0] + (percentageInCurrentChapter * latestChapterDuration));
}
} }
} }
return Math.min(1, time / this.videoDuration); if (isTime) {
return Math.min(1, value / this.videoDuration);
} else {
return Math.max(0, value * this.videoDuration);
}
} }
/* /*

View File

@@ -1,7 +1,7 @@
import Config from "../config"; import Config from "../config";
import { SegmentUUID, SponsorTime } from "../types"; import { SegmentUUID, SponsorTime } from "../types";
import { getSkippingText } from "../utils/categoryUtils"; import { getSkippingText } from "../utils/categoryUtils";
import { AnimationUtils } from "../utils/animationUtils"; import { AnimationUtils } from "../../maze-utils/src/animationUtils";
import { keybindToString } from "../../maze-utils/src/config"; import { keybindToString } from "../../maze-utils/src/config";
import { isMobileControlsOpen } from "../utils/mobileUtils"; import { isMobileControlsOpen } from "../utils/mobileUtils";

View File

@@ -17,7 +17,8 @@ interface DefaultMessage {
| "isChannelWhitelisted" | "isChannelWhitelisted"
| "submitTimes" | "submitTimes"
| "refreshSegments" | "refreshSegments"
| "closePopup"; | "closePopup"
| "getLogs";
} }
interface BoolValueMessage { interface BoolValueMessage {
@@ -103,7 +104,9 @@ export type MessageResponse =
| IsChannelWhitelistedResponse | IsChannelWhitelistedResponse
| Record<string, never> // empty object response {} | Record<string, never> // empty object response {}
| VoteResponse | VoteResponse
| ImportSegmentsResponse; | ImportSegmentsResponse
| RefreshSegmentsResponse
| LogResponse;
export interface VoteResponse { export interface VoteResponse {
successType: number; successType: number;
@@ -115,6 +118,15 @@ interface ImportSegmentsResponse {
importedSegments: SponsorTime[]; importedSegments: SponsorTime[];
} }
export interface RefreshSegmentsResponse {
hasVideo: boolean;
}
export interface LogResponse {
debug: string[];
warn: string[];
}
export interface TimeUpdateMessage { export interface TimeUpdateMessage {
message: "time"; message: "time";
time: number; time: number;

View File

@@ -1,8 +1,7 @@
import * as React from "react"; import * as React from "react";
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import Config from "./config"; import Config, { generateDebugDetails } from "./config";
import * as CompileConfig from "../config.json";
import * as invidiousList from "../ci/invidiouslist.json"; import * as invidiousList from "../ci/invidiouslist.json";
// Make the config public for debugging purposes // Make the config public for debugging purposes
@@ -286,7 +285,7 @@ async function init() {
break; break;
case "resetToDefault": case "resetToDefault":
Config.resetToDefault(); Config.resetToDefault();
window.location.reload(); setTimeout(() => window.location.reload(), 200);
break; break;
} }
}); });
@@ -632,8 +631,7 @@ async function setTextOption(option: string, element: HTMLElement, value: string
await invidiousOnClick(checkbox, "supportInvidious"); await invidiousOnClick(checkbox, "supportInvidious");
} }
window.location.reload(); setTimeout(() => window.location.reload(), 200);
} catch (e) { } catch (e) {
alert(chrome.i18n.getMessage("incorrectlyFormattedOptions")); alert(chrome.i18n.getMessage("incorrectlyFormattedOptions"));
} }
@@ -699,26 +697,8 @@ function validateServerAddress(input: string): string {
} }
function copyDebugOutputToClipboard() { function copyDebugOutputToClipboard() {
// Build output debug information object
const output = {
debug: {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
extensionVersion: chrome.runtime.getManifest().version
},
config: JSON.parse(JSON.stringify(Config.cachedSyncConfig)) // Deep clone config object
};
// Sanitise sensitive user config values
delete output.config.userID;
output.config.serverAddress = (output.config.serverAddress === CompileConfig.serverAddress)
? "Default server address" : "Custom server address";
output.config.invidiousInstances = output.config.invidiousInstances.length;
output.config.whitelistedChannels = output.config.whitelistedChannels.length;
// Copy object to clipboard // Copy object to clipboard
navigator.clipboard.writeText(JSON.stringify(output, null, 4)) navigator.clipboard.writeText(generateDebugDetails())
.then(() => { .then(() => {
alert(chrome.i18n.getMessage("copyDebugInformationComplete")); alert(chrome.i18n.getMessage("copyDebugInformationComplete"));
}) })

View File

@@ -1,4 +1,4 @@
import Config from "./config"; import Config, { generateDebugDetails } from "./config";
import Utils from "./utils"; import Utils from "./utils";
import { import {
@@ -12,14 +12,16 @@ import {
GetChannelIDResponse, GetChannelIDResponse,
IsChannelWhitelistedResponse, IsChannelWhitelistedResponse,
IsInfoFoundMessageResponse, IsInfoFoundMessageResponse,
LogResponse,
Message, Message,
MessageResponse, MessageResponse,
PopupMessage, PopupMessage,
RefreshSegmentsResponse,
SponsorStartResponse, SponsorStartResponse,
VoteResponse, VoteResponse,
} from "./messageTypes"; } from "./messageTypes";
import { showDonationLink } from "./utils/configUtils"; import { showDonationLink } from "./utils/configUtils";
import { AnimationUtils } from "./utils/animationUtils"; import { AnimationUtils } from "../maze-utils/src/animationUtils";
import { shortCategoryName } from "./utils/categoryUtils"; import { shortCategoryName } from "./utils/categoryUtils";
import { localizeHtmlPage } from "../maze-utils/src/setup"; import { localizeHtmlPage } from "../maze-utils/src/setup";
import { exportTimes } from "./utils/exporter"; import { exportTimes } from "./utils/exporter";
@@ -183,7 +185,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
"exportSegmentsButton", "exportSegmentsButton",
"importSegmentsMenu", "importSegmentsMenu",
"importSegmentsText", "importSegmentsText",
"importSegmentsSubmit" "importSegmentsSubmit",
"debugLogs"
].forEach(id => PageElements[id] = document.getElementById(id)); ].forEach(id => PageElements[id] = document.getElementById(id));
@@ -254,6 +257,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.helpButton.addEventListener("click", openHelp); PageElements.helpButton.addEventListener("click", openHelp);
PageElements.refreshSegmentsButton.addEventListener("click", refreshSegments); PageElements.refreshSegmentsButton.addEventListener("click", refreshSegments);
PageElements.sbPopupIconCopyUserID.addEventListener("click", async () => copyToClipboard(await getHash(Config.config.userID))); PageElements.sbPopupIconCopyUserID.addEventListener("click", async () => copyToClipboard(await getHash(Config.config.userID)));
PageElements.debugLogs.addEventListener("click", copyDebgLogs);
// Forward click events // Forward click events
if (window !== window.top) { if (window !== window.top) {
@@ -302,7 +306,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.showNoticeAgain.style.display = "unset"; PageElements.showNoticeAgain.style.display = "unset";
} }
const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions"]; const values = ["userName", "viewCount", "minutesSaved", "vip", "permissions", "segmentCount"];
asyncRequestToServer("GET", "/api/userInfo", { asyncRequestToServer("GET", "/api/userInfo", {
publicUserID: await getHash(Config.config.userID), publicUserID: await getHash(Config.config.userID),
@@ -335,16 +339,18 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.sponsorTimesOthersTimeSavedDisplay.innerText = getFormattedHours(minutesSaved); PageElements.sponsorTimesOthersTimeSavedDisplay.innerText = getFormattedHours(minutesSaved);
} }
//get the amount of times this user has contributed and display it to thank them
PageElements.sponsorTimesContributionsDisplay.innerText = Math.max(Config.config.sponsorTimesContributed ?? 0, userInfo.segmentCount).toLocaleString();
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
Config.config.isVip = userInfo.vip; Config.config.isVip = userInfo.vip;
Config.config.permissions = userInfo.permissions; Config.config.permissions = userInfo.permissions;
} }
}); });
//get the amount of times this user has contributed and display it to thank them
if (Config.config.sponsorTimesContributed != undefined) {
PageElements.sponsorTimesContributionsDisplay.innerText = Config.config.sponsorTimesContributed.toLocaleString();
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
}
//get the amount of times this user has skipped a sponsor //get the amount of times this user has skipped a sponsor
if (Config.config.skipCount != undefined) { if (Config.config.skipCount != undefined) {
@@ -459,14 +465,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
stopLoadingAnimation = null; stopLoadingAnimation = null;
} }
if (chrome.runtime.lastError) { if (chrome.runtime.lastError || request == undefined || request.found == undefined) {
//This page doesn't have the injected content script, or at least not yet //This page doesn't have the injected content script, or at least not yet
// Or if the request is empty, meaning the current page is not YouTube or a video page
displayNoVideo(); displayNoVideo();
return; return;
} }
//if request is undefined, then the page currently being browsed is not YouTube
if (request != undefined) {
//remove loading text //remove loading text
PageElements.mainControls.style.display = "block"; PageElements.mainControls.style.display = "block";
if (request.onMobileYouTube) PageElements.mainControls.classList.add("hidden"); if (request.onMobileYouTube) PageElements.mainControls.classList.add("hidden");
@@ -490,7 +495,6 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
PageElements.issueReporterImportExport.classList.remove("hidden"); PageElements.issueReporterImportExport.classList.remove("hidden");
} }
}
//see if whitelist button should be swapped //see if whitelist button should be swapped
const response = await sendTabMessageAsync({ message: 'isChannelWhitelisted' }) as IsChannelWhitelistedResponse; const response = await sendTabMessageAsync({ message: 'isChannelWhitelisted' }) as IsChannelWhitelistedResponse;
@@ -562,7 +566,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
return true; return true;
} }
}) })
.sort((a, b) => a.segment[1] - b.segment[1]) .sort((a, b) => b.segment[1] - a.segment[1])
.sort((a, b) => a.segment[0] - b.segment[0]); .sort((a, b) => a.segment[0] - b.segment[0]);
//add them as buttons to the issue reporting container //add them as buttons to the issue reporting container
@@ -980,9 +984,17 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
stopLoadingAnimation = AnimationUtils.applyLoadingAnimation(PageElements.refreshSegmentsButton, 0.3); stopLoadingAnimation = AnimationUtils.applyLoadingAnimation(PageElements.refreshSegmentsButton, 0.3);
} }
function refreshSegments() { async function refreshSegments() {
startLoadingAnimation(); startLoadingAnimation();
sendTabMessage({ message: 'refreshSegments' }); const response = await sendTabMessageAsync({ message: 'refreshSegments' }) as RefreshSegmentsResponse;
if (response == null || !response.hasVideo) {
if (stopLoadingAnimation != null) {
stopLoadingAnimation();
stopLoadingAnimation = null;
}
displayNoVideo();
}
} }
function skipSegment(actionType: ActionType, UUID: SegmentUUID, element?: HTMLElement): void { function skipSegment(actionType: ActionType, UUID: SegmentUUID, element?: HTMLElement): void {
@@ -1134,6 +1146,12 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
} }
} }
function copyDebgLogs() {
sendTabMessage({ message: "getLogs" }, (logs: LogResponse) => {
copyToClipboard(`${generateDebugDetails()}\n\nWarn:\n${logs.warn.join("\n")}\n\nDebug:\n${logs.debug.join("\n")}`);
});
}
function onMessage(msg: PopupMessage) { function onMessage(msg: PopupMessage) {
switch (msg.message) { switch (msg.message) {
case "time": case "time":

View File

@@ -43,9 +43,15 @@ export class CategoryPill {
} }
private async attachToPageInternal(): Promise<void> { private async attachToPageInternal(): Promise<void> {
const referenceNode = let referenceNode =
await waitFor(() => getYouTubeTitleNode()); await waitFor(() => getYouTubeTitleNode());
// Experimental YouTube layout with description on right
const isOnDescriptionOnRightLayout = document.querySelector("#title #description");
if (isOnDescriptionOnRightLayout) {
referenceNode = referenceNode.parentElement;
}
if (referenceNode && !referenceNode.contains(this.container)) { if (referenceNode && !referenceNode.contains(this.container)) {
if (!this.container) { if (!this.container) {
this.container = document.createElement('span'); this.container = document.createElement('span');
@@ -91,9 +97,11 @@ export class CategoryPill {
parent.appendChild(this.container); parent.appendChild(this.container);
referenceNode.prepend(parent); referenceNode.prepend(parent);
if (!isOnDescriptionOnRightLayout) {
referenceNode.style.display = "flex"; referenceNode.style.display = "flex";
} }
} }
}
close(): void { close(): void {
this.root.unmount(); this.root.unmount();

View File

@@ -8,6 +8,7 @@ const utils = new Utils();
import { ContentContainer } from "../types"; import { ContentContainer } from "../types";
import NoticeTextSelectionComponent from "../components/NoticeTextSectionComponent"; import NoticeTextSelectionComponent from "../components/NoticeTextSectionComponent";
import { ButtonListener } from "../../maze-utils/src/components/component-types"; import { ButtonListener } from "../../maze-utils/src/components/component-types";
import { getVideo } from "../../maze-utils/src/video";
export interface TextBox { export interface TextBox {
icon: string; icon: string;
@@ -75,7 +76,7 @@ export default class GenericNotice {
{options.textBoxes?.length > 0 ? {options.textBoxes?.length > 0 ?
<tr id={"sponsorSkipNoticeMiddleRow" + this.idSuffix} <tr id={"sponsorSkipNoticeMiddleRow" + this.idSuffix}
className="sponsorTimeMessagesRow" className="sponsorTimeMessagesRow"
style={{maxHeight: this.contentContainer ? (this.contentContainer().v.offsetHeight - 200) + "px" : null}}> style={{maxHeight: getVideo() ? (getVideo().offsetHeight - 200) + "px" : null}}>
<td style={{width: "100%"}}> <td style={{width: "100%"}}>
{this.getMessageBoxes(this.idSuffix, options.textBoxes)} {this.getMessageBoxes(this.idSuffix, options.textBoxes)}
</td> </td>

View File

@@ -59,7 +59,7 @@ export class RectangleTooltip {
className="sponsorBlockRectangleTooltip" > className="sponsorBlockRectangleTooltip" >
<div> <div>
<img className="sponsorSkipLogo sponsorSkipObject" <img className="sponsorSkipLogo sponsorSkipObject"
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}> src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
</img> </img>
<span className="sponsorSkipObject"> <span className="sponsorSkipObject">
{this.text + (props.link ? ". " : "")} {this.text + (props.link ? ". " : "")}

View File

@@ -11,7 +11,7 @@ class SubmissionNotice {
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: () => unknown; contentContainer: () => unknown;
callback: () => unknown; callback: () => Promise<boolean>;
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>; noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
@@ -19,7 +19,7 @@ class SubmissionNotice {
root: Root; root: Root;
constructor(contentContainer: ContentContainer, callback: () => unknown) { constructor(contentContainer: ContentContainer, callback: () => Promise<boolean>) {
this.noticeRef = React.createRef(); this.noticeRef = React.createRef();
this.contentContainer = contentContainer; this.contentContainer = contentContainer;

View File

@@ -10,7 +10,6 @@ export interface ContentContainer {
sponsorTimes: SponsorTime[]; sponsorTimes: SponsorTime[];
sponsorTimesSubmitting: SponsorTime[]; sponsorTimesSubmitting: SponsorTime[];
skipNotices: SkipNotice[]; skipNotices: SkipNotice[];
v: HTMLVideoElement;
sponsorVideoID; sponsorVideoID;
reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void; reskipSponsorTime: (segment: SponsorTime, forceSeek?: boolean) => void;
updatePreviewBar: () => void; updatePreviewBar: () => void;
@@ -57,7 +56,13 @@ export enum ActionType {
Poi = "poi" Poi = "poi"
} }
export const ActionTypes = [ActionType.Skip, ActionType.Mute]; export const ActionTypes = [
ActionType.Skip,
ActionType.Mute,
ActionType.Chapter,
ActionType.Full,
ActionType.Poi
];
export type SegmentUUID = string & { __segmentUUIDBrand: unknown }; export type SegmentUUID = string & { __segmentUUIDBrand: unknown };
export type Category = string & { __categoryBrand: unknown }; export type Category = string & { __categoryBrand: unknown };

View File

@@ -2,7 +2,7 @@ import Config, { VideoDownvotes } from "./config";
import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types"; import { CategorySelection, SponsorTime, BackgroundScriptContainer, Registration, VideoID, SponsorHideType, CategorySkipOption } from "./types";
import { getHash, HashedValue } from "../maze-utils/src/hash"; import { getHash, HashedValue } from "../maze-utils/src/hash";
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src"; import { waitFor } from "../maze-utils/src";
import { findValidElementFromSelector } from "../maze-utils/src/dom"; import { findValidElementFromSelector } from "../maze-utils/src/dom";
import { isSafari } from "../maze-utils/src/config"; import { isSafari } from "../maze-utils/src/config";
@@ -46,10 +46,7 @@ export default class Utils {
*/ */
setupExtraSitePermissions(callback: (granted: boolean) => void): void { setupExtraSitePermissions(callback: (granted: boolean) => void): void {
const permissions = []; const permissions = [];
if (!isFirefoxOrSafari()) { if (isSafari()) {
permissions.push("declarativeContent");
}
if (!isFirefoxOrSafari() || isSafari()) {
permissions.push("webNavigation"); permissions.push("webNavigation");
} }
@@ -67,6 +64,17 @@ export default class Utils {
}); });
} }
getExtraSiteRegistration(): Registration {
return {
message: "registerContentScript",
id: "invidious",
allFrames: true,
js: this.js,
css: this.css,
matches: this.getPermissionRegex()
};
}
/** /**
* Registers the content scripts for the extra sites. * Registers the content scripts for the extra sites.
* Will use a different method depending on the browser. * Will use a different method depending on the browser.
@@ -75,14 +83,7 @@ export default class Utils {
* For now, it is just SB.config.invidiousInstances. * For now, it is just SB.config.invidiousInstances.
*/ */
setupExtraSiteContentScripts(): void { setupExtraSiteContentScripts(): void {
const registration: Registration = { const registration = this.getExtraSiteRegistration();
message: "registerContentScript",
id: "invidious",
allFrames: true,
js: this.js,
css: this.css,
matches: this.getPermissionRegex()
};
if (this.backgroundScriptContainer) { if (this.backgroundScriptContainer) {
this.backgroundScriptContainer.registerFirefoxContentScript(registration); this.backgroundScriptContainer.registerFirefoxContentScript(registration);
@@ -106,11 +107,6 @@ export default class Utils {
}); });
} }
if (!isFirefoxOrSafari() && chrome.declarativeContent) {
// Only if we have permission
chrome.declarativeContent.onPageChanged.removeRules(["invidious"]);
}
chrome.permissions.remove({ chrome.permissions.remove({
origins: this.getPermissionRegex() origins: this.getPermissionRegex()
}); });
@@ -135,8 +131,10 @@ export default class Utils {
containsInvidiousPermission(): Promise<boolean> { containsInvidiousPermission(): Promise<boolean> {
return new Promise((resolve) => { return new Promise((resolve) => {
let permissions = ["declarativeContent"]; const permissions = [];
if (isFirefoxOrSafari()) permissions = []; if (isSafari()) {
permissions.push("webNavigation");
}
chrome.permissions.contains({ chrome.permissions.contains({
origins: this.getPermissionRegex(), origins: this.getPermissionRegex(),
@@ -281,7 +279,8 @@ export default class Utils {
} }
async addHiddenSegment(videoID: VideoID, segmentUUID: string, hidden: SponsorHideType) { async addHiddenSegment(videoID: VideoID, segmentUUID: string, hidden: SponsorHideType) {
if (chrome.extension.inIncognitoContext || !Config.config.trackDownvotes) return; if ((chrome.extension.inIncognitoContext && !Config.config.trackDownvotesInPrivate)
|| !Config.config.trackDownvotes) return;
const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue; const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
const UUIDHash = await getHash(segmentUUID, 1); const UUIDHash = await getHash(segmentUUID, 1);

View File

@@ -1,78 +0,0 @@
/**
* Starts a spinning animation and returns a function to be called when it should be stopped
* The callback will be called when the animation is finished
* It waits until a full rotation is complete
*/
function applyLoadingAnimation(element: HTMLElement, time: number, callback?: () => void): () => Promise<void> {
element.style.animation = `rotate ${time}s 0s infinite`;
return async () => new Promise((resolve) => {
// Make the animation finite
element.style.animation = `rotate ${time}s`;
// When the animation is over, hide the button
const animationEndListener = () => {
if (callback) callback();
element.style.animation = "none";
element.removeEventListener("animationend", animationEndListener);
resolve();
};
element.addEventListener("animationend", animationEndListener);
});
}
function setupCustomHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): { hide: () => void; show: () => void } {
if (enabled) element.classList.add("autoHiding");
element.classList.add("sbhidden");
element.classList.add("animationDone");
if (!rightSlide) element.classList.add("autoHideLeft");
let mouseEntered = false;
return {
hide: () => {
mouseEntered = false;
if (element.classList.contains("autoHiding")) {
element.classList.add("sbhidden");
}
},
show: () => {
mouseEntered = true;
element.classList.remove("animationDone");
// Wait for next event loop
setTimeout(() => {
if (mouseEntered) element.classList.remove("sbhidden")
}, 10);
}
};
}
function setupAutoHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): void {
const { hide, show } = this.setupCustomHideAnimation(element, container, enabled, rightSlide);
container.addEventListener("mouseleave", () => hide());
container.addEventListener("mouseenter", () => show());
}
function enableAutoHideAnimation(element: Element): void {
element.classList.add("autoHiding");
element.classList.add("sbhidden");
}
function disableAutoHideAnimation(element: Element): void {
element.classList.remove("autoHiding");
element.classList.remove("sbhidden");
}
export const AnimationUtils = {
applyLoadingAnimation,
setupAutoHideAnimation,
setupCustomHideAnimation,
enableAutoHideAnimation,
disableAutoHideAnimation
};

View File

@@ -53,14 +53,14 @@ export function importTimes(data: string, videoDuration: number): SponsorTime[]
titleRight = removeIf(split2[split2.length - 1], specialCharMatchers) titleRight = removeIf(split2[split2.length - 1], specialCharMatchers)
const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight; const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight;
if (title) {
const determinedCategory = chapterNames.find(c => c.names.includes(title))?.code as Category; const determinedCategory = chapterNames.find(c => c.names.includes(title))?.code as Category;
const category = title ? (determinedCategory ?? ("chapter" as Category)) : "chooseACategory" as Category;
const segment: SponsorTime = { const segment: SponsorTime = {
segment: [startTime, getFormattedTimeToSeconds(match[1])], segment: [startTime, getFormattedTimeToSeconds(match[1])],
category: determinedCategory ?? ("chapter" as Category), category,
actionType: determinedCategory ? ActionType.Skip : ActionType.Chapter, actionType: category === "chapter" ? ActionType.Chapter : ActionType.Skip,
description: title, description: category === "chapter" ? title : null,
source: SponsorSourceType.Local, source: SponsorSourceType.Local,
UUID: generateUserID() as SegmentUUID UUID: generateUserID() as SegmentUUID
}; };
@@ -73,7 +73,6 @@ export function importTimes(data: string, videoDuration: number): SponsorTime[]
} }
} }
} }
}
if (result.length > 0 && result[result.length - 1].segment[1] === null) { if (result.length > 0 && result[result.length - 1].segment[1] === null) {
result[result.length - 1].segment[1] = videoDuration; result[result.length - 1].segment[1] = videoDuration;

View File

@@ -1,12 +1,22 @@
window["SBLogs"] = { if (typeof (window) !== "undefined") {
window["SBLogs"] = {
debug: [], debug: [],
warn: [] warn: []
}; };
}
export function logDebug(message: string) { export function logDebug(message: string) {
if (typeof (window) !== "undefined") {
window["SBLogs"].debug.push(`[${new Date().toISOString()}] ${message}`); window["SBLogs"].debug.push(`[${new Date().toISOString()}] ${message}`);
} else {
console.log(`[${new Date().toISOString()}] ${message}`)
}
} }
export function logWarn(message: string) { export function logWarn(message: string) {
if (typeof (window) !== "undefined") {
window["SBLogs"].warn.push(`[${new Date().toISOString()}] ${message}`); window["SBLogs"].warn.push(`[${new Date().toISOString()}] ${message}`);
} else {
console.warn(`[${new Date().toISOString()}] ${message}`)
}
} }

View File

@@ -55,7 +55,7 @@ export function getHashParams(): Record<string, unknown> {
export function getExistingChapters(currentVideoID: VideoID, duration: number): SponsorTime[] { export function getExistingChapters(currentVideoID: VideoID, duration: number): SponsorTime[] {
const chaptersBox = document.querySelector("ytd-macro-markers-list-renderer"); const chaptersBox = document.querySelector("ytd-macro-markers-list-renderer");
const title = document.querySelector("[target-id=engagement-panel-macro-markers-auto-chapters] #title-text"); const title = chaptersBox?.closest("ytd-engagement-panel-section-list-renderer")?.querySelector("#title-text.ytd-engagement-panel-title-header-renderer");
if (title?.textContent?.includes("Key moment")) return []; if (title?.textContent?.includes("Key moment")) return [];
const chapters: SponsorTime[] = []; const chapters: SponsorTime[] = [];

View File

@@ -9,8 +9,8 @@ import { FetchResponse, sendRequestToCustomServer } from "../../maze-utils/src/b
* @param address The address to add to the SponsorBlock server address * @param address The address to add to the SponsorBlock server address
* @param callback * @param callback
*/ */
export function asyncRequestToCustomServer(type: string, url: string, data = {}): Promise<FetchResponse> { export function asyncRequestToCustomServer(type: string, url: string, data = {}, headers = {}): Promise<FetchResponse> {
return sendRequestToCustomServer(type, url, data); return sendRequestToCustomServer(type, url, data, headers);
} }
/** /**
@@ -20,10 +20,10 @@ export function asyncRequestToCustomServer(type: string, url: string, data = {})
* @param address The address to add to the SponsorBlock server address * @param address The address to add to the SponsorBlock server address
* @param callback * @param callback
*/ */
export async function asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> { export async function asyncRequestToServer(type: string, address: string, data = {}, headers = {}): Promise<FetchResponse> {
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress; const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
return await (asyncRequestToCustomServer(type, serverAddress + address, data)); return await (asyncRequestToCustomServer(type, serverAddress + address, data, headers));
} }
/** /**

104
src/utils/segmentData.ts Normal file
View File

@@ -0,0 +1,104 @@
import { DataCache } from "../../maze-utils/src/cache";
import { getHash, HashedValue } from "../../maze-utils/src/hash";
import Config from "../config";
import * as CompileConfig from "../../config.json";
import { ActionType, ActionTypes, SponsorSourceType, SponsorTime, VideoID } from "../types";
import { getHashParams } from "./pageUtils";
import { asyncRequestToServer } from "./requests";
const segmentDataCache = new DataCache<VideoID, SegmentResponse>(() => {
return {
segments: null,
status: 200
};
}, 5);
const pendingList: Record<VideoID, Promise<SegmentResponse>> = {};
export interface SegmentResponse {
segments: SponsorTime[] | null;
status: number;
}
export async function getSegmentsForVideo(videoID: VideoID, ignoreCache: boolean): Promise<SegmentResponse> {
if (!ignoreCache) {
const cachedData = segmentDataCache.getFromCache(videoID);
if (cachedData) {
segmentDataCache.cacheUsed(videoID);
return cachedData;
}
}
if (pendingList[videoID]) {
return await pendingList[videoID];
}
const pendingData = fetchSegmentsForVideo(videoID);
pendingList[videoID] = pendingData;
const result = await pendingData;
delete pendingList[videoID];
return result;
}
async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse> {
const categories: string[] = Config.config.categorySelections.map((category) => category.name);
const extraRequestData: Record<string, unknown> = {};
const hashParams = getHashParams();
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
categories: CompileConfig.categoryList,
actionTypes: ActionTypes,
...extraRequestData
}, {
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
});
if (response.ok) {
const enabledActionTypes = getEnabledActionTypes();
const receivedSegments: SponsorTime[] = JSON.parse(response.responseText)
?.filter((video) => video.videoID === videoID)
?.map((video) => video.segments)?.[0]
?.filter((segment) => enabledActionTypes.includes(segment.actionType) && categories.includes(segment.category))
?.map((segment) => ({
...segment,
source: SponsorSourceType.Server
}))
?.sort((a, b) => a.segment[0] - b.segment[0]);
if (receivedSegments && receivedSegments.length) {
const result = {
segments: receivedSegments,
status: response.status
};
segmentDataCache.setupCache(videoID).segments = result.segments;
return result;
} else {
// Setup with null data
segmentDataCache.setupCache(videoID);
}
}
return {
segments: null,
status: response.status
};
}
function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
const actionTypes = [ActionType.Skip, ActionType.Poi, ActionType.Chapter];
if (Config.config.muteSegments) {
actionTypes.push(ActionType.Mute);
}
if (Config.config.fullVideoSegments || forceFullVideo) {
actionTypes.push(ActionType.Full);
}
return actionTypes;
}

View File

@@ -1,10 +1,15 @@
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video"; import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
import Config from "../config"; import Config from "../config";
import { getVideoLabel } from "./videoLabels"; import { getVideoLabel } from "./videoLabels";
import { setThumbnailListener } from "../../maze-utils/src/thumbnailManagement"; import { getThumbnailSelector, setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
import { VideoID } from "../types";
import { getSegmentsForVideo } from "./segmentData";
export async function labelThumbnails(thumbnails: HTMLImageElement[]): Promise<void> { export async function handleThumbnails(thumbnails: HTMLImageElement[]): Promise<void> {
await Promise.all(thumbnails.map((t) => labelThumbnail(t))); await Promise.all(thumbnails.map((t) => {
labelThumbnail(t);
setupThumbnailHover(t);
}));
} }
export async function labelThumbnail(thumbnail: HTMLImageElement): Promise<HTMLElement | null> { export async function labelThumbnail(thumbnail: HTMLImageElement): Promise<HTMLElement | null> {
@@ -13,9 +18,7 @@ export async function labelThumbnail(thumbnail: HTMLImageElement): Promise<HTMLE
return null; return null;
} }
const link = (isOnInvidious() ? thumbnail.parentElement : thumbnail.querySelector("#thumbnail")) as HTMLAnchorElement const videoID = extractVideoID(thumbnail);
if (!link || link.nodeName !== "A" || !link.href) return null; // no link found
const videoID = parseYouTubeVideoIDFromURL(link.href)?.videoID;
if (!videoID) { if (!videoID) {
hideThumbnailLabel(thumbnail); hideThumbnailLabel(thumbnail);
return null; return null;
@@ -37,6 +40,37 @@ export async function labelThumbnail(thumbnail: HTMLImageElement): Promise<HTMLE
return overlay; return overlay;
} }
export async function setupThumbnailHover(thumbnail: HTMLImageElement): Promise<void> {
// Cache would be reset every load due to no SPA
if (isOnInvidious()) return;
const mainElement = thumbnail.closest("#dismissible") as HTMLElement;
if (mainElement) {
mainElement.removeEventListener("mouseenter", thumbnailHoverListener);
mainElement.addEventListener("mouseenter", thumbnailHoverListener);
}
}
function thumbnailHoverListener(e: MouseEvent) {
if (!chrome.runtime?.id) return;
const thumbnail = (e.target as HTMLElement).querySelector(getThumbnailSelector()) as HTMLImageElement;
if (!thumbnail) return;
// Pre-fetch data for this video
const videoID = extractVideoID(thumbnail);
if (videoID) {
void getSegmentsForVideo(videoID, false);
}
}
function extractVideoID(thumbnail: HTMLImageElement): VideoID | null {
const link = (isOnInvidious() ? thumbnail.parentElement : thumbnail.querySelector("#thumbnail")) as HTMLAnchorElement
if (!link || link.nodeName !== "A" || !link.href) return null; // no link found
return parseYouTubeVideoIDFromURL(link.href)?.videoID;
}
function getOldThumbnailLabel(thumbnail: HTMLImageElement): HTMLElement | null { function getOldThumbnailLabel(thumbnail: HTMLImageElement): HTMLElement | null {
return thumbnail.querySelector(".sponsorThumbnailLabel") as HTMLElement | null; return thumbnail.querySelector(".sponsorThumbnailLabel") as HTMLElement | null;
} }
@@ -109,7 +143,7 @@ function insertSBIconDefinition() {
} }
export function setupThumbnailListener(): void { export function setupThumbnailListener(): void {
setThumbnailListener(labelThumbnails, () => { setThumbnailListener(handleThumbnails, () => {
insertSBIconDefinition(); insertSBIconDefinition();
}, () => Config.isReady()); }, () => Config.isReady());
} }

View File

@@ -160,7 +160,7 @@ module.exports = env => {
if (path.match(/(\/|\\)_locales(\/|\\).+/)) { if (path.match(/(\/|\\)_locales(\/|\\).+/)) {
const parsed = JSON.parse(content.toString()); const parsed = JSON.parse(content.toString());
if (env.browser.toLowerCase() === "safari") { if (env.browser.toLowerCase() === "safari") {
parsed.fullName.message = parsed.fullName.message.match(/^.+(?= -)/)?.[0] || parsed.fullName.message; parsed.fullName.message = parsed.fullName.message.match(/^.+(?= [-])/)?.[0] || parsed.fullName.message;
if (parsed.fullName.message.length > 50) { if (parsed.fullName.message.length > 50) {
parsed.fullName.message = parsed.fullName.message.slice(0, 47) + "..."; parsed.fullName.message = parsed.fullName.message.slice(0, 47) + "...";
} }
@@ -171,6 +171,13 @@ module.exports = env => {
} }
} }
if (env.browser.toLowerCase() === "edge") {
parsed.Description.message = parsed.Description.message.match(/^.+(?=\. )/)?.[0] || parsed.Description.message;
if (parsed.Description.message.length > 132) {
parsed.Description.message = parsed.Description.message.slice(0, 129) + "...";
}
}
return Buffer.from(JSON.stringify(parsed)); return Buffer.from(JSON.stringify(parsed));
} }
@@ -185,6 +192,12 @@ module.exports = env => {
stream: env.stream stream: env.stream
}), }),
new configDiffPlugin() new configDiffPlugin()
] ],
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
}
}; };
}; };

View File

@@ -11,6 +11,7 @@ const chromeManifestExtra = require("../manifest/chrome-manifest-extra.json");
const safariManifestExtra = require("../manifest/safari-manifest-extra.json"); const safariManifestExtra = require("../manifest/safari-manifest-extra.json");
const betaManifestExtra = require("../manifest/beta-manifest-extra.json"); const betaManifestExtra = require("../manifest/beta-manifest-extra.json");
const firefoxBetaManifestExtra = require("../manifest/firefox-beta-manifest-extra.json"); const firefoxBetaManifestExtra = require("../manifest/firefox-beta-manifest-extra.json");
const manifestV2ManifestExtra = require("../manifest/manifest-v2-extra.json");
// schema for options object // schema for options object
const schema = { const schema = {
@@ -41,12 +42,14 @@ class BuildManifest {
// Add missing manifest elements // Add missing manifest elements
if (this.options.browser.toLowerCase() === "firefox") { if (this.options.browser.toLowerCase() === "firefox") {
mergeObjects(manifest, manifestV2ManifestExtra);
mergeObjects(manifest, firefoxManifestExtra); mergeObjects(manifest, firefoxManifestExtra);
} else if (this.options.browser.toLowerCase() === "chrome" } else if (this.options.browser.toLowerCase() === "chrome"
|| this.options.browser.toLowerCase() === "chromium" || this.options.browser.toLowerCase() === "chromium"
|| this.options.browser.toLowerCase() === "edge") { || this.options.browser.toLowerCase() === "edge") {
mergeObjects(manifest, chromeManifestExtra); mergeObjects(manifest, chromeManifestExtra);
} else if (this.options.browser.toLowerCase() === "safari") { } else if (this.options.browser.toLowerCase() === "safari") {
mergeObjects(manifest, manifestV2ManifestExtra);
mergeObjects(manifest, safariManifestExtra); mergeObjects(manifest, safariManifestExtra);
} }