Compare commits

...

100 Commits

Author SHA1 Message Date
Ajay Ramachandran
457bd15e17 Merge pull request #298 from ajayyy/experimental
Options Improvements
2020-03-10 23:24:36 -04:00
Ajay Ramachandran
cc1b8ee499 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into experimental 2020-03-10 23:23:30 -04:00
Ajay Ramachandran
4a9ed1348e Increase version number 2020-03-10 23:23:10 -04:00
Ajay Ramachandran
5595420be6 Removed redundant if statement 2020-03-10 23:22:57 -04:00
Ajay Ramachandran
191e9ceb6f Makes sure the playing and play listener both don't get called at the same time.
This led to double notices.
2020-03-10 23:22:17 -04:00
Ajay Ramachandran
1e26faa57c Update README.md 2020-03-10 12:11:08 -04:00
Ajay Ramachandran
030256c9e1 Enable checkbox when the permission prompt is successful 2020-03-10 02:14:00 -04:00
Ajay Ramachandran
77eff12d9b Merge pull request #261 from OfficialNoob/patch-1
Made decodeStoredItem detect item type
2020-03-10 01:21:10 -04:00
Ajay Ramachandran
2967fce013 Update README.md 2020-03-10 00:57:44 -04:00
Ajay Ramachandran
db1def623a Update README.md 2020-03-10 00:56:58 -04:00
Ajay Ramachandran
140e324bf1 Update README.md 2020-03-10 00:55:18 -04:00
Ajay Ramachandran
f0bf051259 Properly ask for permissions when changing the server address 2020-03-10 00:48:53 -04:00
Ajay Ramachandran
2ec47d52cd Added basic options import/export 2020-03-10 00:33:50 -04:00
Ajay Ramachandran
50002cfbbd Fix github token using the wrong key in release workflow 2020-03-09 23:10:59 -04:00
Ajay Ramachandran
6ccd4b8b37 Merge pull request #297 from ajayyy/experimental
Fix non zero second skips
2020-03-09 23:05:45 -04:00
Ajay Ramachandran
c0894afff9 Prevent manual skipping votes from affecting to UI and happening when auto vote is off 2020-03-09 23:03:24 -04:00
Ajay Ramachandran
09f244150c Fixed skipping for non zero second sponsors.
Also now using the playing event to fix issues with mobile YouTube skipping.
2020-03-09 23:00:39 -04:00
Ajay Ramachandran
efec8b320c Increase version num 2020-03-09 18:38:02 -04:00
Ajay Ramachandran
e6ea9f77e9 Fixed skip scheduling for auto skip 2020-03-09 18:34:33 -04:00
Ajay Ramachandran
0813aa4ba3 Prevent release workflow from running multiple times 2020-03-09 18:30:57 -04:00
Ajay Ramachandran
ba575f6b8d Merge pull request #296 from ajayyy/experimental
Prevent manual skips from triggering a view and improved skip schedule for manual skip
2020-03-09 18:20:11 -04:00
Ajay Ramachandran
ff9b2338e0 Fixed scheduling being wrong when manual skip is enabled 2020-03-09 18:12:05 -04:00
Ajay Ramachandran
d2bb4b38e3 Increased version number 2020-03-09 18:07:29 -04:00
Ajay Ramachandran
760c08dd0c Prevent manual skips from triggering a view 2020-03-09 18:07:06 -04:00
Ajay Ramachandran
50517eb462 Switched upload to release action 2020-03-09 11:15:29 -04:00
Ajay Ramachandran
e77425c21e Merge pull request #294 from ajayyy/experimental
Another potential fix for zero second sponsor freezing
2020-03-09 11:04:26 -04:00
Ajay Ramachandran
f63abb053d Revert to only using workflows 2020-03-09 11:02:14 -04:00
Ajay Ramachandran
7b5703aa04 Fixed action format 2020-03-09 10:56:06 -04:00
Ajay Ramachandran
d641066312 Checkout in CI 2020-03-09 10:52:43 -04:00
Ajay Ramachandran
44ca8d47d8 Moved CI into the right place 2020-03-09 10:51:26 -04:00
Ajay Ramachandran
d5f41bf4ad Fixed CI 2020-03-09 10:49:50 -04:00
Ajay Ramachandran
73e8926444 Start skip schedule from skip time to prevent slow video updates from breaking it. 2020-03-09 10:43:13 -04:00
Ajay Ramachandran
5ad694af65 Increase version number 2020-03-08 23:26:46 -04:00
Ajay Ramachandran
d7f7acb219 Fixed release action 2020-03-08 23:20:41 -04:00
Ajay Ramachandran
b4be51333a Merge pull request #293 from ajayyy/experimental
Fixed double sponsor skip for zero second sponsors
2020-03-08 23:17:48 -04:00
Ajay Ramachandran
ae94811a00 Increase version number 2020-03-08 23:16:50 -04:00
Ajay Ramachandran
a484f2f2cc Fixed double sponsor skip for zero second sponsors 2020-03-08 23:16:09 -04:00
Ajay Ramachandran
9cce4e734d Merge pull request #292 from grishka/russian
Fix Russian translation and add missing strings
2020-03-07 12:34:48 -05:00
Grishka
3c63644213 fix 2020-03-07 13:45:56 +03:00
Grishka
ad25bc34de Add missing Russian strings 2020-03-07 13:37:58 +03:00
Ajay Ramachandran
0241e15691 Only seek if the video is not paused 2020-02-28 15:06:08 -05:00
Ajay Ramachandran
af9a6b8a84 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into experimental 2020-02-28 14:09:21 -05:00
Ajay Ramachandran
329b188435 Checkout before using other CI 2020-02-27 22:23:33 -05:00
Ajay Ramachandran
2e49bb73c5 Merge pull request #290 from ajayyy/experimental
Potentially fixed zero second skip spam
2020-02-27 22:21:33 -05:00
Ajay Ramachandran
5158020293 Potentially fixed zero second skip spam 2020-02-27 22:20:30 -05:00
Ajay Ramachandran
feaf80ad1e Merge pull request #289 from Joe-Dowd/messages-typo
Typo in en messages locale "skipeed -> skipped"
2020-02-25 15:14:01 -05:00
Joe Dowd
7fbd89159e Typo in en messages locale skipped 2020-02-25 18:25:28 +00:00
Ajay Ramachandran
716861da18 Fixed beta builds 2020-02-23 20:49:17 -05:00
Ajay Ramachandran
d0a34d423c Fix release CI 2020-02-23 20:42:12 -05:00
Ajay Ramachandran
adfba72f19 Merge pull request #286 from ajayyy/experimental
Mobile fix + mobile update notice
2020-02-23 20:40:48 -05:00
Ajay Ramachandran
f00337c376 Increase version number. 2020-02-23 20:39:29 -05:00
Ajay Ramachandran
737a023b65 Added mobile support message. 2020-02-23 20:39:13 -05:00
Ajay Ramachandran
5551344355 Fixed mobile notice zoom on Firefox 2020-02-23 16:14:18 -05:00
Ajay Ramachandran
07f64382fb Merge pull request #285 from cherryblossom000/patch-1
Fix "0 sponsor" in popup
2020-02-23 11:21:21 -05:00
Ajay Ramachandran
1c7cde2a19 Changed to !== 2020-02-23 11:20:21 -05:00
cherryblossom
8510a7f3d8 Fix popup saying "0 sponsor" 2020-02-23 17:03:18 +11:00
Ajay Ramachandran
db60b11a17 Merge pull request #284 from ajayyy/experimental
CI Improvements
2020-02-20 15:38:35 -05:00
Ajay Ramachandran
6a212b762a Fixed invalid release workflow 2020-02-20 12:21:30 -05:00
Ajay Ramachandran
c8ec2922cf Added makedir to CI 2020-02-20 12:17:51 -05:00
Ajay Ramachandran
b629b7d333 Added basic release workflow 2020-02-20 12:14:01 -05:00
Ajay Ramachandran
514a8b62d6 Added beta build to CI 2020-02-20 11:39:06 -05:00
Ajay Ramachandran
cd11618a5d Merge pull request #283 from ajayyy/experimental
New skipping mechanism fixes
2020-02-20 11:25:32 -05:00
Ajay Ramachandran
8be3cb157a Increased version number 2020-02-20 11:23:44 -05:00
Ajay Ramachandran
4ca57cc025 Fixed preview sponsors not skipping when only they are there. 2020-02-19 13:45:00 -05:00
Ajay Ramachandran
397bcc94c5 Remove redundant code 2020-02-19 13:35:05 -05:00
Ajay Ramachandran
8b28bccfd7 Run dev now uses dev build. 2020-02-19 12:57:22 -05:00
Ajay Ramachandran
c6107057d9 Firefox dev environment now loads Firefox uBlock Origin 2020-02-19 12:54:58 -05:00
Ajay Ramachandran
ab2a9530e9 Sped up zero second sponsors a tiny bit 2020-02-19 12:41:22 -05:00
Ajay Ramachandran
bfc771bd99 Removed unused variables 2020-02-19 12:37:39 -05:00
Ajay Ramachandran
e75e588755 Sped up direct links a tiny bit. 2020-02-19 12:37:17 -05:00
Ajay Ramachandran
0266bb49ca Fixed typo 2020-02-19 12:35:10 -05:00
Ajay Ramachandran
9e693fd555 Update README.md 2020-02-19 12:33:48 -05:00
Ajay Ramachandran
1f30b9ec84 Merge pull request #280 from ajayyy/mobile-youtube
Mobile YouTube support + Precise skipping
2020-02-19 11:40:07 -05:00
Ajay Ramachandran
50862e3c03 Increased version number 2020-02-19 11:25:14 -05:00
Ajay Ramachandran
20e90bbc34 Fixed schedule not updating when new sponsors arrive 2020-02-19 00:54:30 -05:00
Ajay Ramachandran
2e212e6d10 Improved mobile preview bar 2020-02-19 00:14:08 -05:00
Ajay Ramachandran
2039bfa081 Made buttons on mobile not close the menu. 2020-02-19 00:10:05 -05:00
Ajay Ramachandran
7dc8a99247 Added button support to mobile. 2020-02-19 00:00:22 -05:00
Ajay Ramachandran
1b25ea7f95 Shrunk notice on mobile 2020-02-18 19:29:20 -05:00
Ajay Ramachandran
1869382166 Fixed popup issues. 2020-02-18 19:03:34 -05:00
Ajay Ramachandran
d58cd639c7 Added zero second preview sponsors. 2020-02-18 18:45:33 -05:00
Ajay Ramachandran
6cd2d4cf83 Added back whitelist support 2020-02-18 18:44:06 -05:00
Ajay Ramachandran
b681f5abd9 Added support for preview sponsors in new skipping method. 2020-02-18 18:43:45 -05:00
Ajay Ramachandran
5b2a0feccf Switched to new skipping mechanic 2020-02-18 18:29:02 -05:00
Ajay Ramachandran
cd58f6bc73 Skip notice improvement. 2020-02-18 15:57:40 -05:00
Ajay Ramachandran
aeabf806ac Added duration change listener check to prevent mid-video zero second skips.
Sometimes the video gets reset to zero seconds for a few milliseconds, this should not trigger a skip.

Resolves https://github.com/ajayyy/SponsorBlock/issues/183
2020-02-18 15:03:55 -05:00
Ajay Ramachandran
af7ba31c2f Remove logging. 2020-02-18 14:40:40 -05:00
Ajay Ramachandran
5b962b1b9d Merge pull request #281 from ajayyy/experimental
Rename CI artifacts
2020-02-17 15:24:49 -05:00
Ajay Ramachandran
219a7ba8c3 Added preview bar to mobile 2020-02-17 15:10:50 -05:00
Ajay Ramachandran
933babb4e6 Added mobile YouTube site to the whitelist. 2020-02-17 11:32:00 -05:00
Ajay Ramachandran
023ba2e051 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into experimental 2020-02-17 11:24:36 -05:00
Ajay Ramachandran
8d82a6a3e6 Fixed data old format migration. 2020-02-14 23:20:11 -05:00
Ajay Ramachandran
88a8fda566 Moved window.SB creation for security reasons. 2020-02-14 23:16:01 -05:00
Ajay Ramachandran
1c833a8b1d Rename CI artifacts. 2020-02-11 21:04:41 -05:00
Official Noob
995fe072bd Merge pull request #1 from ajayyy/master
Update
2020-02-09 22:15:27 +00:00
Ajay Ramachandran
8cdbebd6de Added the config as a global variable. 2020-02-08 20:16:26 -05:00
Ajay Ramachandran
94af8ab301 Prevent all strings from being parsed as JSON. 2020-02-08 20:15:49 -05:00
Ajay Ramachandran
be3a4a4e91 Added support for old format. 2020-02-08 20:08:34 -05:00
Official Noob
1c17464c94 config => defaults 2020-02-04 23:29:11 +00:00
Official Noob
8896c5707a Made decodeStoredItem detect item type
Not tested because SB.config cant be used anymore :(
2020-02-04 22:16:40 +00:00
19 changed files with 966 additions and 215 deletions

View File

@@ -21,14 +21,42 @@ jobs:
run: npm run build:chrome
- uses: actions/upload-artifact@v1
with:
name: Chrome Extension
name: ChromeExtension
path: dist
- run: mkdir ./builds
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/ChromeExtension.zip ./dist
# Create Firefox artifacts
- name: Create Firefox artifacts
run: npm run build:firefox
- uses: actions/upload-artifact@v1
with:
name: Firefox Extension
name: FirefoxExtension
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
# Create Beta artifacts (Builds with the name changed to beta)
- name: Create Chrome Beta artifacts
run: npm run build:chrome -- --env.stream=beta
- uses: actions/upload-artifact@v1
with:
name: ChromeExtensionBeta
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
- name: Create Firefox Beta artifacts
run: npm run build:firefox -- --env.stream=beta
- uses: actions/upload-artifact@v1
with:
name: FirefoxExtensionBeta
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist

78
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: Upload Release Build
on:
release:
types: [published]
jobs:
build:
name: Upload Release
runs-on: ubuntu-latest
steps:
# Initialization
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- run: npm install
- name: Copy configuration
run: cp config.json.example config.json
# Create Chrome artifacts
- name: Create Chrome artifacts
run: npm run build:chrome
- uses: actions/upload-artifact@v1
with:
name: ChromeExtension
path: dist
- run: mkdir ./builds
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/ChromeExtension.zip ./dist
# Create Firefox artifacts
- name: Create Firefox artifacts
run: npm run build:firefox
- uses: actions/upload-artifact@v1
with:
name: FirefoxExtension
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
# Create Beta artifacts (Builds with the name changed to beta)
- name: Create Chrome Beta artifacts
run: npm run build:chrome -- --env.stream=beta
- uses: actions/upload-artifact@v1
with:
name: ChromeExtensionBeta
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
- name: Create Firefox Beta artifacts
run: npm run build:firefox -- --env.stream=beta
- uses: actions/upload-artifact@v1
with:
name: FirefoxExtensionBeta
path: dist
- uses: montudor/action-zip@v0.1.0
with:
args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist
# Upload each release asset
- name: Upload to release
uses: Shopify/upload-to-release@master
with:
args: builds/ChromeExtension.zip
env:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to release
uses: Shopify/upload-to-release@master
with:
args: builds/FirefoxExtension.zip
env:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -40,37 +40,35 @@ The backend server code is available here: https://github.com/ajayyy/SponsorBloc
It is a simple Sqlite database that will hold all the timing data.
To make sure that this project doesn't die, I have made the database publicly downloadable at https://api.sponsor.ajay.app/database.db. So, you can download a backup or get archive.org to take a backup for you if you want.
To make sure that this project doesn't die, I have made the database publicly downloadable at https://sponsor.ajay.app/database.db. You can download a backup or get archive.org to take a backup for you if you want.
Hopefully this project can be combined with projects like [this](https://github.com/Sponsoff/sponsorship_remover) and use this data to create a neural network to predict when sponsored segments happen. That project is sadly abandoned now, so I have decided to attempt to revive this idea.
The dataset and API are now being used in some [ports](https://github.com/ajayyy/SponsorBlock/wiki/Unofficial-Ports) as well as a [neural network](https://github.com/andrewzlee/NeuralBlock).
A [previous project](https://github.com/Sponsoff/sponsorship_remover) attempted to create a neural network to predict when sponsored segments happen. That project is sadly abandoned now, so I have decided to attempt to revive this idea starting from a crowd-sourced system instead.
# API
You can read the API docs [here](https://github.com/ajayyy/SponsorBlockServer#api-docs).
# Build Yourself
# Building
You can load this project as an unpacked extension. Make sure to rename the `config.json.example` file to `config.json` before installing.
There are also other build scripts available. Install `npm`, then run `npm install` in the repository to install dependencies.
There are also other build scripts available. Install `npm`, then run `npm install` in the repository.
Run `npm run build` to generate a Chrome extension.
Use `npm run build:firefox` to generate a Firefox extension.
The result is in `dist`. This can be loaded as an unpacked extension
## Developing with a clean profile
Run `npm run dev` to run the extension using a clean browser profile with hot reloading. Use `npm run dev:firefox` for Firefox. This uses [`web-ext run`](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#commands).
## Packing
Run `npm run build` to generate a packed Chrome extension.
Use `npm run build:firefox` to generate a Firefox extension.
The result is in `dist`.
# Credit
The awesome [Invidious API](https://github.com/omarroth/invidious/wiki/API) used to be used.
The awesome [Invidious API](https://github.com/omarroth/invidious/wiki/API) previously was used.
Original code from [YTSponsorSkip](https://github.com/OfficialNoob/YTSponsorSkip), but not much of the code is left.
Originally forked from [YTSponsorSkip](https://github.com/OfficialNoob/YTSponsorSkip), but zero code remains.
Some icons made by <a href="https://www.flaticon.com/authors/gregor-cresnar" title="Gregor Cresnar">Gregor Cresnar</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> and are licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a>

View File

@@ -0,0 +1,4 @@
{
"name": "BETA - SponsorBlock"
}

View File

@@ -0,0 +1,8 @@
{
"browser_specific_settings": {
"gecko": {
"id": "sponsorBlockerBETA@ajay.app"
}
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "__MSG_Name__",
"version": "1.2.13",
"version": "1.2.22",
"default_locale": "en",
"description": "__MSG_Description__",
"content_scripts": [{

View File

@@ -24,16 +24,19 @@
},
"scripts": {
"web-run": "npm run web-run:chrome",
"web-run:firefox": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm",
"web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/",
"web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium",
"build": "npm run build:chrome",
"build:chrome": "webpack --env.browser=chrome --config webpack/webpack.prod.js",
"build:firefox": "webpack --env.browser=firefox --config webpack/webpack.prod.js",
"build:dev": "npm run build:dev:chrome",
"build:dev:chrome": "webpack --env.browser=chrome --config webpack/webpack.dev.js",
"build:dev:firefox": "webpack --env.browser=firefox --config webpack/webpack.dev.js",
"build:watch": "npm run build:watch:chrome",
"build:watch:chrome": "webpack --env.browser=chrome --config webpack/webpack.dev.js --watch",
"build:watch:firefox": "webpack --env.browser=firefox --config webpack/webpack.dev.js --watch",
"dev": "npm run build && concurrently \"npm run web-run\" \"npm run build:watch\"",
"dev:firefox": "npm run build:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
"dev": "npm run build:dev && concurrently \"npm run web-run\" \"npm run build:watch\"",
"dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
"clean": "rimraf dist",
"test": "npx jest"
},

View File

@@ -160,7 +160,7 @@
"message": "Click the button below when the sponsorship starts and ends to record and\nsubmit it to the database."
},
"popupHint": {
"message": "Hint: Press the semicolon key while focused on a video report the start/end of a sponsor and quote to submit. (This can be changed in the options)"
"message": "Hint: Press the semicolon key while focused on a video to report the start/end of a sponsor and quote to submit. (This can be changed in the options)"
},
"lastTimes": {
"message": "Latest Sponsor Message Times Chosen"
@@ -226,7 +226,7 @@
"message": "Show Notice Again"
},
"longDescription": {
"message": "SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.",
"message": "SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that lets anyone submit the start and end times of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.",
"description": "Full description of the extension on the store pages."
},
"website": {
@@ -381,17 +381,11 @@
"whatAutoUpvote": {
"message": "With this enabled, the extension will upvote all submissions you view if you do not report them. If the notice is disabled, this will not occur."
},
"invidiousInfo1": {
"message": "Invidious (the 3rd party YouTube site) support has been added!"
},
"invidiousInfo2": {
"message": "You MUST enable it in the options for it to work."
},
"minDuration": {
"message": "Minimum duration (seconds):"
},
"minDurationDescription": {
"message": "Sponsor segments shorter than the set value will not be skipeed or show in the player."
"message": "Sponsor segments shorter than the set value will not be skipped or show in the player."
},
"shortCheck": {
"message": "The following submission is shorter than your minimum duration option. This could mean that this is already submitted, and just being ignored due to this option. Are you sure you would like to submit?"
@@ -428,5 +422,23 @@
},
"whatUnlistedCheck": {
"message": "This setting will significantly slow down SponsorBlock. Sponsor lookups require sending the video ID to the server. If you are concerned about unlisted video IDs being sent over the internet, enable this option."
},
"mobileUpdateInfo": {
"message": "m.youtube.com is now supported"
},
"exportOptions": {
"message": "Import/Export All Options"
},
"whatExportOptions": {
"message": "This is your entire configuration in JSON. This includes your userID, so be sure to share this wisely."
},
"setOptions": {
"message": "Set Options"
},
"exportOptionsWarning": {
"message": "Warning: Changing the options is permanent and can break your install. Are you sure you would like to do this? Make sure to backup your old one just in case."
},
"incorrectlyFormattedOptions": {
"message": "This JSON is not formatted correctly. Your options have not been changed."
}
}

View File

@@ -28,10 +28,10 @@
"message": "Канал добавлен в белый список!"
},
"Sponsor": {
"message": "Спонсор"
"message": "спонсора"
},
"Sponsors": {
"message": "Спонсоры"
"message": "спонсоров"
},
"Segment": {
"message": "спонсорская вставка"
@@ -123,5 +123,299 @@
},
"submitCheck": {
"message": "Вы уверены, что хотите отправить эту информацию?"
},
"whitelistChannel": {
"message": "Добавить канал в белый список"
},
"removeFromWhitelist": {
"message": "Удалить канал из белого списка"
},
"voteOnTime": {
"message": "Проголосовать за время спонсорской вставки"
},
"recordTimes": {
"message": "Записать время спонсорской вставки"
},
"soFarUHSubmited": {
"message": "На данный момент Вы отправили"
},
"savedPeopleFrom": {
"message": "Вы помогли людям сэкономить "
},
"viewLeaderboard": {
"message": "Посмотреть доску почёта"
},
"here": {
"message": "здесь"
},
"recordTimesDescription": {
"message": "Нажмите кнопку ниже, когда спонсорская вставка начинается и заканчивается, чтобы записать\nи отправить её в базу данных."
},
"popupHint": {
"message": "Подсказка: нажмите ;, чтобы сообщить начало/конец спонсорской вставки, и \", чтобы отправить. (Это можно изменить в настройках)"
},
"lastTimes": {
"message": "Последнее выбранное время спонсорской вставки"
},
"clearTimesButton": {
"message": "Очистить время"
},
"submitTimesButton": {
"message": "Отправить время"
},
"publicStats": {
"message": "Оно используется на публичной странице статистики, чтобы показать Ваш вклад. Её можно посмотреть "
},
"setUsername": {
"message": "Установить имя пользователя"
},
"discordAdvert": {
"message": "Присоединяйтесь к официальному серверу Discord, чтобы оставить предложения и обратную связь!"
},
"hideThis": {
"message": "Скрыть это"
},
"Options": {
"message": "Настройки"
},
"showButtons": {
"message": "Показывать кнопки в плеере YouTube"
},
"hideButtons": {
"message": "Скрыть кнопки в плеере YouTube"
},
"hideButtonsDescription": {
"message": "Эта настройка скрывает кнопки для отправки спонсорских вставок, которые появляются в плеере YouTube. Они могут раздражать\n некоторых. Вместо кнопок для отправки спонсорских вставок можно использовать это всплывающее окно. Чтобы скрыть\nуведомление, нажмите кнопку \"Не показывать снова\" в уведомлении. Вы всегда сможете включить эти настройки обратно."
},
"showInfoButton": {
"message": "Показывать кнопку информации в плеере YouTube"
},
"hideInfoButton": {
"message": "Скрыть кнопку информации в плеере YouTube"
},
"whatInfoButton": {
"message": "Эта кнопка открывает всплывающее окно на странице YouTube."
},
"hideDeleteButton": {
"message": "Скрыть кнопку удаления в плеере YouTube"
},
"showDeleteButton": {
"message": "Показывать кнопку удаления в плеере YouTube"
},
"whatDeleteButton": {
"message": "Эта кнопка позволяет Вам очистить все спонсорские вставки в плеере YouTube."
},
"disableViewTracking": {
"message": "Отключить отслеживание количества пропусков спонсорских вставок"
},
"enableViewTracking": {
"message": "Включить отслеживание количества пропусков спонсорских вставок"
},
"whatViewTracking": {
"message": "Эта возможность отслеживает, какие спонсорские вставки Вы пропустили, чтобы помочь пользователям узнать, насколько их\nвклад помог другим, и используется как метрика, чтобы убедиться, что спам не попадает в базу данных. Расширение отправляет\nсообщение на сервер каждый раз, когда Вы пропускаете спонсорскую вставку. Надеемся, большая часть пользователей не поменяет эту настройку, так что у нас будет точная статистика просмотров :)"
},
"showNotice": {
"message": "Показывать уведомление снова"
},
"longDescription": {
"message": "SponsorBlock — это расширение, которое пропускает спонсорские вставки в видео на YouTube. SponsorBlock — это краудсорсинговое расширение, которое позволяет каждому отправить время начала и конца спонсорских сегментов в видео на YouTube. После того, как кто-нибудь отправляет эту информацию, все остальные пользователи расширения будут автоматически пропускать спонсорские сегменты.",
"description": "Полное описание расширения на страницах магазинов."
},
"website": {
"message": "Сайт",
"description": "Используется на странице магазина Firefox"
},
"sourceCode": {
"message": "Исходный код",
"description": "Используется на странице магазина Firefox"
},
"noticeUpdate": {
"message": "Уведомление было обновлено!",
"description": "The first line of the message displayed after the notice was upgraded."
},
"noticeUpdate2": {
"message": "Если оно Вам всё равно не нравится, нажмите \"не показывать\".",
"description": "The second line of the message displayed after the notice was upgraded."
},
"setStartSponsorShortcut": {
"message": "Назначить горячую клавишу для начала спонсорской вставки"
},
"setSubmitKeybind": {
"message": "Назначить горячую клавишу для отправки"
},
"keybindDescription": {
"message": "Нажмите клавишу, чтобы выбрать её"
},
"keybindDescriptionComplete": {
"message": "Клавиша назначена на: "
},
"0": {
"message": "Таймаут подключения. Проверьте ваше соединение с интернетом. Если ваш интернет работает, сервер, скорее всего, перегружен или лежит."
},
"disableSkipping": {
"message": "Отключить SponsorBlock"
},
"enableSkipping": {
"message": "Включить SponsorBlock"
},
"yourWork": {
"message": "Ваша работа",
"description": "Used to describe the section that will show you the statistics from your submissions."
},
"502": {
"message": "Похоже, сервер перегружен. Попробуйте ещё раз через несколько секунд."
},
"errorCode": {
"message": "Код ошибки: "
},
"noticeTitleNotSkipped": {
"message": "Пропустить спонсорскую вставку?"
},
"skip": {
"message": "Пропустить"
},
"disableAutoSkip": {
"message": "Отключить автоматический пропуск"
},
"enableAutoSkip": {
"message": "Включить автоматический пропуск"
},
"autoSkipDescription": {
"message": "Автоматический пропуск будет пропускать спонсорские вставки за Вас. Если выключено, будет показываться уведомление с предложением пропустить."
},
"youHaveSkipped": {
"message": "Вы пропустили "
},
"youHaveSaved": {
"message": "Вы сэкономили "
},
"minLower": {
"message": "минуту"
},
"minsLower": {
"message": "минут"
},
"hourLower": {
"message": "час"
},
"hoursLower": {
"message": "часов"
},
"youHaveSavedTime": {
"message": "Вы сэкономили людям"
},
"youHaveSavedTimeEnd": {
"message": " их жизней."
},
"guildlinesSummary": {
"message": "- Убедитесь, что Ваш сегмент содержит только платную интеграцию, и больше ничего.\n- Убедитесь, что пропуск этого сегмента не пропустит никакой ценный контент\n- Если всё видео целиком спонсорское, пожалуйста, не сообщайте о нём. Система для сообщения о целых видео скоро выйдет.\n- Пожалуйста, не сообщайте об отказах от ответственности, которые могут показать предвзятость (если видео с обзором проплачено, не пропускайте, когда они это упоминают)."
},
"statusReminder": {
"message": "Смотрите состояние сервера на status.sponsor.ajay.app."
},
"changeUserID": {
"message": "Импортировать/экспортировать Ваш идентификатор пользователя"
},
"whatChangeUserID": {
"message": "Это нужно держать в секрете. Это как пароль, не стоит им ни с кем делиться. Если он у кого-то есть, он сможет выдать себя за Вас."
},
"setUserID": {
"message": "Установить идентификатор пользователя"
},
"userIDChangeWarning": {
"message": "Внимание: изменение идентификатора пользователя необратимо. Вы действительно хотите это сделать? Сделайте резервную копию вашего старого на всякий случай."
},
"createdBy": {
"message": "Создано"
},
"autoSkip": {
"message": "Автоматический пропуск"
},
"showSkipNotice": {
"message": "Показывать уведомление после пропуска спонсорской вставки"
},
"keybindCurrentlySet": {
"message": ". Он сейчас назначен на:"
},
"supportInvidious": {
"message": "Поддержка Invidious"
},
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) — это неофициальный клиент YouTube. Чтобы включить поддержку, Вам понадобится принять дополнительные разрешения. Это НЕ работает в приватном режиме в Chrome и других вариантах Chromium."
},
"optionsInfo": {
"message": "Включить поддержку Invidious, выключить автоматический пропуск, скрыть кнопки и не только."
},
"addInvidiousInstance": {
"message": "Добавить инстанс Invidious"
},
"addInvidiousInstanceDescription": {
"message": "Добавить свой инстанс Invidious. Формат: ТОЛЬКО домен. Например, invidious.ajay.app"
},
"add": {
"message": "Добавить"
},
"addInvidiousInstanceError": {
"message": "Это неправильный домен. Введите ТОЛЬКО домен. Например, invidious.ajay.app"
},
"resetInvidiousInstance": {
"message": "Сбросить список инстансов Invidious"
},
"resetInvidiousInstanceAlert": {
"message": "Вы собираетесь сбросить список инстансов Invidious"
},
"currentInstances": {
"message": "Текущие инстансы:"
},
"enableAutoUpvote": {
"message": "Автоматически голосовать \"за\""
},
"whatAutoUpvote": {
"message": "Если это включено, расширение будет голосовать \"за\" все предложения других пользователей, если Вы на них не пожалуетесь. Если уведомление отключено, это не будет происходить."
},
"minDuration": {
"message": "Минимальная длительность (секунд):"
},
"minDurationDescription": {
"message": "Спонсорские сегменты короче этого значения не будут пропускаться и не будут показаны в плеере."
},
"shortCheck": {
"message": "Следующий диапазон времени короче, чем Ваша настройка минимальной длительности. Это может означать, что он уже был отправлен, и просто игнорируется из-за этой настройки. Вы действительно хотите отправить?"
},
"showUploadButton": {
"message": "Показывать кнопку отправки"
},
"whatUploadButton": {
"message": "Эта кнопка появляется в плеере YouTube после того, как Вы выбрали отметку времени и готовы к отправке."
},
"customServerAddress": {
"message": "Адрес сервера SponsorBlock"
},
"customServerAddressDescription": {
"message": "Адрес, по которому SponsorBlock обращается к серверу.\nМеняйте только если Вы подняли свой сервер."
},
"save": {
"message": "Сохранить"
},
"reset": {
"message": "Сбросить"
},
"customAddressError": {
"message": "Этот адрес неправильного формата. Убедитесь, что он начинается с http:// или https://, и что на конце нет слэшей."
},
"areYouSureReset": {
"message": "Вы действительно хотите это сбросить?"
},
"confirmPrivacy": {
"message": "Было обнаружено, что это видео непубличное. Нажмите \"отмена\", если не хотите проверять его на спонсоров."
},
"unlistedCheck": {
"message": "Игнорировать непубличные видео"
},
"whatUnlistedCheck": {
"message": "Эта настройка значительно замедлит SponsorBlock. Поиск спонсоров требует отправки идентификатора видео на сервер. Если Вас беспокоит отправка идентификаторов непубличных видео по интернету, включите эту настройку."
},
"mobileUpdateInfo": {
"message": "m.youtube.com теперь поддерживается"
}
}

View File

@@ -308,6 +308,32 @@
<br/>
<br/>
<div option-type="private-text-change" sync-option="*" confirm-message="exportOptionsWarning">
<div class="option-button trigger-button">
__MSG_exportOptions__
</div>
<br/>
<div class="small-description">__MSG_whatExportOptions__</div>
<div class="option-hidden-section hidden">
<br/>
<input class="option-text-box" type="text">
<br/>
<br/>
<div class="option-button text-change-set">
__MSG_setOptions__
</div>
</div>
</div>
<br/>
<br/>
<div option-type="text-change" sync-option="serverAddress">
<label class="text-label-container">
<div>__MSG_customServerAddress__</div>

View File

@@ -1,5 +1,8 @@
import * as Types from "./types";
import Config from "./config";
// Make the config public for debugging purposes
(<any> window).SB = Config;
import Utils from "./utils";
var utils = new Utils({
@@ -86,9 +89,9 @@ chrome.runtime.onInstalled.addListener(function (object) {
//save this UUID
Config.config.userID = newUserID;
//TODO: Remove when invidious support is old
//TODO: Remove when mobile support is old
// Don't show this to new users
Config.config.invidiousUpdateInfoShowCount = 6;
// Config.config.mobileUpdateShowCount = 1;
}
}, 1500);
});

View File

@@ -20,12 +20,12 @@ interface SBConfig {
hideDiscordLaunches: number,
hideDiscordLink: boolean,
invidiousInstances: string[],
invidiousUpdateInfoShowCount: number,
autoUpvote: boolean,
supportInvidious: boolean,
serverAddress: string,
minDuration: number,
checkForUnlistedVideos: boolean
checkForUnlistedVideos: boolean,
mobileUpdateShowCount: number
}
interface SBObject {
@@ -84,13 +84,8 @@ class SBMap<T, U> extends Map {
return result;
}
toJSON() {
return Array.from(this.entries());
}
}
var Config: SBObject = {
/**
* Callback function when an option is updated
@@ -116,12 +111,12 @@ var Config: SBObject = {
hideDiscordLaunches: 0,
hideDiscordLink: false,
invidiousInstances: ["invidio.us", "invidiou.sh", "invidious.snopyta.org"],
invidiousUpdateInfoShowCount: 0,
autoUpvote: true,
supportInvidious: false,
serverAddress: CompileConfig.serverAddress,
minDuration: 0,
checkForUnlistedVideos: false
checkForUnlistedVideos: false,
mobileUpdateShowCount: 0
},
localConfig: null,
config: null
@@ -131,14 +126,14 @@ var Config: SBObject = {
/**
* A SBMap cannot be stored in the chrome storage.
* This data will be encoded into an array instead as specified by the toJSON function.
* This data will be encoded into an array instead
*
* @param data
*/
function encodeStoredItem(data) {
// if data is SBMap convert to json for storing
if(!(data instanceof SBMap)) return data;
return JSON.stringify(data);
return Array.from(data.entries());
}
/**
@@ -148,18 +143,30 @@ function encodeStoredItem(data) {
* @param {*} data
*/
function decodeStoredItem(id: string, data) {
if(typeof data !== "string") return data;
if (!Config.defaults[id]) return data;
if (Config.defaults[id] instanceof SBMap) {
try {
let str = JSON.parse(data);
let jsonData: any = data;
if(!Array.isArray(str)) return data;
return new SBMap(id, str);
// Check if data is stored in the old format for SBMap (a JSON string)
if (typeof data === "string") {
try {
jsonData = JSON.parse(data);
} catch(e) {
// Continue normally (out of this if statement)
}
}
if (!Array.isArray(jsonData)) return data;
return new SBMap(id, jsonData);
} catch(e) {
console.error("Failed to parse SBMap: " + id);
}
}
// If all else fails, return the data
return data;
}
}
function configProxy(): any {

View File

@@ -15,11 +15,17 @@ utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
var sponsorDataFound = false;
var previousVideoID = null;
//the actual sponsorTimes if loaded and UUIDs associated with them
var sponsorTimes = null;
var sponsorTimes: number[][] = null;
var UUIDs = [];
//what video id are these sponsors for
var sponsorVideoID = null;
// Skips are scheduled to ensure precision.
// Skips are rescheduled every seeked event.
// Skips are canceled every seeking event
var currentSkipSchedule: NodeJS.Timeout = null;
var seekListenerSetUp = false
//these are sponsors that have been downvoted
var hiddenSponsorTimes = [];
@@ -30,6 +36,7 @@ var sponsorSkipped = [];
var video: HTMLVideoElement;
var onInvidious;
var onMobileYouTube;
//the video id of the last preview bar update
var lastPreviewBarUpdate;
@@ -37,6 +44,14 @@ var lastPreviewBarUpdate;
//whether the duration listener listening for the duration changes of the video has been setup yet
var durationListenerSetUp = false;
// Is the video currently being switched
var switchingVideos = null;
// Used by the play and playing listeners to make sure two aren't
// called at the same time
var lastCheckTime = 0;
var lastCheckVideoTime = -1;
//the channel this video is about
var channelURL;
@@ -47,7 +62,7 @@ var title;
var channelWhitelisted = false;
// create preview bar
var previewBar = null;
var previewBar: PreviewBar = null;
// When not null, a sponsor is currently being previewed and auto skip should be enabled.
// This is set to a timeout function when that happens that will reset it after 3 seconds.
@@ -57,10 +72,7 @@ var previewResetter: NodeJS.Timeout = null;
var controls = null;
// Direct Links after the config is loaded
utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
//the last time looked at (used to see if this time is in the interval)
var lastTime = -1;
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
@@ -92,7 +104,8 @@ var skipNoticeContentContainer = () => ({
v: video,
reskipSponsorTime,
hiddenSponsorTimes,
updatePreviewBar
updatePreviewBar,
onMobileYouTube
});
//get messages from the background script and the popup
@@ -230,8 +243,8 @@ document.onkeydown = function(e: KeyboardEvent){
}
function resetValues() {
//reset last sponsor times
lastTime = -1;
lastCheckTime = 0;
lastCheckVideoTime = -1;
//reset sponsor times
sponsorTimes = null;
@@ -245,6 +258,8 @@ function resetValues() {
//reset sponsor data found check
sponsorDataFound = false;
switchingVideos = true;
}
async function videoIDChange(id) {
@@ -278,26 +293,19 @@ async function videoIDChange(id) {
channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true);
//setup the preview bar
if (previewBar == null) {
//create it
utils.wait(getControls).then(result => {
const progressElementSelectors = [
// For YouTube
"ytp-progress-bar-container",
"no-model cue-range-markers",
// For Invidious/VideoJS
"vjs-progress-holder"
];
if (previewBar === null) {
if (onMobileYouTube) {
// Mobile YouTube workaround
const observer = new MutationObserver(handleMobileControlsMutations);
for (const selector of progressElementSelectors) {
const el = document.getElementsByClassName(selector);
if (el && el.length && el[0]) {
previewBar = new PreviewBar(el[0]);
break;
}
}
observer.observe(document.getElementById("player-control-container"), {
attributes: true,
childList: true,
subtree: true
});
} else {
utils.wait(getControls).then(createPreviewBar);
}
}
//warn them if they had unsubmitted times
@@ -355,14 +363,133 @@ async function videoIDChange(id) {
}
});
});
//see if video controls buttons should be added
if (!onInvidious) {
updateVisibilityOfPlayerControlsButton();
}
}
function sponsorsLookup(id: string, channelIDPromise?) {
function handleMobileControlsMutations(): void {
let mobileYouTubeSelector = ".progress-bar-background";
updateVisibilityOfPlayerControlsButton().then((createdButtons) => {
if (createdButtons) {
if (sponsorTimesSubmitting != null && sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].length >= 2) {
changeStartSponsorButton(true, true);
} else if (sponsorTimesSubmitting != null && sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].length < 2) {
changeStartSponsorButton(false, true);
} else {
changeStartSponsorButton(true, false);
}
}
});
if (previewBar !== null) {
if (document.body.contains(previewBar.container)) {
updatePreviewBarPositionMobile(document.getElementsByClassName(mobileYouTubeSelector)[0]);
return;
} else {
// The container does not exist anymore, remove that old preview bar
previewBar.remove();
previewBar = null;
}
}
// Create the preview bar if needed (the function hasn't returned yet)
createPreviewBar();
}
/**
* Creates a preview bar on the video
*/
function createPreviewBar(): void {
if (previewBar !== null) return;
const progressElementSelectors = [
// For mobile YouTube
".progress-bar-background",
// For YouTube
".ytp-progress-bar-container",
".no-model.cue-range-markers",
// For Invidious/VideoJS
".vjs-progress-holder"
];
for (const selector of progressElementSelectors) {
const el = document.querySelectorAll(selector);
if (el && el.length && el[0]) {
previewBar = new PreviewBar(el[0], onMobileYouTube);
updatePreviewBar();
break;
}
}
}
/**
* Triggered every time the video duration changes.
* This happens when the resolution changes or at random time to clear memory.
*/
function durationChangeListener() {
updatePreviewBar();
}
function cancelSponsorSchedule(): void {
if (currentSkipSchedule !== null) {
clearTimeout(currentSkipSchedule);
currentSkipSchedule = null;
}
}
/**
*
* @param currentTime Optional if you don't want to use the actual current time
*/
function startSponsorSchedule(currentTime?: number): void {
cancelSponsorSchedule();
if (Config.config.disableSkipping || channelWhitelisted){
return;
}
if (currentTime === undefined || currentTime === null) currentTime = video.currentTime;
let skipInfo = getNextSkipIndex(currentTime);
if (skipInfo.index === -1) return;
let skipTime = skipInfo.array[skipInfo.index];
let timeUntilSponsor = skipTime[0] - currentTime;
let skippingFunction = () => {
let forcedSkipTime: number = null;
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
skipToTime(video, skipInfo.index, skipInfo.array, skipInfo.openNotice);
if (Config.config.disableAutoSkip) {
forcedSkipTime = skipTime[0] + 0.001;
} else {
forcedSkipTime = skipTime[1];
}
}
startSponsorSchedule(forcedSkipTime);
};
if (timeUntilSponsor <= 0) {
skippingFunction();
} else {
currentSkipSchedule = setTimeout(skippingFunction, timeUntilSponsor * 1000 * (1 / video.playbackRate));
}
}
function sponsorsLookup(id: string, channelIDPromise?) {
video = document.querySelector('video') // Youtube video player
//there is no video here
if (video == null) {
@@ -374,7 +501,40 @@ function sponsorsLookup(id: string, channelIDPromise?) {
durationListenerSetUp = true;
//wait until it is loaded
video.addEventListener('durationchange', updatePreviewBar);
video.addEventListener('durationchange', durationChangeListener);
}
if (!seekListenerSetUp && !Config.config.disableSkipping) {
seekListenerSetUp = true;
video.addEventListener('play', () => {
switchingVideos = false;
// Make sure it doesn't get double called with the playing event
if (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000) {
lastCheckTime = Date.now();
lastCheckVideoTime = video.currentTime;
startSponsorSchedule();
}
});
video.addEventListener('playing', () => {
// Make sure it doesn't get double called with the play event
if (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000) {
lastCheckTime = Date.now();
lastCheckVideoTime = video.currentTime;
startSponsorSchedule();
}
});
video.addEventListener('seeked', () => {
if (!video.paused) startSponsorSchedule();
});
video.addEventListener('ratechange', () => startSponsorSchedule());
video.addEventListener('seeking', cancelSponsorSchedule);
video.addEventListener('pause', cancelSponsorSchedule);
startSponsorSchedule();
}
if (channelIDPromise !== undefined) {
@@ -427,6 +587,31 @@ function sponsorsLookup(id: string, channelIDPromise?) {
UUIDs = smallUUIDs;
}
// See if there are any zero second sponsors
let zeroSecondSponsor = false;
for (const time of sponsorTimes) {
if (time[0] <= 0) {
zeroSecondSponsor = true;
break;
}
}
if (!zeroSecondSponsor) {
for (const time of sponsorTimesSubmitting) {
if (time[0] <= 0) {
zeroSecondSponsor = true;
break;
}
}
}
if (!video.paused && !switchingVideos) {
if (zeroSecondSponsor) {
startSponsorSchedule(0);
} else {
startSponsorSchedule();
}
}
// Reset skip save
sponsorSkipped = [];
@@ -474,13 +659,6 @@ function sponsorsLookup(id: string, channelIDPromise?) {
sponsorLookupRetries++;
}
});
//add the event to run on the videos "ontimeupdate"
if (!Config.config.disableSkipping) {
video.ontimeupdate = function () {
sponsorCheck();
};
}
}
function getYouTubeVideoID(url: string) {
@@ -499,7 +677,9 @@ function getYouTubeVideoID(url: string) {
// Check if valid hostname
if (Config.config && Config.config.invidiousInstances.includes(urlObject.host)) {
onInvidious = true;
} else if (!["www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) {
} else if (urlObject.host === "m.youtube.com") {
onMobileYouTube = true;
} else if (!["m.youtube.com", "www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) {
if (!Config.config) {
// Call this later, in case this is an Invidious tab
utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoID(url)));
@@ -572,6 +752,15 @@ function getChannelID() {
channelWhitelisted = false;
}
/**
* This function is required on mobile YouTube and will keep getting called whenever the preview bar disapears
*/
function updatePreviewBarPositionMobile(parent: Element) {
if (document.getElementById("previewbar") === null) {
previewBar.updatePosition(parent);
}
}
function updatePreviewBar() {
let localSponsorTimes = sponsorTimes;
if (localSponsorTimes == null) localSponsorTimes = [];
@@ -608,73 +797,59 @@ function whitelistCheck() {
}
}
//video skipping
function sponsorCheck() {
if (Config.config.disableSkipping) {
// Make sure this isn't called again
video.ontimeupdate = null;
return;
} else if (channelWhitelisted) {
return;
/**
* Returns info about the next upcoming sponsor skip
*/
function getNextSkipIndex(currentTime: number): {array: number[][], index: number, openNotice: boolean} {
let sponsorStartTimes = getStartTimes(sponsorTimes);
let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, currentTime, true);
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting);
let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, currentTime, false);
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) ||
sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) {
return {
array: sponsorTimes,
index: minSponsorTimeIndex,
openNotice: true
};
} else {
return {
array: sponsorTimesSubmitting,
index: minPreviewSponsorTimeIndex,
openNotice: false
};
}
}
let skipHappened = false;
/**
* Gets just the start times from a sponsor times array.
* Optionally specify a minimum
*
* @param sponsorTimes
* @param minimum
* @param hideHiddenSponsors
*/
function getStartTimes(sponsorTimes: number[][], minimum?: number, hideHiddenSponsors: boolean = false): number[] {
if (sponsorTimes === null) return [];
let startTimes: number[] = [];
if (sponsorTimes != null) {
//see if any sponsor start time was just passed
for (let i = 0; i < sponsorTimes.length; i++) {
//if something was skipped
if (checkSponsorTime(sponsorTimes, i, true)) {
skipHappened = true;
break;
}
if ((minimum === undefined || sponsorTimes[i][0] >= minimum) && (!hideHiddenSponsors || !hiddenSponsorTimes.includes(i))) {
startTimes.push(sponsorTimes[i][0]);
}
}
if (!skipHappened) {
//check for the "preview" sponsors (currently edited by this user)
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
//must be a finished sponsor and be valid
if (sponsorTimesSubmitting[i].length > 1 && sponsorTimesSubmitting[i][1] > sponsorTimesSubmitting[i][0]) {
checkSponsorTime(sponsorTimesSubmitting, i, false);
}
}
}
//don't keep track until they are loaded in
if (sponsorTimes !== null || sponsorTimesSubmitting.length > 0) {
lastTime = video.currentTime;
}
return startTimes;
}
function checkSponsorTime(sponsorTimes, index, openNotice): boolean {
//this means part of the video was just skipped
if (Math.abs(video.currentTime - lastTime) > 1 && lastTime != -1) {
//make lastTime as if the video was playing normally
lastTime = video.currentTime - 0.0001;
}
if (checkIfTimeToSkip(video.currentTime, sponsorTimes[index][0], sponsorTimes[index][1]) && !hiddenSponsorTimes.includes(index)) {
//skip it
skipToTime(video, index, sponsorTimes, openNotice);
//something was skipped
return true;
}
return false;
}
function checkIfTimeToSkip(currentVideoTime, startTime, endTime) {
//If the sponsor time is in between these times, skip it
//Checks if the last time skipped to is not too close to now, to make sure not to get too many
// sponsor times in a row (from one troll)
//the last term makes 0 second start times possible only if the video is not setup to start at a different time from zero
return (Math.abs(currentVideoTime - startTime) < 3 && startTime >= lastTime && startTime <= currentVideoTime) ||
(lastTime == -1 && startTime == 0 && currentVideoTime < endTime)
}
//skip fromt he start time to the end time for a certain index sponsor time
//skip from the start time to the end time for a certain index sponsor time
function skipToTime(v, index, sponsorTimes, openNotice) {
if (!Config.config.disableAutoSkip || previewResetter !== null) {
v.currentTime = sponsorTimes[index][1];
@@ -688,8 +863,16 @@ function skipToTime(v, index, sponsorTimes, openNotice) {
if (openNotice) {
//send out the message saying that a sponsor message was skipped
if (!Config.config.dontShowNotice) {
let skipNotice = new SkipNotice(this, currentUUID, Config.config.disableAutoSkip, skipNoticeContentContainer);
//TODO: Remove this when Mobile support is old
if (Config.config.mobileUpdateShowCount < 1) {
skipNotice.addNoticeInfoMessage(chrome.i18n.getMessage("mobileUpdateInfo"));
Config.config.mobileUpdateShowCount += 1;
}
//auto-upvote this sponsor
if (Config.config.trackViewCount && !Config.config.disableAutoSkip && Config.config.autoUpvote) {
vote(1, currentUUID, null);
@@ -697,10 +880,9 @@ function skipToTime(v, index, sponsorTimes, openNotice) {
}
//send telemetry that a this sponsor was skipped
if (Config.config.trackViewCount && !sponsorSkipped[index]) {
if (Config.config.trackViewCount && !sponsorSkipped[index] && !Config.config.disableAutoSkip) {
utils.sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + currentUUID);
if (!Config.config.disableAutoSkip) {
// Count this as a skip
Config.config.minutesSaved = Config.config.minutesSaved + (sponsorTimes[index][1] - sponsorTimes[index][0]) / 60;
Config.config.skipCount = Config.config.skipCount + 1;
@@ -708,7 +890,6 @@ function skipToTime(v, index, sponsorTimes, openNotice) {
sponsorSkipped[index] = true;
}
}
}
}
function unskipSponsorTime(UUID) {
@@ -725,16 +906,27 @@ function reskipSponsorTime(UUID) {
}
}
function createButton(baseID, title, callback, imageName, isDraggable=false) {
if (document.getElementById(baseID + "Button") != null) return;
function createButton(baseID, title, callback, imageName, isDraggable=false): boolean {
if (document.getElementById(baseID + "Button") != null) return false;
// Button HTML
let newButton = document.createElement("button");
newButton.draggable = isDraggable;
newButton.id = baseID + "Button";
newButton.className = "ytp-button playerButton";
newButton.classList.add("playerButton");
if (!onMobileYouTube) {
newButton.classList.add("ytp-button");
} else {
newButton.classList.add("icon-button");
newButton.style.padding = "0";
}
newButton.setAttribute("title", chrome.i18n.getMessage(title));
newButton.addEventListener("click", callback);
newButton.addEventListener("click", (event: Event) => {
callback();
// Prevents the contols from closing when clicked
if (onMobileYouTube) event.stopPropagation();
});
// Image HTML
let newButtonImage = document.createElement("img");
@@ -748,39 +940,55 @@ function createButton(baseID, title, callback, imageName, isDraggable=false) {
// Add the button to player
controls.prepend(newButton);
return true;
}
function getControls() {
let controls = document.getElementsByClassName("ytp-right-controls");
function getControls(): HTMLElement | boolean {
let controlsSelectors = [
// YouTube
".ytp-right-controls",
// Mobile YouTube
".player-controls-top",
// Invidious/videojs video element's controls element
".vjs-control-bar"
]
if (!controls || controls.length === 0) {
// The invidious video element's controls element
controls = document.getElementsByClassName("vjs-control-bar");
return (!controls || controls.length === 0) ? false : controls[controls.length - 1];
} else {
return controls[controls.length - 1];
for (const controlsSelector of controlsSelectors) {
let controls = document.querySelectorAll(controlsSelector);
if (controls && controls.length > 0) {
return <HTMLElement> controls[controls.length - 1];
}
}
return false;
};
//adds all the player controls buttons
async function createButtons() {
async function createButtons(): Promise<boolean> {
let result = await utils.wait(getControls).catch();
//set global controls variable
controls = result;
// Add button if does not already exist in html
createButton("startSponsor", "sponsorStart", startSponsorClicked, "PlayerStartIconSponsorBlocker256px.png");
createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png")
createButton("delete", "clearTimes", clearSponsorTimes, "PlayerDeleteIconSponsorBlocker256px.png");
createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png");
}
//adds or removes the player controls button to what it should be
async function updateVisibilityOfPlayerControlsButton() {
//not on a proper video yet
if (!sponsorVideoID) return;
let createdButton = false;
await createButtons();
// Add button if does not already exist in html
createdButton = createButton("startSponsor", "sponsorStart", startSponsorClicked, "PlayerStartIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("delete", "clearTimes", clearSponsorTimes, "PlayerDeleteIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png") || createdButton;
return createdButton;
}
//adds or removes the player controls button to what it should be
async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
//not on a proper video yet
if (!sponsorVideoID) return false;
let createdButtons = await createButtons();
if (Config.config.hideVideoPlayerControls || onInvidious) {
document.getElementById("startSponsorButton").style.display = "none";
@@ -799,6 +1007,8 @@ async function updateVisibilityOfPlayerControlsButton() {
if (Config.config.hideDeleteButtonPlayerControls || onInvidious) {
document.getElementById("deleteButton").style.display = "none";
}
return createdButtons;
}
function startSponsorClicked() {
@@ -831,22 +1041,17 @@ function updateSponsorTimesSubmitting() {
sponsorTimesSubmitting = sponsorTimes;
updatePreviewBar();
// Restart skipping schedule
startSponsorSchedule();
}
}
});
}
//is the submit button on the player loaded yet
function isSubmitButtonLoaded() {
return document.getElementById("submitButton") !== null;
}
async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
if(!sponsorVideoID) return false;
//make sure submit button is loaded
await utils.wait(isSubmitButtonLoaded);
//if it isn't visible, there is no data
let shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
document.getElementById("deleteButton").style.display = shouldHide;

View File

@@ -23,19 +23,31 @@ let barTypes = {
class PreviewBar {
container: HTMLUListElement;
parent: any;
onMobileYouTube: boolean;
constructor(parent) {
constructor(parent, onMobileYouTube) {
this.container = document.createElement('ul');
this.container.id = 'previewbar';
this.parent = parent;
this.updatePosition();
this.onMobileYouTube = onMobileYouTube;
this.updatePosition(parent);
}
updatePosition() {
updatePosition(parent) {
//below the seek bar
// this.parent.insertAdjacentElement("afterEnd", this.container);
this.parent = parent;
if (this.onMobileYouTube) {
parent.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
parent.style.opacity = "1";
this.container.style.transform = "none";
}
//on the seek bar
this.parent.insertAdjacentElement("afterBegin", this.container);
}
@@ -70,7 +82,7 @@ class PreviewBar {
bar.setAttribute('data-vs-segment-type', types[i]);
bar.style.backgroundColor = barTypes[types[i]].color;
bar.style.opacity = barTypes[types[i]].opacity;
if (!this.onMobileYouTube) bar.style.opacity = barTypes[types[i]].opacity;
bar.style.width = width + '%';
bar.style.left = (timestamps[i][0] / duration * 100) + "%";
bar.style.position = "absolute"

View File

@@ -1,5 +1,7 @@
'use strict';
import Config from "../config";
/**
* The notice that tells the user that a sponsor was just skipped
*/
@@ -56,6 +58,10 @@ class SkipNotice {
noticeElement.classList.add("sponsorSkipObject");
noticeElement.classList.add("sponsorSkipNotice");
noticeElement.style.zIndex = String(50 + amountOfPreviousNotices);
if (contentContainer().onMobileYouTube) {
noticeElement.style.bottom = "4em";
noticeElement.style.transform = "scale(0.8) translate(10%, 10%)";
}
//add mouse enter and leave listeners
noticeElement.addEventListener("mouseenter", this.pauseCountdown.bind(this));
@@ -173,7 +179,8 @@ class SkipNotice {
noticeElement.appendChild(secondRow);
//get reference node
let referenceNode = document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
let referenceNode = document.getElementById("player-container-id")
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) {
//for embeds
let player = document.getElementById("player");
@@ -300,7 +307,7 @@ class SkipNotice {
if (this.manualSkip) {
this.changeNoticeTitle(chrome.i18n.getMessage("noticeTitle"));
this.contentContainer().vote(1, this.UUID, this);
if (Config.config.autoUpvote) this.contentContainer().vote(1, this.UUID);
}
}

View File

@@ -1,4 +1,6 @@
import Config from "./config";
// Make the config public for debugging purposes
(<any> window).SB = Config;
import Utils from "./utils";
var utils = new Utils();
@@ -72,7 +74,7 @@ async function init() {
textChangeInput.value = Config.config[textChangeOption];
textChangeSetButton.addEventListener("click", () => {
textChangeSetButton.addEventListener("click", async () => {
// See if anything extra must be done
switch (textChangeOption) {
case "serverAddress":
@@ -84,6 +86,18 @@ async function init() {
return;
}
// Permission needed on Firefox
if (utils.isFirefox()) {
let permissionSuccess = await new Promise((resolve, reject) => {
chrome.permissions.request({
origins: [textChangeInput.value + "/"],
permissions: []
}, resolve);
});
if (!permissionSuccess) return;
}
break;
}
@@ -259,6 +273,8 @@ function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
if (!granted) {
Config.config[option] = false;
checkbox.checked = false;
} else {
checkbox.checked = true;
}
});
} else {
@@ -346,15 +362,50 @@ function activatePrivateTextChange(element: HTMLElement) {
return;
}
textBox.value = Config.config[option];
let result = Config.config[option];
// See if anything extra must be done
switch (option) {
case "*":
result = JSON.stringify(Config.localConfig);
break;
}
textBox.value = result;
let setButton = element.querySelector(".text-change-set");
setButton.addEventListener("click", () => {
let confirmMessage = element.getAttribute("confirm-message");
if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) {
// See if anything extra must be done
switch (option) {
case "*":
try {
let newConfig = JSON.parse(textBox.value);
for (const key in newConfig) {
Config.config[key] = newConfig[key];
}
init();
if (newConfig.supportInvidious) {
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
checkbox.checked = true;
invidiousOnClick(checkbox, "supportInvidious");
}
} catch (e) {
alert(chrome.i18n.getMessage("incorrectlyFormattedOptions"));
}
break;
default:
Config.config[option] = textBox.value;
}
}
});
element.querySelector(".option-hidden-section").classList.remove("hidden");

View File

@@ -157,7 +157,7 @@ async function runThePopup(messageListener?: MessageListener) {
//get the amount of times this user has contributed and display it to thank them
if (Config.config.sponsorTimesContributed != undefined) {
if (Config.config.sponsorTimesContributed > 1) {
if (Config.config.sponsorTimesContributed !== 1) {
PageElements.sponsorTimesContributionsDisplayEndWord.innerText = chrome.i18n.getMessage("Sponsors");
} else {
PageElements.sponsorTimesContributionsDisplayEndWord.innerText = chrome.i18n.getMessage("Sponsor");
@@ -720,15 +720,6 @@ async function runThePopup(messageListener?: MessageListener) {
//save this
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
messageHandler.query({
active: true,
currentWindow: true
}, tabs => {
messageHandler.sendMessage(
tabs[0].id,
{message: "sponsorDataChanged"}
);
});
//update display
displaySponsorTimes();
@@ -750,6 +741,16 @@ async function runThePopup(messageListener?: MessageListener) {
//hide submission section
document.getElementById("submissionSection").style.display = "none";
}
messageHandler.query({
active: true,
currentWindow: true
}, tabs => {
messageHandler.sendMessage(
tabs[0].id,
{message: "sponsorDataChanged"}
);
});
}
function clearTimes() {

View File

@@ -42,7 +42,8 @@ module.exports = env => ({
),
new BuildManifest({
browser: env.browser,
pretty: env.mode === "production"
pretty: env.mode === "production",
stream: env.stream
})
]
});

View File

@@ -8,6 +8,8 @@ const fs = require('fs');
const manifest = require("../manifest/manifest.json");
const firefoxManifestExtra = require("../manifest/firefox-manifest-extra.json");
const chromeManifestExtra = require("../manifest/chrome-manifest-extra.json");
const betaManifestExtra = require("../manifest/beta-manifest-extra.json");
const firefoxBetaManifestExtra = require("../manifest/firefox-beta-manifest-extra.json");
// schema for options object
const schema = {
@@ -18,6 +20,9 @@ const schema = {
},
pretty: {
type: 'boolean'
},
steam: {
type: 'string'
}
}
};
@@ -40,6 +45,14 @@ class BuildManifest {
mergeObjects(manifest, chromeManifestExtra);
}
if (this.options.stream === "beta") {
mergeObjects(manifest, betaManifestExtra);
if (this.options.browser.toLowerCase() === "firefox") {
mergeObjects(manifest, firefoxBetaManifestExtra);
}
}
let result = JSON.stringify(manifest);
if (this.options.pretty) result = JSON.stringify(manifest, null, 2);