mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 11:37:02 +03:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba575f6b8d | ||
|
|
ff9b2338e0 | ||
|
|
d2bb4b38e3 | ||
|
|
760c08dd0c | ||
|
|
50517eb462 | ||
|
|
e77425c21e | ||
|
|
f63abb053d | ||
|
|
7b5703aa04 | ||
|
|
d641066312 | ||
|
|
44ca8d47d8 | ||
|
|
d5f41bf4ad | ||
|
|
73e8926444 | ||
|
|
5ad694af65 | ||
|
|
d7f7acb219 | ||
|
|
b4be51333a | ||
|
|
ae94811a00 | ||
|
|
a484f2f2cc | ||
|
|
9cce4e734d | ||
|
|
3c63644213 | ||
|
|
ad25bc34de | ||
|
|
0241e15691 | ||
|
|
af9a6b8a84 | ||
|
|
329b188435 | ||
|
|
2e49bb73c5 | ||
|
|
5158020293 | ||
|
|
feaf80ad1e | ||
|
|
7fbd89159e | ||
|
|
716861da18 | ||
|
|
d0a34d423c | ||
|
|
adfba72f19 | ||
|
|
f00337c376 | ||
|
|
737a023b65 | ||
|
|
5551344355 | ||
|
|
07f64382fb | ||
|
|
1c7cde2a19 | ||
|
|
8510a7f3d8 | ||
|
|
db60b11a17 | ||
|
|
6a212b762a | ||
|
|
c8ec2922cf | ||
|
|
b629b7d333 | ||
|
|
514a8b62d6 | ||
|
|
cd11618a5d | ||
|
|
8be3cb157a | ||
|
|
4ca57cc025 | ||
|
|
397bcc94c5 | ||
|
|
8b28bccfd7 | ||
|
|
c6107057d9 | ||
|
|
ab2a9530e9 | ||
|
|
bfc771bd99 | ||
|
|
e75e588755 | ||
|
|
0266bb49ca | ||
|
|
9e693fd555 | ||
|
|
1f30b9ec84 | ||
|
|
50862e3c03 | ||
|
|
20e90bbc34 | ||
|
|
2e212e6d10 | ||
|
|
2039bfa081 | ||
|
|
7dc8a99247 | ||
|
|
1b25ea7f95 | ||
|
|
1869382166 | ||
|
|
d58cd639c7 | ||
|
|
6cd2d4cf83 | ||
|
|
b681f5abd9 | ||
|
|
5b2a0feccf | ||
|
|
cd58f6bc73 | ||
|
|
aeabf806ac | ||
|
|
af7ba31c2f | ||
|
|
5b962b1b9d | ||
|
|
219a7ba8c3 | ||
|
|
933babb4e6 | ||
|
|
023ba2e051 | ||
|
|
1c833a8b1d |
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
|
||||
76
.github/workflows/release.yml
vendored
Normal file
76
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: Upload Release Build
|
||||
|
||||
on: release
|
||||
|
||||
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:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload to release
|
||||
uses: Shopify/upload-to-release@master
|
||||
with:
|
||||
args: builds/FirefoxExtension.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -68,9 +68,9 @@ 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>
|
||||
|
||||
|
||||
4
manifest/beta-manifest-extra.json
Normal file
4
manifest/beta-manifest-extra.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "BETA - SponsorBlock"
|
||||
}
|
||||
|
||||
8
manifest/firefox-beta-manifest-extra.json
Normal file
8
manifest/firefox-beta-manifest-extra.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "sponsorBlockerBETA@ajay.app"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "__MSG_Name__",
|
||||
"version": "1.2.13",
|
||||
"version": "1.2.20",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"content_scripts": [{
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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,8 @@
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 теперь поддерживается"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,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);
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
@@ -116,12 +116,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
|
||||
|
||||
447
src/content.ts
447
src/content.ts
@@ -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,9 @@ 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 = false;
|
||||
|
||||
//the channel this video is about
|
||||
var channelURL;
|
||||
|
||||
@@ -47,7 +57,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 +67,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 +99,8 @@ var skipNoticeContentContainer = () => ({
|
||||
v: video,
|
||||
reskipSponsorTime,
|
||||
hiddenSponsorTimes,
|
||||
updatePreviewBar
|
||||
updatePreviewBar,
|
||||
onMobileYouTube
|
||||
});
|
||||
|
||||
//get messages from the background script and the popup
|
||||
@@ -230,9 +238,6 @@ document.onkeydown = function(e: KeyboardEvent){
|
||||
}
|
||||
|
||||
function resetValues() {
|
||||
//reset last sponsor times
|
||||
lastTime = -1;
|
||||
|
||||
//reset sponsor times
|
||||
sponsorTimes = null;
|
||||
UUIDs = [];
|
||||
@@ -255,10 +260,12 @@ async function videoIDChange(id) {
|
||||
sponsorVideoID = id;
|
||||
|
||||
resetValues();
|
||||
|
||||
|
||||
//id is not valid
|
||||
if (!id) return;
|
||||
|
||||
switchingVideos = true;
|
||||
|
||||
// Wait for options to be ready
|
||||
await utils.wait(() => Config.config !== null, 5000, 1);
|
||||
|
||||
@@ -278,26 +285,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
|
||||
@@ -354,15 +354,126 @@ 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 = video.currentTime;
|
||||
|
||||
let skipInfo = getNextSkipIndex(currentTime);
|
||||
|
||||
if (skipInfo.index === -1) return;
|
||||
|
||||
let skipTime = skipInfo.array[skipInfo.index];
|
||||
let timeUntilSponsor = skipTime[0] - currentTime;
|
||||
|
||||
let skippingFunction = () => {
|
||||
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
|
||||
skipToTime(video, skipInfo.index, skipInfo.array, skipInfo.openNotice);
|
||||
}
|
||||
|
||||
startSponsorSchedule(skipTime[0] + 0.001);
|
||||
};
|
||||
|
||||
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 +485,24 @@ 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;
|
||||
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 +555,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 +627,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 +645,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 +720,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 +765,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 skipHappened = false;
|
||||
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting);
|
||||
let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, currentTime, false);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
|
||||
|
||||
//don't keep track until they are loaded in
|
||||
if (sponsorTimes !== null || sponsorTimesSubmitting.length > 0) {
|
||||
lastTime = video.currentTime;
|
||||
if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) ||
|
||||
sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) {
|
||||
return {
|
||||
array: sponsorTimes,
|
||||
index: minSponsorTimeIndex,
|
||||
openNotice: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
array: sponsorTimesSubmitting,
|
||||
index: minPreviewSponsorTimeIndex,
|
||||
openNotice: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
/**
|
||||
* 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[] = [];
|
||||
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
if ((minimum === undefined || sponsorTimes[i][0] >= minimum) && (!hideHiddenSponsors || !hiddenSponsorTimes.includes(i))) {
|
||||
startTimes.push(sponsorTimes[i][0]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return startTimes;
|
||||
}
|
||||
|
||||
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 +831,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,16 +848,14 @@ 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;
|
||||
// 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;
|
||||
|
||||
sponsorSkipped[index] = true;
|
||||
}
|
||||
sponsorSkipped[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -725,16 +874,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,40 +908,56 @@ 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;
|
||||
|
||||
// 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();
|
||||
|
||||
await createButtons();
|
||||
|
||||
if (Config.config.hideVideoPlayerControls || onInvidious) {
|
||||
document.getElementById("startSponsorButton").style.display = "none";
|
||||
document.getElementById("submitButton").style.display = "none";
|
||||
@@ -799,6 +975,8 @@ async function updateVisibilityOfPlayerControlsButton() {
|
||||
if (Config.config.hideDeleteButtonPlayerControls || onInvidious) {
|
||||
document.getElementById("deleteButton").style.display = "none";
|
||||
}
|
||||
|
||||
return createdButtons;
|
||||
}
|
||||
|
||||
function startSponsorClicked() {
|
||||
@@ -831,22 +1009,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;
|
||||
|
||||
@@ -23,18 +23,30 @@ 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"
|
||||
|
||||
@@ -56,6 +56,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 +177,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");
|
||||
|
||||
25
src/popup.ts
25
src/popup.ts
@@ -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");
|
||||
@@ -719,17 +719,8 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
sponsorTimes.splice(index, 1);
|
||||
|
||||
//save this
|
||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||
messageHandler.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, tabs => {
|
||||
messageHandler.sendMessage(
|
||||
tabs[0].id,
|
||||
{message: "sponsorDataChanged"}
|
||||
);
|
||||
});
|
||||
|
||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
//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() {
|
||||
|
||||
@@ -42,7 +42,8 @@ module.exports = env => ({
|
||||
),
|
||||
new BuildManifest({
|
||||
browser: env.browser,
|
||||
pretty: env.mode === "production"
|
||||
pretty: env.mode === "production",
|
||||
stream: env.stream
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user