mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-21 06:58:28 +03:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e12a914d5 | ||
|
|
25eaf4fa20 | ||
|
|
b3efa1f787 | ||
|
|
9a18e70e34 | ||
|
|
64ece9cb73 | ||
|
|
66c974b011 | ||
|
|
d8cc93c841 | ||
|
|
de22accfda | ||
|
|
e5b0b60dde | ||
|
|
32d98e6544 | ||
|
|
3dde05eda2 | ||
|
|
6aeefaae64 | ||
|
|
93d695e6c2 | ||
|
|
160924feee | ||
|
|
e3f3ed20e6 | ||
|
|
52149f4c0f | ||
|
|
cbc586f9ac | ||
|
|
fc8e20be0d | ||
|
|
368059eb0d | ||
|
|
ea77375fcc | ||
|
|
16005e417d | ||
|
|
7c5b750264 | ||
|
|
cc6b65c6c4 | ||
|
|
d2c99c2d77 | ||
|
|
84c25e3042 | ||
|
|
8840dba90f | ||
|
|
856dded58f | ||
|
|
1d1bd2a003 | ||
|
|
dc91ee76ca | ||
|
|
90bb9a4d02 | ||
|
|
d12d847f2f | ||
|
|
31a9de252d | ||
|
|
299cb485c3 | ||
|
|
882d462849 | ||
|
|
fc3710b37b | ||
|
|
3ac170ad01 | ||
|
|
4069545603 | ||
|
|
db9fc11f13 | ||
|
|
76667f68ec | ||
|
|
e27a287a68 | ||
|
|
ce3731774d | ||
|
|
8cbf2bb50b | ||
|
|
6f8c44b2eb |
@@ -1,15 +1,31 @@
|
|||||||
If you make any contributions to SponsorBlock after this file was created, you are agreeing that any code you have contributed will be licensed under LGPL-3.0.
|
If you make any contributions to SponsorBlock after this file was created, you are agreeing that any code you have contributed will be licensed under LGPL-3.0 or later.
|
||||||
|
|
||||||
# All Platforms
|
# Translations
|
||||||
Make sure to pull and update all submodules
|
https://crowdin.com/project/sponsorblock
|
||||||
`git submodule update --init --recursive`
|
|
||||||
|
|
||||||
"? property does not exist on type ConfigClass"
|
# Building
|
||||||
> Make sure to copy `config.json.example` to `config.json` and remove comments
|
## Building locally
|
||||||
|
0. You must have [Node.js 16 or later](https://nodejs.org/) and npm installed. Works best on Linux
|
||||||
|
1. Clone with submodules
|
||||||
|
```bash
|
||||||
|
git clone --recursive https://github.com/ajayyy/SponsorBlock
|
||||||
|
```
|
||||||
|
Or if you already cloned it, pull submodules with
|
||||||
|
```bash
|
||||||
|
git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
2. Copy the file `config.json.example` to `config.json` and adjust configuration as desired.
|
||||||
|
- Comments are invalid in JSON, make sure they are all removed.
|
||||||
|
- You will need to repeat this step in the future if you get build errors related to `CompileConfig` or `property does not exist on type ConfigClass`. This can happen for example when a new category is added.
|
||||||
|
3. Run `npm ci` in the repository to install dependencies.
|
||||||
|
4. Run `npm run build:dev` (for Chrome) or `npm run build:dev:firefox` (for Firefox) to generate a development version of the extension with source maps.
|
||||||
|
- You can also run `npm run build` (for Chrome) or `npm run build:firefox` (for Firefox) to generate a production build.
|
||||||
|
5. The built extension is now in `dist/`. You can load this folder directly in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#manifest), or convert it to a zip file to load it as a [temporary extension](https://developer.mozilla.org/docs/Tools/about:debugging#loading_a_temporary_extension) in Firefox.
|
||||||
|
|
||||||
|
## Developing with a clean profile and hot reloading
|
||||||
|
Run `npm run dev` (for Chrome) or `npm run dev:firefox` (for Firefox) to run the extension using a clean browser profile with hot reloading. This uses [`web-ext run`](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#commands).
|
||||||
|
|
||||||
|
Known chromium bug: Extension is not loaded properly on first start. Visit `chrome://extensions/` and reload the extension.
|
||||||
|
|
||||||
|
For Firefox for Android, use `npm run dev:firefox-android -- --adb-device <ip-address of the device>`. See the [Firefox documentation](https://extensionworkshop.com/documentation/develop/developing-extensions-for-firefox-for-android/#debug-your-extension) for more information. You may need to edit package.json and add the parameters directly there.
|
||||||
|
|
||||||
# Windows
|
|
||||||
"Cannot find module "../maze-utils"
|
|
||||||
- Enable "Developer Mode" in windows for symlinks
|
|
||||||
- `src/maze-utils` will not appear properly and builds will fail since it is is only rendered as a file
|
|
||||||
- Enable symlink support in git `git config --global core.symlinks true`
|
|
||||||
- run `git checkout -- src/maze-utils` in order to create a symlink instead of a text file
|
|
||||||
41
README.md
41
README.md
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
SponsorBlock is an open-source crowdsourced browser extension to skip sponsor segments in YouTube videos. Users submit when a sponsor happens from the extension, and the extension automatically skips sponsors it knows about. It also supports skipping other categories, such as intros, outros and reminders to subscribe.
|
SponsorBlock is an open-source crowdsourced browser extension to skip sponsor segments in YouTube videos. Users submit when a sponsor happens from the extension, and the extension automatically skips sponsors it knows about. It also supports skipping other categories, such as intros, outros and reminders to subscribe.
|
||||||
|
|
||||||
It also supports Invidio.us.
|
It also supports Invidious.
|
||||||
|
|
||||||
**Translate:** [](https://crowdin.com/project/sponsorblock)
|
**Translate:** [](https://crowdin.com/project/sponsorblock)
|
||||||
|
|
||||||
@@ -56,47 +56,14 @@ The dataset and API are now being used in some [ports](https://github.com/ajayyy
|
|||||||
|
|
||||||
# API
|
# API
|
||||||
|
|
||||||
You can read the API docs [here](https://wiki.sponsor.ajay.app/index.php/API_Docs).
|
You can read the API docs [here](https://wiki.sponsor.ajay.app/w/API_Docs).
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
You must have [Node.js 16](https://nodejs.org/) and npm installed.
|
|
||||||
|
|
||||||
1. Clone with submodules
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone --recursive https://github.com/ajayyy/SponsorBlock
|
|
||||||
```
|
|
||||||
|
|
||||||
Or if you already cloned it, pull submodules with
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Copy the file `config.json.example` to `config.json` and adjust configuration as desired.
|
|
||||||
|
|
||||||
- You will need to repeat this step in the future if you get build errors related to `CompileConfig`. This can happen for example when a new category is added.
|
|
||||||
|
|
||||||
3. Run `npm install` in the repository to install dependencies.
|
|
||||||
|
|
||||||
4. Run `npm run build:dev` (for Chrome) or `npm run build:dev:firefox` (for Firefox) to generate a development version of the extension with source maps.
|
|
||||||
|
|
||||||
- You can also run `npm run build` (for Chrome) or `npm run build:firefox` (for Firefox) to generate a production build.
|
|
||||||
|
|
||||||
5. The built extension is now in `dist/`. You can load this folder directly in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/#manifest), or convert it to a zip file to load it as a [temporary extension](https://developer.mozilla.org/en-US/docs/Tools/about:debugging#loading_a_temporary_extension) in Firefox.
|
|
||||||
|
|
||||||
### Developing with a clean profile and hot reloading
|
|
||||||
|
|
||||||
Run `npm run dev` (for Chrome) or `npm run dev:firefox` (for Firefox) to run the extension using a clean browser profile with hot reloading. This uses [`web-ext run`](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#commands).
|
|
||||||
|
|
||||||
Known chromium bug: Extension is not loaded properly on first start. Visit `chrome://extensions/` and reload the extension.
|
|
||||||
|
|
||||||
For Firefox for Android, use `npm run dev:firefox-android -- --adb-device <ip-address of the device>`. See the [Firefox documentation](https://extensionworkshop.com/documentation/develop/developing-extensions-for-firefox-for-android/#debug-your-extension) for more information.
|
|
||||||
|
|
||||||
# Credit
|
# Credit
|
||||||
|
|
||||||
The awesome [Invidious API](https://docs.invidious.io/API.md) was previously used, and the server is now using [NewLeaf](https://git.sr.ht/~cadence/NewLeaf) as a to get video info from YouTube.
|
The awesome [Invidious API](https://docs.invidious.io/) was previously used, and the server is now using [NewLeaf](https://git.sr.ht/~cadence/NewLeaf) as a to get video info from YouTube.
|
||||||
|
|
||||||
Originally forked from [YTSponsorSkip](https://github.com/NDevTK/YTSponsorSkip), but very little code remains.
|
Originally forked from [YTSponsorSkip](https://github.com/NDevTK/YTSponsorSkip), but very little code remains.
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,8 @@
|
|||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"persistent": false
|
"persistent": false
|
||||||
}
|
},
|
||||||
|
"permissions": [
|
||||||
|
"https://*.youtube.com/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,11 @@
|
|||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "sponsorBlocker@ajay.app"
|
"id": "sponsorBlocker@ajay.app"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"background": {
|
||||||
|
"persistent": false
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"scripting"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.4.11",
|
"version": "5.4.16",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
@@ -116,6 +116,21 @@
|
|||||||
"light": "icons/IconSponsorBlocker128px.png",
|
"light": "icons/IconSponsorBlocker128px.png",
|
||||||
"dark": "icons/IconSponsorBlocker128px.png",
|
"dark": "icons/IconSponsorBlocker128px.png",
|
||||||
"size": 128
|
"size": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"light": "icons/IconSponsorBlocker256px.png",
|
||||||
|
"dark": "icons/IconSponsorBlocker256px.png",
|
||||||
|
"size": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"light": "icons/IconSponsorBlocker512px.png",
|
||||||
|
"dark": "icons/IconSponsorBlocker512px.png",
|
||||||
|
"size": 512
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"light": "icons/IconSponsorBlocker1024px.png",
|
||||||
|
"dark": "icons/IconSponsorBlocker1024px.png",
|
||||||
|
"size": 1024
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"background": {
|
"background": {
|
||||||
"persistent": false
|
"persistent": false
|
||||||
}
|
},
|
||||||
|
"permissions": [
|
||||||
|
"scripting"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule maze-utils updated: b7f55f39cc...79bef97a3b
10
package-lock.json
generated
10
package-lock.json
generated
@@ -13332,8 +13332,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -23348,8 +23349,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
|
|||||||
Submodule public/_locales updated: fc6e767fd0...5528978f2f
@@ -867,3 +867,7 @@ input::-webkit-inner-spin-button {
|
|||||||
.sponsorThumbnailLabel:hover span {
|
.sponsorThumbnailLabel:hover span {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sponsorblock-chapter-visible {
|
||||||
|
display: inherit !important;
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ import * as CompileConfig from "../config.json";
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
import { Registration } from "./types";
|
import { Registration } from "./types";
|
||||||
import "content-scripts-register-polyfill";
|
import "content-scripts-register-polyfill";
|
||||||
import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "./maze-utils/background-request-proxy";
|
import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "../maze-utils/src/background-request-proxy";
|
||||||
import { setupTabUpdates } from "./maze-utils/tab-updates";
|
import { setupTabUpdates } from "../maze-utils/src/tab-updates";
|
||||||
import { generateUserID } from "./maze-utils/setup";
|
import { generateUserID } from "../maze-utils/src/setup";
|
||||||
|
|
||||||
// Make the config public for debugging purposes
|
// Make the config public for debugging purposes
|
||||||
|
|
||||||
@@ -13,6 +13,10 @@ 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 { injectUpdatedScripts } from "../maze-utils/src/cleanup";
|
||||||
|
import { logWarn } from "./utils/logger";
|
||||||
|
import { chromeP } from "../maze-utils/src/browserApi";
|
||||||
const utils = new Utils({
|
const utils = new Utils({
|
||||||
registerFirefoxContentScript,
|
registerFirefoxContentScript,
|
||||||
unregisterFirefoxContentScript
|
unregisterFirefoxContentScript
|
||||||
@@ -24,7 +28,7 @@ const popupPort: Record<string, chrome.runtime.Port> = {};
|
|||||||
const contentScriptRegistrations = {};
|
const contentScriptRegistrations = {};
|
||||||
|
|
||||||
// Register content script if needed
|
// Register content script if needed
|
||||||
utils.wait(() => Config.config !== null).then(function() {
|
utils.wait(() => Config.isReady()).then(function() {
|
||||||
if (Config.config.supportInvidious) utils.setupExtraSiteContentScripts();
|
if (Config.config.supportInvidious) utils.setupExtraSiteContentScripts();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,7 +76,11 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
|||||||
case "infoUpdated":
|
case "infoUpdated":
|
||||||
case "videoChanged":
|
case "videoChanged":
|
||||||
if (sender.tab) {
|
if (sender.tab) {
|
||||||
popupPort[sender.tab.id]?.postMessage(request);
|
try {
|
||||||
|
popupPort[sender.tab.id]?.postMessage(request);
|
||||||
|
} catch (e) {
|
||||||
|
// This can happen if the popup is closed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
@@ -132,6 +140,11 @@ chrome.runtime.onInstalled.addListener(function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
|
// Only do this once the old version understands how to clean itself up
|
||||||
|
if (!isFirefoxOrSafari() && chrome.runtime.getManifest().version !== "5.4.13") {
|
||||||
|
injectUpdatedScripts().catch(logWarn);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,27 +153,65 @@ chrome.runtime.onInstalled.addListener(function () {
|
|||||||
*
|
*
|
||||||
* @param {JSON} options
|
* @param {JSON} options
|
||||||
*/
|
*/
|
||||||
function registerFirefoxContentScript(options: Registration) {
|
async function registerFirefoxContentScript(options: Registration) {
|
||||||
const oldRegistration = contentScriptRegistrations[options.id];
|
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||||
if (oldRegistration) oldRegistration.unregister();
|
// Bug in Firefox where you need to use browser namespace for this call
|
||||||
|
const getContentScripts = async (filter: browser.scripting.ContentScriptFilter) => {
|
||||||
|
if (isFirefoxOrSafari()) {
|
||||||
|
return await browser.scripting.getRegisteredContentScripts(filter);
|
||||||
|
} else {
|
||||||
|
return await chrome.scripting.getRegisteredContentScripts(filter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const existingRegistrations = await getContentScripts({
|
||||||
|
ids: [options.id]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingRegistrations.length > 0
|
||||||
|
&& existingRegistrations[0].matches.every((match) => options.matches.includes(match))) {
|
||||||
|
// No need to register another script, already registered
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await unregisterFirefoxContentScript(options.id);
|
||||||
|
|
||||||
|
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||||
|
await chromeP.scripting.registerContentScripts([{
|
||||||
|
id: options.id,
|
||||||
|
runAt: "document_start",
|
||||||
|
matches: options.matches,
|
||||||
|
allFrames: options.allFrames,
|
||||||
|
js: options.js,
|
||||||
|
css: options.css,
|
||||||
|
persistAcrossSessions: true,
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
chrome.contentScripts.register({
|
||||||
|
allFrames: options.allFrames,
|
||||||
|
js: options.js?.map?.(file => ({file})),
|
||||||
|
css: options.css?.map?.(file => ({file})),
|
||||||
|
matches: options.matches
|
||||||
|
}).then((registration) => void (contentScriptRegistrations[options.id] = registration));
|
||||||
|
}
|
||||||
|
|
||||||
chrome.contentScripts.register({
|
|
||||||
allFrames: options.allFrames,
|
|
||||||
js: options.js,
|
|
||||||
css: options.css,
|
|
||||||
matches: options.matches
|
|
||||||
}).then((registration) => void (contentScriptRegistrations[options.id] = registration));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only works on Firefox.
|
* Only works on Firefox.
|
||||||
* Firefox requires that this is handled by the background script
|
* Firefox requires that this is handled by the background script
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
function unregisterFirefoxContentScript(id: string) {
|
async function unregisterFirefoxContentScript(id: string) {
|
||||||
if (contentScriptRegistrations[id]) {
|
if ("scripting" in chrome && "getRegisteredContentScripts" in chrome.scripting) {
|
||||||
contentScriptRegistrations[id].unregister();
|
await chromeP.scripting.unregisterContentScripts({
|
||||||
delete contentScriptRegistrations[id];
|
ids: [id]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (contentScriptRegistrations[id]) {
|
||||||
|
contentScriptRegistrations[id].unregister();
|
||||||
|
delete contentScriptRegistrations[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
|||||||
import { VoteResponse } from "../messageTypes";
|
import { VoteResponse } from "../messageTypes";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
import { Tooltip } from "../render/Tooltip";
|
import { Tooltip } from "../render/Tooltip";
|
||||||
import { getErrorMessage } from "../maze-utils/formating";
|
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
export interface CategoryPillProps {
|
export interface CategoryPillProps {
|
||||||
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
|
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
|||||||
import { VoteResponse } from "../messageTypes";
|
import { VoteResponse } from "../messageTypes";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
import { Tooltip } from "../render/Tooltip";
|
import { Tooltip } from "../render/Tooltip";
|
||||||
import { getErrorMessage } from "../maze-utils/formating";
|
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
export interface ChapterVoteProps {
|
export interface ChapterVoteProps {
|
||||||
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
|
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
|
import SbSvg from "../svg-icons/sb_svg";
|
||||||
|
|
||||||
enum CountdownMode {
|
enum CountdownMode {
|
||||||
Timer,
|
Timer,
|
||||||
@@ -28,6 +29,7 @@ export interface NoticeProps {
|
|||||||
extraClass?: string;
|
extraClass?: string;
|
||||||
hideLogo?: boolean;
|
hideLogo?: boolean;
|
||||||
hideRightInfo?: boolean;
|
hideRightInfo?: boolean;
|
||||||
|
logoFill?: string;
|
||||||
|
|
||||||
// Callback for when this is closed
|
// Callback for when this is closed
|
||||||
closeListener: () => void;
|
closeListener: () => void;
|
||||||
@@ -122,10 +124,10 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||||||
<td className="noticeLeftIcon">
|
<td className="noticeLeftIcon">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
{!this.props.hideLogo &&
|
{!this.props.hideLogo &&
|
||||||
<img id={"sponsorSkipLogo" + this.idSuffix}
|
<SbSvg
|
||||||
className="sponsorSkipLogo sponsorSkipObject"
|
id={"sponsorSkipLogo" + this.idSuffix}
|
||||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
fill={this.props.logoFill}
|
||||||
</img>
|
className="sponsorSkipLogo sponsorSkipObject"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<span id={"sponsorSkipMessage" + this.idSuffix}
|
<span id={"sponsorSkipMessage" + this.idSuffix}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ 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 PencilSvg from "../svg-icons/pencil_svg";
|
import PencilSvg from "../svg-icons/pencil_svg";
|
||||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||||
import { generateUserID } from "../maze-utils/setup";
|
import { generateUserID } from "../../maze-utils/src/setup";
|
||||||
import { keybindToString } from "../maze-utils/config";
|
import { keybindToString } from "../../maze-utils/src/config";
|
||||||
|
|
||||||
enum SkipButtonState {
|
enum SkipButtonState {
|
||||||
Undo, // Unskip
|
Undo, // Unskip
|
||||||
@@ -177,7 +177,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
<NoticeComponent
|
||||||
|
noticeTitle={this.state.noticeTitle}
|
||||||
amountOfPreviousNotices={this.amountOfPreviousNotices}
|
amountOfPreviousNotices={this.amountOfPreviousNotices}
|
||||||
showInSecondSlot={this.showInSecondSlot}
|
showInSecondSlot={this.showInSecondSlot}
|
||||||
idSuffix={this.idSuffix}
|
idSuffix={this.idSuffix}
|
||||||
@@ -191,6 +192,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
ref={this.noticeRef}
|
ref={this.noticeRef}
|
||||||
closeListener={() => this.closeListener()}
|
closeListener={() => this.closeListener()}
|
||||||
smaller={this.state.smaller}
|
smaller={this.state.smaller}
|
||||||
|
logoFill={Config.config.barTypes[this.segments[0].category].color}
|
||||||
limitWidth={true}
|
limitWidth={true}
|
||||||
firstColumn={firstColumn}
|
firstColumn={firstColumn}
|
||||||
bottomRow={[...this.getMessageBoxes(), ...this.getBottomRow() ]}
|
bottomRow={[...this.getMessageBoxes(), ...this.getBottomRow() ]}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
|
|||||||
import { RectangleTooltip } from "../render/RectangleTooltip";
|
import { RectangleTooltip } from "../render/RectangleTooltip";
|
||||||
import SelectorComponent, { SelectorOption } from "./SelectorComponent";
|
import SelectorComponent, { SelectorOption } from "./SelectorComponent";
|
||||||
import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
|
import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
|
||||||
import { getFormattedTime, getFormattedTimeToSeconds } from "../maze-utils/formating";
|
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as React from "react";
|
|||||||
import { createRoot, Root } from 'react-dom/client';
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
import Config from "../../config";
|
import Config from "../../config";
|
||||||
import KeybindDialogComponent from "./KeybindDialogComponent";
|
import KeybindDialogComponent from "./KeybindDialogComponent";
|
||||||
import { formatKey, Keybind, keybindEquals, keybindToString } from "../../maze-utils/config";
|
import { formatKey, Keybind, keybindEquals, keybindToString } from "../../../maze-utils/src/config";
|
||||||
|
|
||||||
export interface KeybindProps {
|
export interface KeybindProps {
|
||||||
option: string;
|
option: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ChangeEvent } from "react";
|
import { ChangeEvent } from "react";
|
||||||
import Config from "../../config";
|
import Config from "../../config";
|
||||||
import { Keybind, formatKey, keybindEquals } from "../../maze-utils/config";
|
import { Keybind, formatKey, keybindEquals } from "../../../maze-utils/src/config";
|
||||||
|
|
||||||
export interface KeybindDialogProps {
|
export interface KeybindDialogProps {
|
||||||
option: string;
|
option: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
import * as invidiousList from "../ci/invidiouslist.json";
|
import * as invidiousList from "../ci/invidiouslist.json";
|
||||||
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, VideoID, SponsorHideType } from "./types";
|
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, VideoID, SponsorHideType } from "./types";
|
||||||
import { Keybind, ProtoConfig, keybindEquals } from "./maze-utils/config";
|
import { Keybind, ProtoConfig, keybindEquals } from "../maze-utils/src/config";
|
||||||
import { HashedValue } from "./maze-utils/hash";
|
import { HashedValue } from "../maze-utils/src/hash";
|
||||||
|
|
||||||
export interface Permission {
|
export interface Permission {
|
||||||
canSubmit: boolean;
|
canSubmit: boolean;
|
||||||
@@ -75,7 +75,7 @@ interface SBConfig {
|
|||||||
allowScrollingToEdit: boolean;
|
allowScrollingToEdit: boolean;
|
||||||
deArrowInstalled: boolean;
|
deArrowInstalled: boolean;
|
||||||
showDeArrowPromotion: boolean;
|
showDeArrowPromotion: boolean;
|
||||||
showZoomToFillError: boolean;
|
showZoomToFillError2: boolean;
|
||||||
|
|
||||||
// Used to cache calculated text color info
|
// Used to cache calculated text color info
|
||||||
categoryPillColors: {
|
categoryPillColors: {
|
||||||
@@ -148,6 +148,10 @@ class ConfigClass extends ProtoConfig<SBConfig, SBStorage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function migrateOldSyncFormats(config: SBConfig) {
|
function migrateOldSyncFormats(config: SBConfig) {
|
||||||
|
if (config["showZoomToFillError"]) {
|
||||||
|
chrome.storage.sync.remove("showZoomToFillError");
|
||||||
|
}
|
||||||
|
|
||||||
if (!config["chapterCategoryAdded"]) {
|
if (!config["chapterCategoryAdded"]) {
|
||||||
config["chapterCategoryAdded"] = true;
|
config["chapterCategoryAdded"] = true;
|
||||||
|
|
||||||
@@ -312,7 +316,7 @@ const syncDefaults = {
|
|||||||
allowScrollingToEdit: true,
|
allowScrollingToEdit: true,
|
||||||
deArrowInstalled: false,
|
deArrowInstalled: false,
|
||||||
showDeArrowPromotion: true,
|
showDeArrowPromotion: true,
|
||||||
showZoomToFillError: true,
|
showZoomToFillError2: true,
|
||||||
|
|
||||||
categoryPillColors: {},
|
categoryPillColors: {},
|
||||||
|
|
||||||
|
|||||||
195
src/content.ts
195
src/content.ts
@@ -24,7 +24,7 @@ import SubmissionNotice from "./render/SubmissionNotice";
|
|||||||
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
||||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||||
import { getControls, getExistingChapters, getHashParams, isVisible } from "./utils/pageUtils";
|
import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isVisible } from "./utils/pageUtils";
|
||||||
import { CategoryPill } from "./render/CategoryPill";
|
import { CategoryPill } from "./render/CategoryPill";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "./utils/animationUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
@@ -32,19 +32,23 @@ import { logDebug } from "./utils/logger";
|
|||||||
import { importTimes } from "./utils/exporter";
|
import { importTimes } from "./utils/exporter";
|
||||||
import { ChapterVote } from "./render/ChapterVote";
|
import { ChapterVote } from "./render/ChapterVote";
|
||||||
import { openWarningDialog } from "./utils/warnings";
|
import { openWarningDialog } from "./utils/warnings";
|
||||||
import { isFirefoxOrSafari, waitFor } from "./maze-utils";
|
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||||
import { getErrorMessage, getFormattedTime } from "./maze-utils/formating";
|
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
||||||
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube } from "./maze-utils/video";
|
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube } from "../maze-utils/src/video";
|
||||||
import { Keybind, StorageChangesObject, isSafari, keybindEquals } from "./maze-utils/config";
|
import { Keybind, StorageChangesObject, isSafari, keybindEquals } from "../maze-utils/src/config";
|
||||||
import { findValidElement, waitForElement } from "./maze-utils/dom"
|
import { findValidElement, waitForElement } from "../maze-utils/src/dom"
|
||||||
import { getHash, HashedValue } from "./maze-utils/hash";
|
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
import { generateUserID } from "./maze-utils/setup";
|
import { generateUserID } from "../maze-utils/src/setup";
|
||||||
import { updateAll } from "./maze-utils/thumbnailManagement";
|
import { updateAll } from "../maze-utils/src/thumbnailManagement";
|
||||||
import { setupThumbnailListener } from "./utils/thumbnails";
|
import { setupThumbnailListener } from "./utils/thumbnails";
|
||||||
import * as documentScript from "../dist/js/document.js";
|
import * as documentScript from "../dist/js/document.js";
|
||||||
import { Tooltip } from "./render/Tooltip";
|
import { Tooltip } from "./render/Tooltip";
|
||||||
import { isDeArrowInstalled } from "./utils/crossExtension";
|
import { isDeArrowInstalled } from "./utils/crossExtension";
|
||||||
import { runCompatibilityChecks } from "./utils/compatibility";
|
import { runCompatibilityChecks } from "./utils/compatibility";
|
||||||
|
import { cleanPage } from "./utils/pageCleaner";
|
||||||
|
import { addCleanupListener } from "../maze-utils/src/cleanup";
|
||||||
|
|
||||||
|
cleanPage();
|
||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
@@ -473,6 +477,8 @@ function videoIDChange(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleMobileControlsMutations(): void {
|
function handleMobileControlsMutations(): void {
|
||||||
|
if (!chrome.runtime?.id) return;
|
||||||
|
|
||||||
updateVisibilityOfPlayerControlsButton();
|
updateVisibilityOfPlayerControlsButton();
|
||||||
|
|
||||||
skipButtonControlBar?.updateMobileControls();
|
skipButtonControlBar?.updateMobileControls();
|
||||||
@@ -703,7 +709,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
|
|
||||||
// Don't pretend to be earlier than we are, could result in loops
|
// Don't pretend to be earlier than we are, could result in loops
|
||||||
if (forcedSkipTime !== null && forceVideoTime > forcedSkipTime) {
|
if (forcedSkipTime !== null && forceVideoTime > forcedSkipTime) {
|
||||||
forcedSkipTime = null;
|
forcedSkipTime = forceVideoTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
startSponsorSchedule(forcedIncludeIntersectingSegments, forcedSkipTime, forcedIncludeNonIntersectingSegments);
|
startSponsorSchedule(forcedIncludeIntersectingSegments, forcedSkipTime, forcedIncludeNonIntersectingSegments);
|
||||||
@@ -797,11 +803,12 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
|
|||||||
const currentVideoID = getYouTubeVideoID();
|
const currentVideoID = getYouTubeVideoID();
|
||||||
const recordedVideoID = videoID || getVideoID();
|
const recordedVideoID = videoID || getVideoID();
|
||||||
if (currentVideoID !== recordedVideoID || (sponsorTime
|
if (currentVideoID !== recordedVideoID || (sponsorTime
|
||||||
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment === sponsorTime.segment))
|
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))
|
||||||
&& !sponsorTimesSubmitting.some((time) => time.segment === sponsorTime.segment))) {
|
&& !sponsorTimesSubmitting.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))) {
|
||||||
// Something has really gone wrong
|
// Something has really gone wrong
|
||||||
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
|
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
|
||||||
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
|
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
|
||||||
|
console.error("[SponsorBlock] SponsorTime", sponsorTime, "sponsorTimes", sponsorTimes, "sponsorTimesSubmitting", sponsorTimesSubmitting);
|
||||||
|
|
||||||
// Video ID change occured
|
// Video ID change occured
|
||||||
checkVideoIDChange();
|
checkVideoIDChange();
|
||||||
@@ -812,18 +819,38 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let playbackRateCheckInterval: NodeJS.Timeout | null = null;
|
||||||
|
let lastPlaybackSpeed = 1;
|
||||||
|
let setupVideoListenersFirstTime = true;
|
||||||
function setupVideoListeners() {
|
function setupVideoListeners() {
|
||||||
//wait until it is loaded
|
//wait until it is loaded
|
||||||
getVideo().addEventListener('loadstart', videoOnReadyListener)
|
getVideo().addEventListener('loadstart', videoOnReadyListener)
|
||||||
getVideo().addEventListener('durationchange', durationChangeListener);
|
getVideo().addEventListener('durationchange', durationChangeListener);
|
||||||
|
|
||||||
|
if (setupVideoListenersFirstTime) {
|
||||||
|
addCleanupListener(() => {
|
||||||
|
getVideo().removeEventListener('loadstart', videoOnReadyListener);
|
||||||
|
getVideo().removeEventListener('durationchange', durationChangeListener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!Config.config.disableSkipping) {
|
if (!Config.config.disableSkipping) {
|
||||||
switchingVideos = false;
|
switchingVideos = false;
|
||||||
|
|
||||||
let startedWaiting = false;
|
let startedWaiting = false;
|
||||||
let lastPausedAtZero = true;
|
let lastPausedAtZero = true;
|
||||||
|
|
||||||
getVideo().addEventListener('play', () => {
|
const rateChangeListener = () => {
|
||||||
|
updateVirtualTime();
|
||||||
|
clearWaitingTime();
|
||||||
|
|
||||||
|
startSponsorSchedule();
|
||||||
|
};
|
||||||
|
getVideo().addEventListener('ratechange', rateChangeListener);
|
||||||
|
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
|
||||||
|
getVideo().addEventListener('videoSpeed_ratechange', rateChangeListener);
|
||||||
|
|
||||||
|
const playListener = () => {
|
||||||
// If it is not the first event, then the only way to get to 0 is if there is a seek event
|
// If it is not the first event, then the only way to get to 0 is if there is a seek event
|
||||||
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
||||||
// gone back to the begining
|
// gone back to the begining
|
||||||
@@ -853,9 +880,10 @@ function setupVideoListeners() {
|
|||||||
|
|
||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
getVideo().addEventListener('play', playListener);
|
||||||
|
|
||||||
});
|
const playingListener = () => {
|
||||||
getVideo().addEventListener('playing', () => {
|
|
||||||
updateVirtualTime();
|
updateVirtualTime();
|
||||||
lastPausedAtZero = false;
|
lastPausedAtZero = false;
|
||||||
|
|
||||||
@@ -881,8 +909,31 @@ function setupVideoListeners() {
|
|||||||
|
|
||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
getVideo().addEventListener('seeking', () => {
|
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||||
|
lastPlaybackSpeed = getVideo().playbackRate;
|
||||||
|
|
||||||
|
// Video speed controller compatibility
|
||||||
|
// That extension makes rate change events not propagate
|
||||||
|
if (document.body.classList.contains("vsc-initialized")) {
|
||||||
|
playbackRateCheckInterval = setInterval(() => {
|
||||||
|
if ((!getVideoID() || getVideo().paused) && playbackRateCheckInterval) {
|
||||||
|
// Video is gone, stop checking
|
||||||
|
clearInterval(playbackRateCheckInterval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getVideo().playbackRate !== lastPlaybackSpeed) {
|
||||||
|
lastPlaybackSpeed = getVideo().playbackRate;
|
||||||
|
|
||||||
|
rateChangeListener();
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getVideo().addEventListener('playing', playingListener);
|
||||||
|
|
||||||
|
const seekingListener = () => {
|
||||||
lastKnownVideoTime.fromPause = false;
|
lastKnownVideoTime.fromPause = false;
|
||||||
|
|
||||||
if (!getVideo().paused){
|
if (!getVideo().paused){
|
||||||
@@ -906,45 +957,54 @@ function setupVideoListeners() {
|
|||||||
lastPausedAtZero = true;
|
lastPausedAtZero = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
getVideo().addEventListener('ratechange', () => {
|
getVideo().addEventListener('seeking', seekingListener);
|
||||||
updateVirtualTime();
|
|
||||||
clearWaitingTime();
|
|
||||||
|
|
||||||
startSponsorSchedule();
|
|
||||||
});
|
|
||||||
// Used by videospeed extension (https://github.com/igrigorik/videospeed/pull/740)
|
|
||||||
getVideo().addEventListener('videoSpeed_ratechange', () => {
|
|
||||||
updateVirtualTime();
|
|
||||||
clearWaitingTime();
|
|
||||||
|
|
||||||
startSponsorSchedule();
|
|
||||||
});
|
|
||||||
const stoppedPlayback = () => {
|
const stoppedPlayback = () => {
|
||||||
// Reset lastCheckVideoTime
|
// Reset lastCheckVideoTime
|
||||||
lastCheckVideoTime = -1;
|
lastCheckVideoTime = -1;
|
||||||
lastCheckTime = 0;
|
lastCheckTime = 0;
|
||||||
|
|
||||||
|
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||||
|
|
||||||
lastKnownVideoTime.videoTime = null;
|
lastKnownVideoTime.videoTime = null;
|
||||||
lastKnownVideoTime.preciseTime = null;
|
lastKnownVideoTime.preciseTime = null;
|
||||||
updateWaitingTime();
|
updateWaitingTime();
|
||||||
|
|
||||||
cancelSponsorSchedule();
|
cancelSponsorSchedule();
|
||||||
};
|
};
|
||||||
getVideo().addEventListener('pause', () => {
|
const pauseListener = () => {
|
||||||
lastKnownVideoTime.fromPause = true;
|
lastKnownVideoTime.fromPause = true;
|
||||||
|
|
||||||
stoppedPlayback();
|
stoppedPlayback();
|
||||||
});
|
};
|
||||||
getVideo().addEventListener('waiting', () => {
|
getVideo().addEventListener('pause', pauseListener);
|
||||||
|
const waitingListener = () => {
|
||||||
logDebug("[SB] Not skipping due to buffering");
|
logDebug("[SB] Not skipping due to buffering");
|
||||||
startedWaiting = true;
|
startedWaiting = true;
|
||||||
|
|
||||||
stoppedPlayback();
|
stoppedPlayback();
|
||||||
});
|
};
|
||||||
|
getVideo().addEventListener('waiting', waitingListener);
|
||||||
|
|
||||||
startSponsorSchedule();
|
startSponsorSchedule();
|
||||||
|
|
||||||
|
if (setupVideoListenersFirstTime) {
|
||||||
|
addCleanupListener(() => {
|
||||||
|
getVideo().removeEventListener('play', playListener);
|
||||||
|
getVideo().removeEventListener('playing', playingListener);
|
||||||
|
getVideo().removeEventListener('seeking', seekingListener);
|
||||||
|
getVideo().removeEventListener('ratechange', rateChangeListener);
|
||||||
|
getVideo().removeEventListener('videoSpeed_ratechange', rateChangeListener);
|
||||||
|
getVideo().removeEventListener('pause', pauseListener);
|
||||||
|
getVideo().removeEventListener('waiting', waitingListener);
|
||||||
|
|
||||||
|
if (playbackRateCheckInterval) clearInterval(playbackRateCheckInterval);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupVideoListenersFirstTime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVirtualTime() {
|
function updateVirtualTime() {
|
||||||
@@ -1356,18 +1416,20 @@ async function channelIDChange(channelIDInfo: ChannelIDInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function videoElementChange(newVideo: boolean): void {
|
function videoElementChange(newVideo: boolean): void {
|
||||||
if (newVideo) {
|
waitFor(() => Config.isReady()).then(() => {
|
||||||
setupVideoListeners();
|
if (newVideo) {
|
||||||
setupSkipButtonControlBar();
|
setupVideoListeners();
|
||||||
setupCategoryPill();
|
setupSkipButtonControlBar();
|
||||||
}
|
setupCategoryPill();
|
||||||
|
}
|
||||||
checkPreviewbarState();
|
|
||||||
|
checkPreviewbarState();
|
||||||
// Incase the page is still transitioning, check again in a few seconds
|
|
||||||
setTimeout(checkPreviewbarState, 100);
|
// Incase the page is still transitioning, check again in a few seconds
|
||||||
setTimeout(checkPreviewbarState, 1000);
|
setTimeout(checkPreviewbarState, 100);
|
||||||
setTimeout(checkPreviewbarState, 5000);
|
setTimeout(checkPreviewbarState, 1000);
|
||||||
|
setTimeout(checkPreviewbarState, 5000);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPreviewbarState(): void {
|
function checkPreviewbarState(): void {
|
||||||
@@ -1587,8 +1649,10 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
|||||||
// for some reason you also can't skip to 1 second before the end
|
// for some reason you also can't skip to 1 second before the end
|
||||||
if (v.loop && v.duration > 1 && skipTime[1] >= v.duration - 1) {
|
if (v.loop && v.duration > 1 && skipTime[1] >= v.duration - 1) {
|
||||||
v.currentTime = 0;
|
v.currentTime = 0;
|
||||||
} else if (navigator.vendor === "Apple Computer, Inc." && v.duration > 1 && skipTime[1] >= v.duration) {
|
} else if (v.duration > 1 && skipTime[1] >= v.duration
|
||||||
|
&& (navigator.vendor === "Apple Computer, Inc." || isPlayingPlaylist())) {
|
||||||
// MacOS will loop otherwise #1027
|
// MacOS will loop otherwise #1027
|
||||||
|
// Sometimes playlists loop too #1804
|
||||||
v.currentTime = v.duration - 0.001;
|
v.currentTime = v.duration - 0.001;
|
||||||
} else {
|
} else {
|
||||||
if (inMuteSegment(skipTime[1], true)) {
|
if (inMuteSegment(skipTime[1], true)) {
|
||||||
@@ -1619,9 +1683,10 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
|||||||
beep.play();
|
beep.play();
|
||||||
beep.addEventListener("ended", () => {
|
beep.addEventListener("ended", () => {
|
||||||
navigator.mediaSession.metadata = null;
|
navigator.mediaSession.metadata = null;
|
||||||
setTimeout(() =>
|
setTimeout(() => {
|
||||||
navigator.mediaSession.metadata = oldMetadata
|
navigator.mediaSession.metadata = oldMetadata;
|
||||||
);
|
beep.remove();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2331,11 +2396,21 @@ function previousChapter(): void {
|
|||||||
function addHotkeyListener(): void {
|
function addHotkeyListener(): void {
|
||||||
document.addEventListener("keydown", hotkeyListener);
|
document.addEventListener("keydown", hotkeyListener);
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
const onLoad = () => {
|
||||||
// Allow us to stop propagation to YouTube by being deeper
|
// Allow us to stop propagation to YouTube by being deeper
|
||||||
document.removeEventListener("keydown", hotkeyListener);
|
document.removeEventListener("keydown", hotkeyListener);
|
||||||
document.body.addEventListener("keydown", hotkeyListener);
|
document.body.addEventListener("keydown", hotkeyListener);
|
||||||
});
|
|
||||||
|
addCleanupListener(() => {
|
||||||
|
document.body.removeEventListener("keydown", hotkeyListener);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
onLoad();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", onLoad);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hotkeyListener(e: KeyboardEvent): void {
|
function hotkeyListener(e: KeyboardEvent): void {
|
||||||
@@ -2392,7 +2467,7 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||||||
*/
|
*/
|
||||||
function addCSS() {
|
function addCSS() {
|
||||||
if (!isFirefoxOrSafari() && Config.config.invidiousInstances.includes(new URL(document.URL).hostname)) {
|
if (!isFirefoxOrSafari() && Config.config.invidiousInstances.includes(new URL(document.URL).hostname)) {
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
const onLoad = () => {
|
||||||
const head = document.getElementsByTagName("head")[0];
|
const head = document.getElementsByTagName("head")[0];
|
||||||
|
|
||||||
for (const file of utils.css) {
|
for (const file of utils.css) {
|
||||||
@@ -2404,7 +2479,13 @@ function addCSS() {
|
|||||||
|
|
||||||
head.appendChild(fileref);
|
head.appendChild(fileref);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
onLoad();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", onLoad);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2489,7 +2570,9 @@ function setCategoryColorCSSVariables() {
|
|||||||
if (!styleContainer) {
|
if (!styleContainer) {
|
||||||
styleContainer = document.createElement("style");
|
styleContainer = document.createElement("style");
|
||||||
styleContainer.id = "sbCategoryColorStyle";
|
styleContainer.id = "sbCategoryColorStyle";
|
||||||
document.head.appendChild(styleContainer)
|
|
||||||
|
const head = (document.head || document.documentElement);
|
||||||
|
head.appendChild(styleContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
let css = ":root {"
|
let css = ":root {"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import { init } from "./maze-utils/injected/document";
|
import { init } from "../maze-utils/src/injected/document";
|
||||||
|
|
||||||
init();
|
init();
|
||||||
10
src/help.ts
10
src/help.ts
@@ -1,10 +1,14 @@
|
|||||||
import { localizeHtmlPage } from "./maze-utils/setup";
|
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
|
|
||||||
import { waitFor } from "./maze-utils";
|
import { waitFor } from "../maze-utils/src";
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', init);
|
if (document.readyState === "complete") {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
localizeHtmlPage();
|
localizeHtmlPage();
|
||||||
|
|||||||
@@ -11,8 +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/formating";
|
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||||
import { findValidElement } from "../maze-utils/dom";
|
import { findValidElement } from "../../maze-utils/src/dom";
|
||||||
|
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
||||||
|
|
||||||
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
|
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
|
||||||
const MIN_CHAPTER_SIZE = 0.003;
|
const MIN_CHAPTER_SIZE = 0.003;
|
||||||
@@ -201,6 +202,10 @@ class PreviewBar {
|
|||||||
childList: true,
|
childList: true,
|
||||||
subtree: true,
|
subtree: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addCleanupListener(() => {
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
||||||
@@ -626,6 +631,11 @@ class PreviewBar {
|
|||||||
childListObserver.observe(this.originalChapterBar, {
|
childListObserver.observe(this.originalChapterBar, {
|
||||||
childList: true
|
childList: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addCleanupListener(() => {
|
||||||
|
attributeObserver.disconnect();
|
||||||
|
childListObserver.disconnect();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateChapterAllMutation(originalChapterBar: HTMLElement, progressBar: HTMLElement, firstUpdate = false): void {
|
private updateChapterAllMutation(originalChapterBar: HTMLElement, progressBar: HTMLElement, firstUpdate = false): void {
|
||||||
@@ -774,9 +784,12 @@ class PreviewBar {
|
|||||||
if (!Config.config.showSegmentNameInChapterBar
|
if (!Config.config.showSegmentNameInChapterBar
|
||||||
|| ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) {
|
|| ((!segments || segments.length <= 0) && submittingSegments?.length <= 0)) {
|
||||||
const chaptersContainer = this.getChaptersContainer();
|
const chaptersContainer = this.getChaptersContainer();
|
||||||
const chapterButton = this.getChapterButton(chaptersContainer);
|
if (chaptersContainer) {
|
||||||
if (chapterButton && chapterButton.classList.contains("ytp-chapter-container-disabled")) {
|
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
|
||||||
chaptersContainer.style.display = "none";
|
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||||
|
|
||||||
|
chapterTitle.style.removeProperty("display");
|
||||||
|
chaptersContainer.classList.remove("sponsorblock-chapter-visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
@@ -802,7 +815,7 @@ class PreviewBar {
|
|||||||
|
|
||||||
if (chaptersContainer) {
|
if (chaptersContainer) {
|
||||||
if (segments.length > 0) {
|
if (segments.length > 0) {
|
||||||
chaptersContainer.style.removeProperty("display");
|
chaptersContainer.classList.add("sponsorblock-chapter-visible");
|
||||||
|
|
||||||
const chosenSegment = segments.sort((a, b) => {
|
const chosenSegment = segments.sort((a, b) => {
|
||||||
if (a.actionType === ActionType.Chapter && b.actionType !== ActionType.Chapter) {
|
if (a.actionType === ActionType.Chapter && b.actionType !== ActionType.Chapter) {
|
||||||
@@ -819,11 +832,11 @@ class PreviewBar {
|
|||||||
chapterButton.disabled = false;
|
chapterButton.disabled = false;
|
||||||
|
|
||||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||||
chapterTitle.innerText = "";
|
chapterTitle.style.display = "none";
|
||||||
|
|
||||||
const chapterCustomText = (chapterTitle.querySelector(".sponsorChapterText") || (() => {
|
const chapterCustomText = (chapterTitle.parentElement.querySelector(".sponsorChapterText") || (() => {
|
||||||
const elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
chapterTitle.appendChild(elem);
|
chapterTitle.parentElement.insertBefore(elem, chapterTitle);
|
||||||
elem.classList.add("sponsorChapterText");
|
elem.classList.add("sponsorChapterText");
|
||||||
return elem;
|
return elem;
|
||||||
})()) as HTMLDivElement;
|
})()) as HTMLDivElement;
|
||||||
@@ -854,11 +867,10 @@ class PreviewBar {
|
|||||||
} else {
|
} else {
|
||||||
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
|
chaptersContainer.querySelector(".sponsorChapterText")?.remove();
|
||||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||||
if (chapterTitle.innerText === "") {
|
|
||||||
chaptersContainer.style.display = "none";
|
chapterTitle.style.removeProperty("display");
|
||||||
} else {
|
chaptersContainer.classList.remove("sponsorblock-chapter-visible");
|
||||||
chaptersContainer.style.removeProperty("display");
|
|
||||||
}
|
|
||||||
this.chapterVote.setVisibility(false);
|
this.chapterVote.setVisibility(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Config from "../config";
|
|||||||
import { SponsorTime } from "../types";
|
import { SponsorTime } from "../types";
|
||||||
import { getSkippingText } from "../utils/categoryUtils";
|
import { getSkippingText } from "../utils/categoryUtils";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
import { keybindToString } from "../maze-utils/config";
|
import { keybindToString } from "../../maze-utils/src/config";
|
||||||
|
|
||||||
export interface SkipButtonControlBarProps {
|
export interface SkipButtonControlBarProps {
|
||||||
skip: (segment: SponsorTime) => void;
|
skip: (segment: SponsorTime) => void;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../maze-utils/src/
|
|
||||||
@@ -13,10 +13,10 @@ import CategoryChooser from "./render/CategoryChooser";
|
|||||||
import UnsubmittedVideos from "./render/UnsubmittedVideos";
|
import UnsubmittedVideos from "./render/UnsubmittedVideos";
|
||||||
import KeybindComponent from "./components/options/KeybindComponent";
|
import KeybindComponent from "./components/options/KeybindComponent";
|
||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
import { localizeHtmlPage } from "./maze-utils/setup";
|
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||||
import { StorageChangesObject } from "./maze-utils/config";
|
import { StorageChangesObject } from "../maze-utils/src/config";
|
||||||
import { getHash } from "./maze-utils/hash";
|
import { getHash } from "../maze-utils/src/hash";
|
||||||
import { isFirefoxOrSafari } from "./maze-utils";
|
import { isFirefoxOrSafari } from "../maze-utils/src";
|
||||||
import { isDeArrowInstalled } from "./utils/crossExtension";
|
import { isDeArrowInstalled } from "./utils/crossExtension";
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
let embed = false;
|
let embed = false;
|
||||||
@@ -24,7 +24,11 @@ let embed = false;
|
|||||||
const categoryChoosers: CategoryChooser[] = [];
|
const categoryChoosers: CategoryChooser[] = [];
|
||||||
const unsubmittedVideos: UnsubmittedVideos[] = [];
|
const unsubmittedVideos: UnsubmittedVideos[] = [];
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', init);
|
if (document.readyState === "complete") {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
localizeHtmlPage();
|
localizeHtmlPage();
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import Config from "./config";
|
import Config from "./config";
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
import { localizeHtmlPage } from "./maze-utils/setup";
|
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
// This is needed, if Config is not imported before Utils, things break.
|
// This is needed, if Config is not imported before Utils, things break.
|
||||||
// Probably due to cyclic dependencies
|
// Probably due to cyclic dependencies
|
||||||
Config.config;
|
Config.config;
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', init);
|
if (document.readyState === "complete") {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
localizeHtmlPage();
|
localizeHtmlPage();
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ import {
|
|||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "./utils/animationUtils";
|
||||||
import { shortCategoryName } from "./utils/categoryUtils";
|
import { shortCategoryName } from "./utils/categoryUtils";
|
||||||
import { localizeHtmlPage } from "./maze-utils/setup";
|
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||||
import { exportTimes } from "./utils/exporter";
|
import { exportTimes } from "./utils/exporter";
|
||||||
import GenericNotice from "./render/GenericNotice";
|
import GenericNotice from "./render/GenericNotice";
|
||||||
import { getErrorMessage, getFormattedTime } from "./maze-utils/formating";
|
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
||||||
import { StorageChangesObject } from "./maze-utils/config";
|
import { StorageChangesObject } from "../maze-utils/src/config";
|
||||||
import { getHash } from "./maze-utils/hash";
|
import { getHash } from "../maze-utils/src/hash";
|
||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import Config from "../config";
|
|||||||
import { VoteResponse } from "../messageTypes";
|
import { VoteResponse } from "../messageTypes";
|
||||||
import { Category, SegmentUUID, SponsorTime } from "../types";
|
import { Category, SegmentUUID, SponsorTime } from "../types";
|
||||||
import { Tooltip } from "./Tooltip";
|
import { Tooltip } from "./Tooltip";
|
||||||
import { waitFor } from "../maze-utils";
|
import { waitFor } from "../../maze-utils/src";
|
||||||
import { getYouTubeTitleNode } from "../maze-utils/elements";
|
import { getYouTubeTitleNode } from "../../maze-utils/src/elements";
|
||||||
|
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
||||||
|
|
||||||
const id = "categoryPill";
|
const id = "categoryPill";
|
||||||
|
|
||||||
@@ -24,6 +25,12 @@ export class CategoryPill {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.ref = React.createRef();
|
this.ref = React.createRef();
|
||||||
|
|
||||||
|
addCleanupListener(() => {
|
||||||
|
if (this.mutationObserver) {
|
||||||
|
this.mutationObserver.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async attachToPage(onMobileYouTube: boolean, onInvidious: boolean,
|
async attachToPage(onMobileYouTube: boolean, onInvidious: boolean,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export default class GenericNotice {
|
|||||||
const referenceNode = options.referenceNode ?? utils.findReferenceNode();
|
const referenceNode = options.referenceNode ?? utils.findReferenceNode();
|
||||||
|
|
||||||
this.noticeElement = document.createElement("div");
|
this.noticeElement = document.createElement("div");
|
||||||
|
this.noticeElement.className = "sponsorSkipNoticeContainer";
|
||||||
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
||||||
|
|
||||||
referenceNode.prepend(this.noticeElement);
|
referenceNode.prepend(this.noticeElement);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class SkipNotice {
|
|||||||
idSuffix += amountOfPreviousNotices;
|
idSuffix += amountOfPreviousNotices;
|
||||||
|
|
||||||
this.noticeElement = document.createElement("div");
|
this.noticeElement = document.createElement("div");
|
||||||
|
this.noticeElement.className = "sponsorSkipNoticeContainer";
|
||||||
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
|
||||||
|
|
||||||
referenceNode.prepend(this.noticeElement);
|
referenceNode.prepend(this.noticeElement);
|
||||||
|
|||||||
55
src/svg-icons/sb_svg.tsx
Normal file
55
src/svg-icons/sb_svg.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface SbIconProps {
|
||||||
|
id?: string;
|
||||||
|
fill?: string;
|
||||||
|
className?: string;
|
||||||
|
width?: string;
|
||||||
|
height?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SbSvg({
|
||||||
|
id = "",
|
||||||
|
fill = "#ff0000",
|
||||||
|
className = "",
|
||||||
|
onClick
|
||||||
|
}: SbIconProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 565.15 568"
|
||||||
|
id={id}
|
||||||
|
className={className}
|
||||||
|
onClick={() => onClick?.() } >
|
||||||
|
<g
|
||||||
|
id="Layer_2"
|
||||||
|
data-name="Layer 2">
|
||||||
|
<g
|
||||||
|
id="Layer_1-2"
|
||||||
|
data-name="Layer 1"
|
||||||
|
style={{
|
||||||
|
fill
|
||||||
|
}}>
|
||||||
|
<path
|
||||||
|
d="M282.58,568a65,65,0,0,1-34.14-9.66C95.41,463.94,2.54,300.46,0,121A64.91,64.91,0,0,1,34,62.91a522.56,522.56,0,0,1,497.16,0,64.91,64.91,0,0,1,34,58.12c-2.53,179.43-95.4,342.91-248.42,437.3A65,65,0,0,1,282.58,568Zm0-548.31A502.24,502.24,0,0,0,43.4,80.22a45.27,45.27,0,0,0-23.7,40.53c2.44,172.67,91.81,330,239.07,420.83a46.19,46.19,0,0,0,47.61,0C453.64,450.73,543,293.42,545.45,120.75a45.26,45.26,0,0,0-23.7-40.54A502.26,502.26,0,0,0,282.58,19.69Z"
|
||||||
|
id="path8"
|
||||||
|
style={{
|
||||||
|
fill
|
||||||
|
}} />
|
||||||
|
<path
|
||||||
|
style={{
|
||||||
|
fill
|
||||||
|
}}
|
||||||
|
d="M 284.70508 42.693359 A 479.9 479.9 0 0 0 54.369141 100.41992 A 22.53 22.53 0 0 0 42.669922 120.41992 C 45.069922 290.25992 135.67008 438.63977 270.83008 522.00977 A 22.48 22.48 0 0 0 294.32031 522.00977 C 429.48031 438.63977 520.08047 290.25992 522.48047 120.41992 A 22.53 22.53 0 0 0 510.7793 100.41992 A 479.9 479.9 0 0 0 284.70508 42.693359 z M 220.41016 145.74023 L 411.2793 255.93945 L 220.41016 366.14062 L 220.41016 145.74023 z "
|
||||||
|
id="path10" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<polygon style={{
|
||||||
|
fill: "#fff"
|
||||||
|
}}
|
||||||
|
points="411.28 255.94 220.41 145.74 220.41 366.14 411.28 255.94"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -99,8 +99,8 @@ export interface Registration {
|
|||||||
message: string;
|
message: string;
|
||||||
id: string;
|
id: string;
|
||||||
allFrames: boolean;
|
allFrames: boolean;
|
||||||
js: browser.extensionTypes.ExtensionFileOrCode[];
|
js: string[];
|
||||||
css: browser.extensionTypes.ExtensionFileOrCode[];
|
css: string[];
|
||||||
matches: string[];
|
matches: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
src/utils.ts
33
src/utils.ts
@@ -1,12 +1,12 @@
|
|||||||
import Config, { VideoDownvotes } from "./config";
|
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/hash";
|
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
import { isFirefoxOrSafari, waitFor } from "./maze-utils";
|
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||||
import { findValidElementFromSelector } from "./maze-utils/dom";
|
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
||||||
import { FetchResponse, sendRequestToCustomServer } from "./maze-utils/background-request-proxy"
|
import { FetchResponse, sendRequestToCustomServer } from "../maze-utils/src/background-request-proxy"
|
||||||
import { isSafari } from "./maze-utils/config";
|
import { isSafari } from "../maze-utils/src/config";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
|
|
||||||
@@ -47,9 +47,13 @@ export default class Utils {
|
|||||||
* @param {CallableFunction} callback
|
* @param {CallableFunction} callback
|
||||||
*/
|
*/
|
||||||
setupExtraSitePermissions(callback: (granted: boolean) => void): void {
|
setupExtraSitePermissions(callback: (granted: boolean) => void): void {
|
||||||
let permissions = ["webNavigation"];
|
const permissions = [];
|
||||||
if (!isSafari()) permissions.push("declarativeContent");
|
if (!isFirefoxOrSafari()) {
|
||||||
if (isFirefoxOrSafari() && !isSafari()) permissions = [];
|
permissions.push("declarativeContent");
|
||||||
|
}
|
||||||
|
if (!isFirefoxOrSafari() || isSafari()) {
|
||||||
|
permissions.push("webNavigation");
|
||||||
|
}
|
||||||
|
|
||||||
chrome.permissions.request({
|
chrome.permissions.request({
|
||||||
origins: this.getPermissionRegex(),
|
origins: this.getPermissionRegex(),
|
||||||
@@ -73,21 +77,12 @@ 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 firefoxJS = [];
|
|
||||||
for (const file of this.js) {
|
|
||||||
firefoxJS.push({file});
|
|
||||||
}
|
|
||||||
const firefoxCSS = [];
|
|
||||||
for (const file of this.css) {
|
|
||||||
firefoxCSS.push({file});
|
|
||||||
}
|
|
||||||
|
|
||||||
const registration: Registration = {
|
const registration: Registration = {
|
||||||
message: "registerContentScript",
|
message: "registerContentScript",
|
||||||
id: "invidious",
|
id: "invidious",
|
||||||
allFrames: true,
|
allFrames: true,
|
||||||
js: firefoxJS,
|
js: this.js,
|
||||||
css: firefoxCSS,
|
css: this.css,
|
||||||
matches: this.getPermissionRegex()
|
matches: this.getPermissionRegex()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
|
|
||||||
export function runCompatibilityChecks() {
|
export function runCompatibilityChecks() {
|
||||||
if (Config.config.showZoomToFillError) {
|
if (Config.config.showZoomToFillError2 && document.URL.includes("watch?v=")) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const zoomToFill = document.querySelector(".zoomtofillBtn");
|
const zoomToFill = document.querySelector(".zoomtofillBtn");
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ export function runCompatibilityChecks() {
|
|||||||
alert(chrome.i18n.getMessage("zoomToFillUnsupported"));
|
alert(chrome.i18n.getMessage("zoomToFillUnsupported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.config.showZoomToFillError = false;
|
Config.config.showZoomToFillError2 = false;
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as CompileConfig from "../../config.json";
|
import * as CompileConfig from "../../config.json";
|
||||||
|
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { isSafari } from "../maze-utils/config";
|
import { isSafari } from "../../maze-utils/src/config";
|
||||||
import { isFirefoxOrSafari } from "../maze-utils";
|
import { isFirefoxOrSafari } from "../../maze-utils/src";
|
||||||
|
|
||||||
export function isDeArrowInstalled(): Promise<boolean> {
|
export function isDeArrowInstalled(): Promise<boolean> {
|
||||||
if (Config.config.deArrowInstalled) {
|
if (Config.config.deArrowInstalled) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../types";
|
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../types";
|
||||||
import { shortCategoryName } from "./categoryUtils";
|
import { shortCategoryName } from "./categoryUtils";
|
||||||
import * as CompileConfig from "../../config.json";
|
import * as CompileConfig from "../../config.json";
|
||||||
import { getFormattedTime, getFormattedTimeToSeconds } from "../maze-utils/formating";
|
import { getFormattedTime, getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||||
import { generateUserID } from "../maze-utils/setup";
|
import { generateUserID } from "../../maze-utils/src/setup";
|
||||||
|
|
||||||
const inTest = typeof chrome === "undefined";
|
const inTest = typeof chrome === "undefined";
|
||||||
|
|
||||||
|
|||||||
8
src/utils/pageCleaner.ts
Normal file
8
src/utils/pageCleaner.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export function cleanPage() {
|
||||||
|
// For live-updates
|
||||||
|
if (document.readyState === "complete") {
|
||||||
|
for (const element of document.querySelectorAll("#categoryPillParent, .playerButton, .sponsorThumbnailLabel, #submissionNoticeContainer, .sponsorSkipNoticeContainer, #sponsorBlockPopupContainer, .skipButtonControlBarContainer, #previewbar, .sponsorBlockChapterBar")) {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types";
|
import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types";
|
||||||
import { getFormattedTimeToSeconds } from "../maze-utils/formating";
|
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
export function getControls(): HTMLElement {
|
export function getControls(): HTMLElement {
|
||||||
const controlsSelectors = [
|
const controlsSelectors = [
|
||||||
@@ -93,4 +93,8 @@ export function getExistingChapters(currentVideoID: VideoID, duration: number):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return chapters;
|
return chapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPlayingPlaylist() {
|
||||||
|
return !!document.URL.includes("&list=");
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../maze-utils/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/thumbnailManagement";
|
import { setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
|
||||||
|
|
||||||
export async function labelThumbnails(thumbnails: HTMLImageElement[]): Promise<void> {
|
export async function labelThumbnails(thumbnails: HTMLImageElement[]): Promise<void> {
|
||||||
await Promise.all(thumbnails.map((t) => labelThumbnail(t)));
|
await Promise.all(thumbnails.map((t) => labelThumbnail(t)));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Category, CategorySkipOption, VideoID } from "../types";
|
import { Category, CategorySkipOption, VideoID } from "../types";
|
||||||
import { getHash } from "../maze-utils/hash";
|
import { getHash } from "../../maze-utils/src/hash";
|
||||||
import Utils from "../utils";
|
import Utils from "../utils";
|
||||||
import { logWarn } from "./logger";
|
import { logWarn } from "./logger";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { objectToURI } from "../maze-utils";
|
import { objectToURI } from "../../maze-utils/src";
|
||||||
import { getHash } from "../maze-utils/hash";
|
import { getHash } from "../../maze-utils/src/hash";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
|
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
|
||||||
import { ContentContainer } from "../types";
|
import { ContentContainer } from "../types";
|
||||||
|
|||||||
Reference in New Issue
Block a user