mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-14 23:47:04 +03:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
522a04eecb | ||
|
|
d8dfbef1a7 | ||
|
|
60ea7190f9 | ||
|
|
804870f18a | ||
|
|
7c302af207 | ||
|
|
2cc1dcc6fd | ||
|
|
31cc4b4960 | ||
|
|
af86534992 | ||
|
|
0f9122aa1c | ||
|
|
d0e35032a5 | ||
|
|
acf26d3127 | ||
|
|
d352c6efb4 | ||
|
|
5ff9b10f21 | ||
|
|
80c67d8340 | ||
|
|
3ee2e2517a | ||
|
|
dd7f227305 | ||
|
|
c1d3c7d680 | ||
|
|
fae6d0d0cf | ||
|
|
60d106fc52 | ||
|
|
a4df2eab8f | ||
|
|
7a50167222 | ||
|
|
e48d956577 | ||
|
|
7ed01a181e | ||
|
|
4119fd8433 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
name: ChromeExtension
|
name: ChromeExtension
|
||||||
path: dist
|
path: dist
|
||||||
- run: mkdir ./builds
|
- run: mkdir ./builds
|
||||||
- uses: montudor/action-zip@v1
|
- uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
|
||||||
with:
|
with:
|
||||||
args: zip -qq -r ./builds/ChromeExtension.zip ./dist
|
args: zip -qq -r ./builds/ChromeExtension.zip ./dist
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: FirefoxExtension
|
name: FirefoxExtension
|
||||||
path: dist
|
path: dist
|
||||||
- uses: montudor/action-zip@v1
|
- uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
|
||||||
with:
|
with:
|
||||||
args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
|
args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ChromeExtensionBeta
|
name: ChromeExtensionBeta
|
||||||
path: dist
|
path: dist
|
||||||
- uses: montudor/action-zip@v1
|
- uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
|
||||||
with:
|
with:
|
||||||
args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
|
args: zip -qq -r ./builds/ChromeExtensionBeta.zip ./dist
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: FirefoxExtensionBeta
|
name: FirefoxExtensionBeta
|
||||||
path: dist
|
path: dist
|
||||||
- uses: montudor/action-zip@v1
|
- uses: montudor/action-zip@0852c26906e00f8a315c704958823928d8018b28
|
||||||
with:
|
with:
|
||||||
args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist
|
args: zip -qq -r ./builds/FirefoxExtensionBeta.zip ./dist
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/take-action.yml
vendored
2
.github/workflows/take-action.yml
vendored
@@ -9,6 +9,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: take the issue
|
- name: take the issue
|
||||||
uses: bdougie/take-action@main
|
uses: bdougie/take-action@28b86cd8d25593f037406ecbf96082db2836e928
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
|||||||
2
.github/workflows/update-oss-attribution.yml
vendored
2
.github/workflows/update-oss-attribution.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
mv ./oss-attribution/attribution.txt ./public/oss-attribution/attribution.txt
|
mv ./oss-attribution/attribution.txt ./public/oss-attribution/attribution.txt
|
||||||
|
|
||||||
- name: Create pull request to update list
|
- name: Create pull request to update list
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27
|
||||||
with:
|
with:
|
||||||
commit-message: Update OSS Attribution
|
commit-message: Update OSS Attribution
|
||||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||||
|
|||||||
2
.github/workflows/updateInvidous.yml
vendored
2
.github/workflows/updateInvidous.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
run: npm run ci:invidious
|
run: npm run ci:invidious
|
||||||
|
|
||||||
- name: Create pull request to update list
|
- name: Create pull request to update list
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27
|
||||||
with:
|
with:
|
||||||
commit-message: Update Invidious List
|
commit-message: Update Invidious List
|
||||||
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const reliableCheck = mapped
|
|||||||
.filter(instance => instance.url.includes(instance.name))
|
.filter(instance => instance.url.includes(instance.name))
|
||||||
|
|
||||||
// finally map to array
|
// finally map to array
|
||||||
const result: string[] = reliableCheck.map(instance => instance.name)
|
const result: string[] = reliableCheck.map(instance => instance.name).sort()
|
||||||
writeFile(join(__dirname, "./invidiouslist.json"), JSON.stringify(result), (err) => {
|
writeFile(join(__dirname, "./invidiouslist.json"), JSON.stringify(result), (err) => {
|
||||||
if (err) return console.log(err);
|
if (err) return console.log(err);
|
||||||
})
|
})
|
||||||
@@ -1 +1 @@
|
|||||||
["yewtu.be","vid.puffyan.us","invidious.snopyta.org","inv.riverside.rocks","invidious-us.kavin.rocks","invidious.osi.kr","tube.cthd.icu","invidious.flokinet.to","yt.artemislena.eu","invidious.mutahar.rocks","invidious.esmailelbob.xyz","youtube.076.ne.jp","invidious.weblibre.org","invidious.namazso.eu","invidious.kavin.rocks"]
|
["inv.cthd.icu","inv.riverside.rocks","invidio.xamh.de","invidious.kavin.rocks","invidious.namazso.eu","invidious.osi.kr","invidious.snopyta.org","vid.puffyan.us","yewtu.be","youtube.076.ne.jp","yt.artemislena.eu"]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "4.6.2",
|
"version": "4.7.1",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
|
|||||||
13671
package-lock.json
generated
13671
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@@ -3,39 +3,38 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "background.js",
|
"main": "background.js",
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^17.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chrome": "^0.0.188",
|
"@types/chrome": "^0.0.193",
|
||||||
"@types/firefox-webext-browser": "^94.0.1",
|
"@types/firefox-webext-browser": "^94.0.1",
|
||||||
"@types/jest": "^27.5.1",
|
"@types/jest": "^28.1.5",
|
||||||
"@types/react": "^17.0.43",
|
"@types/react": "^17.0.47",
|
||||||
"@types/react-dom": "^17.0.14",
|
"@types/react-dom": "^17.0.17",
|
||||||
"@types/selenium-webdriver": "^4.1.1",
|
"@types/selenium-webdriver": "^4.1.1",
|
||||||
"@types/wicg-mediasession": "^1.1.3",
|
"@types/wicg-mediasession": "^1.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.26.0",
|
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||||
"@typescript-eslint/parser": "^5.26.0",
|
"@typescript-eslint/parser": "^5.30.6",
|
||||||
"chromedriver": "^101.0.0",
|
"chromedriver": "^103.0.0",
|
||||||
"concurrently": "^7.2.1",
|
"concurrently": "^7.2.2",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"eslint": "^8.16.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-plugin-react": "^7.30.0",
|
"eslint-plugin-react": "^7.30.1",
|
||||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
"fork-ts-checker-webpack-plugin": "^7.2.12",
|
||||||
"jest": "^28.1.0",
|
"jest": "^28.1.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"schema-utils": "^4.0.0",
|
"schema-utils": "^4.0.0",
|
||||||
"selenium-webdriver": "^4.2.0",
|
"selenium-webdriver": "^4.3.1",
|
||||||
"speed-measure-webpack-plugin": "^1.5.0",
|
"speed-measure-webpack-plugin": "^1.5.0",
|
||||||
"ts-jest": "^28.0.3",
|
"ts-jest": "^28.0.5",
|
||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.1",
|
||||||
"ts-node": "^10.8.0",
|
"ts-node": "^10.8.2",
|
||||||
"typescript": "4.7",
|
"typescript": "4.7",
|
||||||
"web-ext": "^6.8.0",
|
"web-ext": "^7.1.1",
|
||||||
"webpack": "^5.72.1",
|
"webpack": "^5.73.0",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -867,11 +867,19 @@
|
|||||||
"message": "Hide forever"
|
"message": "Hide forever"
|
||||||
},
|
},
|
||||||
"warningChatInfo": {
|
"warningChatInfo": {
|
||||||
"message": "You got a warning and cannot submit segments temporarily. This means that we noticed you were making some common mistakes that are not malicious, please just confirm that you understand the rules and we will remove the warning. You can also join this chat using discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app"
|
"message": "We noticed you were making some common mistakes that are not malicious"
|
||||||
},
|
},
|
||||||
"voteRejectedWarning": {
|
"warningTitle": {
|
||||||
"message": "Vote rejected due to a warning. Click to open a chat to resolve it, or come back later when you have time.",
|
"message": "You got a warning"
|
||||||
"description": "This is an integrated chat panel that will appearing allowing them to talk to the Discord/Matrix chat without leaving their browser."
|
},
|
||||||
|
"questionButton": {
|
||||||
|
"message": "I have a question"
|
||||||
|
},
|
||||||
|
"warningConfirmButton": {
|
||||||
|
"message": "I understand the reason"
|
||||||
|
},
|
||||||
|
"warningError": {
|
||||||
|
"message": "Error when trying to acknowledge warning:"
|
||||||
},
|
},
|
||||||
"Donate": {
|
"Donate": {
|
||||||
"message": "Donate"
|
"message": "Donate"
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
"message": "Pomiń"
|
"message": "Pomiń"
|
||||||
},
|
},
|
||||||
"unmute": {
|
"unmute": {
|
||||||
"message": "Odcisz"
|
"message": "Anuluj wyciszenie"
|
||||||
},
|
},
|
||||||
"paused": {
|
"paused": {
|
||||||
"message": "Zatrzymany"
|
"message": "Zatrzymany"
|
||||||
@@ -246,16 +246,16 @@
|
|||||||
"message": "Pełnowymiarowe powiadomienia o przewinięciu"
|
"message": "Pełnowymiarowe powiadomienia o przewinięciu"
|
||||||
},
|
},
|
||||||
"noticeVisibilityMode1": {
|
"noticeVisibilityMode1": {
|
||||||
"message": "Małe powiadomienia o automatycznym pomijaniu"
|
"message": "Małe powiadomienia o automatycznym przewinięciu"
|
||||||
},
|
},
|
||||||
"noticeVisibilityMode2": {
|
"noticeVisibilityMode2": {
|
||||||
"message": "Małe powiadomienia o przewinięciu"
|
"message": "Małe powiadomienia o przewinięciu"
|
||||||
},
|
},
|
||||||
"noticeVisibilityMode3": {
|
"noticeVisibilityMode3": {
|
||||||
"message": "Znikające powiadomienia o automatycznym pomijaniu"
|
"message": "Półprzezroczyste powiadomienie o automatycznym przewinięciu"
|
||||||
},
|
},
|
||||||
"noticeVisibilityMode4": {
|
"noticeVisibilityMode4": {
|
||||||
"message": "Znikające powiadomienia o pomijaniu"
|
"message": "Półprzezroczyste powiadomienie dla wszystkich przewinięć"
|
||||||
},
|
},
|
||||||
"longDescription": {
|
"longDescription": {
|
||||||
"message": "SponsorBlock pozwala pomijać sponsorów, intra, outra, przypomnienia o subskrypcjach i inne irytujące fragmenty filmów na YouTube. SponsorBlock jest opartym na crowdsourcingu rozszerzeniem do przeglądarki, które pozwala każdemu zgłosić początek i koniec segmentów sponsorowanych oraz innych segmentów w filmach na YouTube. Kiedy ktoś już zamieści te informacje, wszyscy pozostali z tym rozszerzeniem będą pomijać segment sponsorowany. Możesz również pomijać fragmenty teledysków bez muzyki.",
|
"message": "SponsorBlock pozwala pomijać sponsorów, intra, outra, przypomnienia o subskrypcjach i inne irytujące fragmenty filmów na YouTube. SponsorBlock jest opartym na crowdsourcingu rozszerzeniem do przeglądarki, które pozwala każdemu zgłosić początek i koniec segmentów sponsorowanych oraz innych segmentów w filmach na YouTube. Kiedy ktoś już zamieści te informacje, wszyscy pozostali z tym rozszerzeniem będą pomijać segment sponsorowany. Możesz również pomijać fragmenty teledysków bez muzyki.",
|
||||||
@@ -549,7 +549,7 @@
|
|||||||
"message": "Zawiera płynne przejścia"
|
"message": "Zawiera płynne przejścia"
|
||||||
},
|
},
|
||||||
"generic_guideline2": {
|
"generic_guideline2": {
|
||||||
"message": "Odtwarza się, jakby nic nie zostało pominięte"
|
"message": "Pominięcie bez zauważalnego przeskoku"
|
||||||
},
|
},
|
||||||
"category_sponsor": {
|
"category_sponsor": {
|
||||||
"message": "Sponsor"
|
"message": "Sponsor"
|
||||||
@@ -649,7 +649,7 @@
|
|||||||
"message": "Nie dla sekcji, które zawierają potrzebne informacje"
|
"message": "Nie dla sekcji, które zawierają potrzebne informacje"
|
||||||
},
|
},
|
||||||
"category_filler": {
|
"category_filler": {
|
||||||
"message": "Wypełniacz nietematyczny/Żart"
|
"message": "Wypełniacz nietematyczny/żart"
|
||||||
},
|
},
|
||||||
"category_filler_description": {
|
"category_filler_description": {
|
||||||
"message": "Sceny nietematyczne dodawane tylko jako wypełniacz lub dla humoru, które nie są wymagane do zrozumienia głównej treści filmu. Nie powinno to obejmować segmentów zawierających informacje kontekstowe lub szczegółowe."
|
"message": "Sceny nietematyczne dodawane tylko jako wypełniacz lub dla humoru, które nie są wymagane do zrozumienia głównej treści filmu. Nie powinno to obejmować segmentów zawierających informacje kontekstowe lub szczegółowe."
|
||||||
@@ -664,7 +664,7 @@
|
|||||||
"message": "Rozpraszacze, wpadki, powtórki"
|
"message": "Rozpraszacze, wpadki, powtórki"
|
||||||
},
|
},
|
||||||
"category_filler_guideline3": {
|
"category_filler_guideline3": {
|
||||||
"message": "Nie dla scen wymaganych, by zrozumieć temat"
|
"message": "Nie nadaje się do scen wymaganych do zrozumienia tematu"
|
||||||
},
|
},
|
||||||
"category_music_offtopic": {
|
"category_music_offtopic": {
|
||||||
"message": "Muzyka: Sekcja niemuzyczna"
|
"message": "Muzyka: Sekcja niemuzyczna"
|
||||||
@@ -691,10 +691,10 @@
|
|||||||
"message": "Część filmu, której szuka większość osób"
|
"message": "Część filmu, której szuka większość osób"
|
||||||
},
|
},
|
||||||
"category_poi_highlight_guideline2": {
|
"category_poi_highlight_guideline2": {
|
||||||
"message": "Może pomijać kontekst"
|
"message": "Może pomóc pominąć kontekst"
|
||||||
},
|
},
|
||||||
"category_poi_highlight_guideline3": {
|
"category_poi_highlight_guideline3": {
|
||||||
"message": "Może pomijać do tytułu lub miniaturki"
|
"message": "Może pominąć do karty tytułowej lub miniaturki"
|
||||||
},
|
},
|
||||||
"category_livestream_messages": {
|
"category_livestream_messages": {
|
||||||
"message": "Transmisja live: Dotacja/Czytanie wiadomości"
|
"message": "Transmisja live: Dotacja/Czytanie wiadomości"
|
||||||
@@ -737,7 +737,7 @@
|
|||||||
"description": "Referring to the category pill that is now shown on videos that are entirely sponsor or entirely selfpromo"
|
"description": "Referring to the category pill that is now shown on videos that are entirely sponsor or entirely selfpromo"
|
||||||
},
|
},
|
||||||
"previewColor": {
|
"previewColor": {
|
||||||
"message": "Nieprzesłany kolor",
|
"message": "Kolor nieprzesłanego segmentu",
|
||||||
"description": "Referring to submissions that have not been sent to the server yet."
|
"description": "Referring to submissions that have not been sent to the server yet."
|
||||||
},
|
},
|
||||||
"seekBarColor": {
|
"seekBarColor": {
|
||||||
@@ -1007,7 +1007,7 @@
|
|||||||
"description": "Appears in Options as a tab header for advanced/niche options. To fit inside the button, it should not be longer than ~20-25 characters (depending on their width)."
|
"description": "Appears in Options as a tab header for advanced/niche options. To fit inside the button, it should not be longer than ~20-25 characters (depending on their width)."
|
||||||
},
|
},
|
||||||
"noticeVisibilityLabel": {
|
"noticeVisibilityLabel": {
|
||||||
"message": "Pomiń wygląd wpisu",
|
"message": "Wygląd okna pomijania",
|
||||||
"description": "Option label"
|
"description": "Option label"
|
||||||
},
|
},
|
||||||
"unbind": {
|
"unbind": {
|
||||||
|
|||||||
@@ -239,6 +239,9 @@
|
|||||||
"showSkipNotice": {
|
"showSkipNotice": {
|
||||||
"message": "Показувати сповіщення після пропуску сегмента"
|
"message": "Показувати сповіщення після пропуску сегмента"
|
||||||
},
|
},
|
||||||
|
"showCategoryGuidelines": {
|
||||||
|
"message": "Показати Довідку по Категоріях"
|
||||||
|
},
|
||||||
"noticeVisibilityMode0": {
|
"noticeVisibilityMode0": {
|
||||||
"message": "Повнорозмірні сповіщення про пропуски"
|
"message": "Повнорозмірні сповіщення про пропуски"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -239,6 +239,9 @@
|
|||||||
"showSkipNotice": {
|
"showSkipNotice": {
|
||||||
"message": "Hiển thị thông báo sau khi bỏ qua phân đoạn"
|
"message": "Hiển thị thông báo sau khi bỏ qua phân đoạn"
|
||||||
},
|
},
|
||||||
|
"showCategoryGuidelines": {
|
||||||
|
"message": "Hiển thị Danh mục Trợ giúp"
|
||||||
|
},
|
||||||
"noticeVisibilityMode0": {
|
"noticeVisibilityMode0": {
|
||||||
"message": "Thông báo bỏ qua với kích thước đầy đủ"
|
"message": "Thông báo bỏ qua với kích thước đầy đủ"
|
||||||
},
|
},
|
||||||
@@ -542,18 +545,36 @@
|
|||||||
"message": "đến",
|
"message": "đến",
|
||||||
"description": "Used between segments. Example: 1:20 to 1:30"
|
"description": "Used between segments. Example: 1:20 to 1:30"
|
||||||
},
|
},
|
||||||
|
"generic_guideline2": {
|
||||||
|
"message": "Chơi như thể không có gì bị bỏ qua"
|
||||||
|
},
|
||||||
"category_sponsor": {
|
"category_sponsor": {
|
||||||
"message": "Nhà tài trợ"
|
"message": "Nhà tài trợ"
|
||||||
},
|
},
|
||||||
"category_sponsor_description": {
|
"category_sponsor_description": {
|
||||||
"message": "Nội dung được trả tiền để quảng cáo, giới thiệu và quảng cáo trực tiếp. Không phải là quảng cáo không trả công hay được đề cập miễn phí."
|
"message": "Nội dung được trả tiền để quảng cáo, giới thiệu và quảng cáo trực tiếp. Không phải là quảng cáo không trả công hay được đề cập miễn phí."
|
||||||
},
|
},
|
||||||
|
"category_sponsor_guideline1": {
|
||||||
|
"message": "Quảng cáo trả phí"
|
||||||
|
},
|
||||||
|
"category_sponsor_guideline2": {
|
||||||
|
"message": "Không dành cho các khoản đóng góp"
|
||||||
|
},
|
||||||
"category_selfpromo": {
|
"category_selfpromo": {
|
||||||
"message": "Quảng cáo không trả công/Tự quảng cáo"
|
"message": "Quảng cáo không trả công/Tự quảng cáo"
|
||||||
},
|
},
|
||||||
"category_selfpromo_description": {
|
"category_selfpromo_description": {
|
||||||
"message": "Tương tự như 'nhà tài trợ' ngoại trừ việc quảng cáo không được trả tiền hay tự quảng cáo. Điều này bao gồm các phần hàng hóa, đóng góp, hoặc thông tin về người mà họ hợp tác cùng."
|
"message": "Tương tự như 'nhà tài trợ' ngoại trừ việc quảng cáo không được trả tiền hay tự quảng cáo. Điều này bao gồm các phần hàng hóa, đóng góp, hoặc thông tin về người mà họ hợp tác cùng."
|
||||||
},
|
},
|
||||||
|
"category_selfpromo_guideline1": {
|
||||||
|
"message": "Quyên góp, tư cách thành viên và hàng hóa tùy chỉnh"
|
||||||
|
},
|
||||||
|
"category_selfpromo_guideline2": {
|
||||||
|
"message": "Lời cảm ơn miễn phí không thêm vào video"
|
||||||
|
},
|
||||||
|
"category_selfpromo_guideline3": {
|
||||||
|
"message": "Không dành cho các sản phẩm và hàng hóa do công ty thiết kế"
|
||||||
|
},
|
||||||
"category_exclusive_access": {
|
"category_exclusive_access": {
|
||||||
"message": "Truy cập riêng"
|
"message": "Truy cập riêng"
|
||||||
},
|
},
|
||||||
@@ -564,12 +585,24 @@
|
|||||||
"message": "Video này giới thiệu sản phẩm, dịch vụ hoặc vị trí mà họ đã nhận được quyền truy cập miễn phí hoặc được trợ cấp",
|
"message": "Video này giới thiệu sản phẩm, dịch vụ hoặc vị trí mà họ đã nhận được quyền truy cập miễn phí hoặc được trợ cấp",
|
||||||
"description": "Short description for this category"
|
"description": "Short description for this category"
|
||||||
},
|
},
|
||||||
|
"category_exclusive_access_guideline1": {
|
||||||
|
"message": "Toàn bộ video giới thiệu nội dung nào đó có quyền truy cập miễn phí hoặc được trợ cấp"
|
||||||
|
},
|
||||||
"category_interaction": {
|
"category_interaction": {
|
||||||
"message": "Nhắc tương tác (Đăng ký)"
|
"message": "Nhắc tương tác (Đăng ký)"
|
||||||
},
|
},
|
||||||
"category_interaction_description": {
|
"category_interaction_description": {
|
||||||
"message": "Nhắc nhở người xem Thích, Đăng ký hoặc Theo dõi. Nếu nó dài hoặc là một cái gì cụ thể, nó nên là danh mục \"Tự quảng cáo\"."
|
"message": "Nhắc nhở người xem Thích, Đăng ký hoặc Theo dõi. Nếu nó dài hoặc là một cái gì cụ thể, nó nên là danh mục \"Tự quảng cáo\"."
|
||||||
},
|
},
|
||||||
|
"category_interaction_guideline1": {
|
||||||
|
"message": "Lời nhắc ngắn gọn để thích, đăng ký hoặc theo dõi"
|
||||||
|
},
|
||||||
|
"category_interaction_guideline2": {
|
||||||
|
"message": "Bao gồm lời nhắc gián tiếp để nhận xét"
|
||||||
|
},
|
||||||
|
"category_interaction_guideline3": {
|
||||||
|
"message": "Không dành cho quảng cáo chung, chỉ dành cho lời kêu gọi hành động"
|
||||||
|
},
|
||||||
"category_interaction_short": {
|
"category_interaction_short": {
|
||||||
"message": "Nhắc nhở tương tác"
|
"message": "Nhắc nhở tương tác"
|
||||||
},
|
},
|
||||||
@@ -582,18 +615,36 @@
|
|||||||
"category_intro_short": {
|
"category_intro_short": {
|
||||||
"message": "Tạm ngừng"
|
"message": "Tạm ngừng"
|
||||||
},
|
},
|
||||||
|
"category_intro_guideline1": {
|
||||||
|
"message": "Khoảng thời gian không có nội dung thực tế"
|
||||||
|
},
|
||||||
|
"category_intro_guideline2": {
|
||||||
|
"message": "Không dành cho chuyển tiếp với thông tin"
|
||||||
|
},
|
||||||
"category_outro": {
|
"category_outro": {
|
||||||
"message": "Màn hình kết thúc/Danh đề"
|
"message": "Màn hình kết thúc/Danh đề"
|
||||||
},
|
},
|
||||||
"category_outro_description": {
|
"category_outro_description": {
|
||||||
"message": "Credits hoặc khi thẻ màn hình kết thúc của YouTube xuất hiện. Không dùng với những đoạn có thông tin."
|
"message": "Credits hoặc khi thẻ màn hình kết thúc của YouTube xuất hiện. Không dùng với những đoạn có thông tin."
|
||||||
},
|
},
|
||||||
|
"category_outro_guideline1": {
|
||||||
|
"message": "Không bao gồm nội dung, ngay cả khi thẻ kết thúc ở trên màn hình"
|
||||||
|
},
|
||||||
"category_preview": {
|
"category_preview": {
|
||||||
"message": "Xem trước/Tóm tắt"
|
"message": "Xem trước/Tóm tắt"
|
||||||
},
|
},
|
||||||
"category_preview_description": {
|
"category_preview_description": {
|
||||||
"message": "Tóm tắt nhanh về tập trước/tập sau trong 1 chuỗi video (series) dài (hoặc cũng có thể là tóm tắt trước về video sắp chiếu)."
|
"message": "Tóm tắt nhanh về tập trước/tập sau trong 1 chuỗi video (series) dài (hoặc cũng có thể là tóm tắt trước về video sắp chiếu)."
|
||||||
},
|
},
|
||||||
|
"category_preview_guideline1": {
|
||||||
|
"message": "Các clip xuất hiện sau đó hoặc trong một video trong tương lai"
|
||||||
|
},
|
||||||
|
"category_preview_guideline2": {
|
||||||
|
"message": "Tóm tắt video trước đó"
|
||||||
|
},
|
||||||
|
"category_preview_guideline3": {
|
||||||
|
"message": "Không dành cho các phần thêm nội dung bổ sung"
|
||||||
|
},
|
||||||
"category_filler_description": {
|
"category_filler_description": {
|
||||||
"message": "Tập hợp các cảnh không bắt buộc để xem trong video. Điều này không bao gồm các đoạn chứa nội dung hoặc nói về ngữ cảnh của video."
|
"message": "Tập hợp các cảnh không bắt buộc để xem trong video. Điều này không bao gồm các đoạn chứa nội dung hoặc nói về ngữ cảnh của video."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -351,6 +351,7 @@
|
|||||||
.sponsorTimesInfoMessage {
|
.sponsorTimesInfoMessage {
|
||||||
font-size: 13.3333px;
|
font-size: 13.3333px;
|
||||||
color: rgb(235, 235, 235);
|
color: rgb(235, 235, 235);
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sb-guidelines-notice .sponsorTimesInfoMessage td {
|
.sb-guidelines-notice .sponsorTimesInfoMessage td {
|
||||||
@@ -543,17 +544,6 @@ input::-webkit-inner-spin-button {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sbChatNotice iframe {
|
|
||||||
height: 32px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sbChatClose {
|
|
||||||
height: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skipButtonControlBarContainer {
|
.skipButtonControlBarContainer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Registration } from "./types";
|
|||||||
window.SB = Config;
|
window.SB = Config;
|
||||||
|
|
||||||
import Utils from "./utils";
|
import Utils from "./utils";
|
||||||
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
const utils = new Utils({
|
const utils = new Utils({
|
||||||
registerFirefoxContentScript,
|
registerFirefoxContentScript,
|
||||||
unregisterFirefoxContentScript
|
unregisterFirefoxContentScript
|
||||||
@@ -205,7 +206,7 @@ async function asyncRequestToServer(type: string, address: string, data = {}) {
|
|||||||
async function sendRequestToCustomServer(type: string, url: string, data = {}) {
|
async function sendRequestToCustomServer(type: string, url: string, data = {}) {
|
||||||
// If GET, convert JSON to parameters
|
// If GET, convert JSON to parameters
|
||||||
if (type.toLowerCase() === "get") {
|
if (type.toLowerCase() === "get") {
|
||||||
url = utils.objectToURI(url, data, true);
|
url = GenericUtils.objectToURI(url, data, true);
|
||||||
|
|
||||||
data = null;
|
data = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,10 +111,10 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||||||
skipOptionSelected(event: React.ChangeEvent<HTMLSelectElement>): void {
|
skipOptionSelected(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||||
let option: CategorySkipOption;
|
let option: CategorySkipOption;
|
||||||
|
|
||||||
this.removeCurrentCategorySelection();
|
|
||||||
|
|
||||||
switch (event.target.value) {
|
switch (event.target.value) {
|
||||||
case "disable":
|
case "disable":
|
||||||
|
Config.config.categorySelections = Config.config.categorySelections.filter(
|
||||||
|
categorySelection => categorySelection.name !== this.props.category);
|
||||||
return;
|
return;
|
||||||
case "showOverlay":
|
case "showOverlay":
|
||||||
option = CategorySkipOption.ShowOverlay;
|
option = CategorySkipOption.ShowOverlay;
|
||||||
@@ -130,28 +130,17 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.config.categorySelections.push({
|
const existingSelection = Config.config.categorySelections.find(selection => selection.name === this.props.category);
|
||||||
name: this.props.category,
|
if (existingSelection) {
|
||||||
option: option
|
existingSelection.option = option;
|
||||||
});
|
} else {
|
||||||
|
Config.config.categorySelections.push({
|
||||||
// Forces the Proxy to send this to the chrome storage API
|
name: this.props.category,
|
||||||
Config.config.categorySelections = Config.config.categorySelections;
|
option: option
|
||||||
}
|
});
|
||||||
|
|
||||||
/** Removes this category from the config list of category selections */
|
|
||||||
removeCurrentCategorySelection(): void {
|
|
||||||
// Remove it if it exists
|
|
||||||
for (let i = 0; i < Config.config.categorySelections.length; i++) {
|
|
||||||
if (Config.config.categorySelections[i].name === this.props.category) {
|
|
||||||
Config.config.categorySelections.splice(i, 1);
|
|
||||||
|
|
||||||
// Forces the Proxy to send this to the chrome storage API
|
|
||||||
Config.config.categorySelections = Config.config.categorySelections;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Config.forceSyncUpdate("categorySelections");
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategorySkipOptions(): JSX.Element[] {
|
getCategorySkipOptions(): JSX.Element[] {
|
||||||
|
|||||||
@@ -36,12 +36,31 @@ class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionPr
|
|||||||
: null}
|
: null}
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{this.props.text}
|
{this.getTextElements(this.props.text)}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTextElements(text: string): Array<string | React.ReactElement> {
|
||||||
|
const elements: Array<string | React.ReactElement> = [];
|
||||||
|
const textParts = text.split(/(?=\s+)/);
|
||||||
|
for (const textPart of textParts) {
|
||||||
|
if (textPart.match(/^\s*http/)) {
|
||||||
|
elements.push(
|
||||||
|
<a href={textPart} target="_blank" rel="noreferrer">
|
||||||
|
{textPart}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
elements.push(textPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoticeTextSelectionComponent;
|
export default NoticeTextSelectionComponent;
|
||||||
116
src/content.ts
116
src/content.ts
@@ -10,7 +10,6 @@ import SkipNotice from "./render/SkipNotice";
|
|||||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||||
import SubmissionNotice from "./render/SubmissionNotice";
|
import SubmissionNotice from "./render/SubmissionNotice";
|
||||||
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
||||||
import * as Chat from "./js-components/chat";
|
|
||||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||||
import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils";
|
import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils";
|
||||||
@@ -19,6 +18,7 @@ import { CategoryPill } from "./render/CategoryPill";
|
|||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "./utils/animationUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
import { logDebug } from "./utils/logger";
|
import { logDebug } from "./utils/logger";
|
||||||
|
import { openWarningDialog } from "./utils/warnings";
|
||||||
|
|
||||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||||
@@ -74,9 +74,6 @@ let lastPreviewBarUpdate;
|
|||||||
// Is the video currently being switched
|
// Is the video currently being switched
|
||||||
let switchingVideos = null;
|
let switchingVideos = null;
|
||||||
|
|
||||||
// Made true every videoID change
|
|
||||||
let firstEvent = false;
|
|
||||||
|
|
||||||
// Used by the play and playing listeners to make sure two aren't
|
// Used by the play and playing listeners to make sure two aren't
|
||||||
// called at the same time
|
// called at the same time
|
||||||
let lastCheckTime = 0;
|
let lastCheckTime = 0;
|
||||||
@@ -120,8 +117,8 @@ let submissionNotice: SubmissionNotice = null;
|
|||||||
// If there is an advert playing (or about to be played), this is true
|
// If there is an advert playing (or about to be played), this is true
|
||||||
let isAdPlaying = false;
|
let isAdPlaying = false;
|
||||||
|
|
||||||
// last response status
|
|
||||||
let lastResponseStatus: number;
|
let lastResponseStatus: number;
|
||||||
|
let retryCount = 0;
|
||||||
|
|
||||||
// Contains all of the functions and variables needed by the skip notice
|
// Contains all of the functions and variables needed by the skip notice
|
||||||
const skipNoticeContentContainer: ContentContainer = () => ({
|
const skipNoticeContentContainer: ContentContainer = () => ({
|
||||||
@@ -278,6 +275,7 @@ if (!Config.configSyncListeners.includes(contentConfigUpdateListener)) {
|
|||||||
function resetValues() {
|
function resetValues() {
|
||||||
lastCheckTime = 0;
|
lastCheckTime = 0;
|
||||||
lastCheckVideoTime = -1;
|
lastCheckVideoTime = -1;
|
||||||
|
retryCount = 0;
|
||||||
|
|
||||||
//reset sponsor times
|
//reset sponsor times
|
||||||
sponsorTimes = null;
|
sponsorTimes = null;
|
||||||
@@ -307,8 +305,6 @@ function resetValues() {
|
|||||||
logDebug("Setting switching videos to true (reset data)");
|
logDebug("Setting switching videos to true (reset data)");
|
||||||
}
|
}
|
||||||
|
|
||||||
firstEvent = true;
|
|
||||||
|
|
||||||
// Reset advert playing flag
|
// Reset advert playing flag
|
||||||
isAdPlaying = false;
|
isAdPlaying = false;
|
||||||
|
|
||||||
@@ -512,7 +508,16 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
}
|
}
|
||||||
lastTimeFromWaitingEvent = null;
|
lastTimeFromWaitingEvent = null;
|
||||||
|
|
||||||
if (videoMuted && !inMuteSegment(currentTime)) {
|
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||||
|
|
||||||
|
const currentSkip = skipInfo.array[skipInfo.index];
|
||||||
|
const skipTime: number[] = [currentSkip?.scheduledTime, skipInfo.array[skipInfo.endIndex]?.segment[1]];
|
||||||
|
const timeUntilSponsor = skipTime?.[0] - currentTime;
|
||||||
|
const videoID = sponsorVideoID;
|
||||||
|
const skipBuffer = 0.003;
|
||||||
|
|
||||||
|
if (videoMuted && !inMuteSegment(currentTime, skipInfo.index !== -1
|
||||||
|
&& timeUntilSponsor < skipBuffer && shouldAutoSkip(currentSkip))) {
|
||||||
video.muted = false;
|
video.muted = false;
|
||||||
videoMuted = false;
|
videoMuted = false;
|
||||||
|
|
||||||
@@ -522,22 +527,15 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDebug(`Ready to start skipping: ${skipInfo.index} at ${currentTime}`);
|
||||||
|
if (skipInfo.index === -1) return;
|
||||||
|
|
||||||
if (Config.config.disableSkipping || channelWhitelisted || (channelIDInfo.status === ChannelIDStatus.Fetching && Config.config.forceChannelCheck)){
|
if (Config.config.disableSkipping || channelWhitelisted || (channelIDInfo.status === ChannelIDStatus.Fetching && Config.config.forceChannelCheck)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (incorrectVideoCheck()) return;
|
if (incorrectVideoCheck()) return;
|
||||||
|
|
||||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
|
||||||
|
|
||||||
logDebug(`Ready to start skipping: ${skipInfo.index} at ${currentTime}`);
|
|
||||||
if (skipInfo.index === -1) return;
|
|
||||||
|
|
||||||
const currentSkip = skipInfo.array[skipInfo.index];
|
|
||||||
const skipTime: number[] = [currentSkip.scheduledTime, skipInfo.array[skipInfo.endIndex].segment[1]];
|
|
||||||
const timeUntilSponsor = skipTime[0] - currentTime;
|
|
||||||
const videoID = sponsorVideoID;
|
|
||||||
|
|
||||||
// Find all indexes in between the start and end
|
// Find all indexes in between the start and end
|
||||||
let skippingSegments = [skipInfo.array[skipInfo.index]];
|
let skippingSegments = [skipInfo.array[skipInfo.index]];
|
||||||
if (skipInfo.index !== skipInfo.endIndex) {
|
if (skipInfo.index !== skipInfo.endIndex) {
|
||||||
@@ -556,7 +554,6 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
// Don't skip if this category should not be skipped
|
// Don't skip if this category should not be skipped
|
||||||
if (!shouldSkip(currentSkip) && !sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)) return;
|
if (!shouldSkip(currentSkip) && !sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)) return;
|
||||||
|
|
||||||
const skipBuffer = 0.003;
|
|
||||||
const skippingFunction = (forceVideoTime?: number) => {
|
const skippingFunction = (forceVideoTime?: number) => {
|
||||||
let forcedSkipTime: number = null;
|
let forcedSkipTime: number = null;
|
||||||
let forcedIncludeIntersectingSegments = false;
|
let forcedIncludeIntersectingSegments = false;
|
||||||
@@ -573,6 +570,19 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
openNotice: skipInfo.openNotice
|
openNotice: skipInfo.openNotice
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// These are segments that start at the exact same time but need seperate notices
|
||||||
|
for (const extra of skipInfo.extraIndexes) {
|
||||||
|
const extraSkip = skipInfo.array[extra];
|
||||||
|
if (shouldSkip(extraSkip)) {
|
||||||
|
skipToTime({
|
||||||
|
v: video,
|
||||||
|
skipTime: [extraSkip.scheduledTime, extraSkip.segment[1]],
|
||||||
|
skippingSegments: [extraSkip],
|
||||||
|
openNotice: skipInfo.openNotice
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ManualSkip
|
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ManualSkip
|
||||||
|| currentSkip.actionType === ActionType.Mute) {
|
|| currentSkip.actionType === ActionType.Mute) {
|
||||||
forcedSkipTime = skipTime[0] + 0.001;
|
forcedSkipTime = skipTime[0] + 0.001;
|
||||||
@@ -630,8 +640,10 @@ function getVirtualTime(): number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function inMuteSegment(currentTime: number): boolean {
|
function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
|
||||||
const checkFunction = (segment) => segment.actionType === ActionType.Mute && segment.segment[0] <= currentTime && segment.segment[1] > currentTime;
|
const checkFunction = (segment) => segment.actionType === ActionType.Mute
|
||||||
|
&& segment.segment[0] <= currentTime
|
||||||
|
&& (segment.segment[1] > currentTime || (includeOverlap && segment.segment[1] + 0.02 > currentTime));
|
||||||
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,8 +719,7 @@ function setupVideoListeners() {
|
|||||||
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
// This check makes sure that changing the video resolution doesn't cause the extension to think it
|
||||||
// gone back to the begining
|
// gone back to the begining
|
||||||
if (video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
|
if (video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
|
||||||
&& !firstEvent && video.currentTime === 0) return;
|
&& video.currentTime === 0) return;
|
||||||
firstEvent = false;
|
|
||||||
|
|
||||||
updateVirtualTime();
|
updateVirtualTime();
|
||||||
|
|
||||||
@@ -857,7 +868,7 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
|||||||
?.map((video) => video.segments)[0];
|
?.map((video) => video.segments)[0];
|
||||||
if (!recievedSegments || !recievedSegments.length) {
|
if (!recievedSegments || !recievedSegments.length) {
|
||||||
// return if no video found
|
// return if no video found
|
||||||
retryFetch();
|
retryFetch(404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,9 +930,7 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
|||||||
updatePreviewBar();
|
updatePreviewBar();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (lastResponseStatus === 404) {
|
retryFetch(lastResponseStatus);
|
||||||
retryFetch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.config.isVip) {
|
if (Config.config.isVip) {
|
||||||
@@ -955,16 +964,23 @@ async function lockedCategoriesLookup(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryFetch(): void {
|
function retryFetch(errorCode: number): void {
|
||||||
if (!Config.config.refetchWhenNotFound) return;
|
if (!Config.config.refetchWhenNotFound) return;
|
||||||
|
|
||||||
sponsorDataFound = false;
|
sponsorDataFound = false;
|
||||||
|
|
||||||
|
if (errorCode !== 404 && retryCount > 1) {
|
||||||
|
// Too many errors (50x), give up
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
retryCount++;
|
||||||
|
|
||||||
|
const delay = errorCode === 404 ? (10000 + Math.random() * 30000) : (2000 + Math.random() * 10000);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (sponsorVideoID && sponsorTimes?.length === 0) {
|
if (sponsorVideoID && sponsorTimes?.length === 0) {
|
||||||
sponsorsLookup();
|
sponsorsLookup();
|
||||||
}
|
}
|
||||||
}, 10000 + Math.random() * 30000);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1028,8 +1044,8 @@ function startSkipScheduleCheckingForStartSponsors() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getYouTubeVideoID(document: Document): string | boolean {
|
function getYouTubeVideoID(document: Document, url?: string): string | boolean {
|
||||||
const url = document.URL;
|
url ||= document.URL;
|
||||||
// clips should never skip, going from clip to full video has no indications.
|
// clips should never skip, going from clip to full video has no indications.
|
||||||
if (url.includes("youtube.com/clip/")) return false;
|
if (url.includes("youtube.com/clip/")) return false;
|
||||||
// skip to document and don't hide if on /embed/
|
// skip to document and don't hide if on /embed/
|
||||||
@@ -1192,13 +1208,33 @@ async function whitelistCheck() {
|
|||||||
* Returns info about the next upcoming sponsor skip
|
* Returns info about the next upcoming sponsor skip
|
||||||
*/
|
*/
|
||||||
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
|
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
|
||||||
{array: ScheduledTime[], index: number, endIndex: number, openNotice: boolean} {
|
{array: ScheduledTime[], index: number, endIndex: number, extraIndexes: number[], openNotice: boolean} {
|
||||||
|
|
||||||
|
const autoSkipSorter = (segment: ScheduledTime) => {
|
||||||
|
const skipOption = utils.getCategorySelection(segment.category)?.option;
|
||||||
|
if ((skipOption === CategorySkipOption.AutoSkip || shouldAutoSkip(segment))
|
||||||
|
&& segment.actionType === ActionType.Skip) {
|
||||||
|
return 0;
|
||||||
|
} else if (skipOption !== CategorySkipOption.ShowOverlay) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { includedTimes: submittedArray, scheduledTimes: sponsorStartTimes } =
|
const { includedTimes: submittedArray, scheduledTimes: sponsorStartTimes } =
|
||||||
getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
|
getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||||
const { scheduledTimes: sponsorStartTimesAfterCurrentTime } = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
|
const { scheduledTimes: sponsorStartTimesAfterCurrentTime } = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
|
||||||
|
|
||||||
const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
|
// This is an array in-case multiple segments have the exact same start time
|
||||||
|
const minSponsorTimeIndexes = GenericUtils.indexesOf(sponsorStartTimes, Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||||
|
// Find auto skipping segments if possible, sort by duration otherwise
|
||||||
|
const minSponsorTimeIndex = minSponsorTimeIndexes.sort(
|
||||||
|
(a, b) => ((autoSkipSorter(submittedArray[a]) - autoSkipSorter(submittedArray[b]))
|
||||||
|
|| (submittedArray[a].segment[1] - submittedArray[a].segment[0]) - (submittedArray[b].segment[1] - submittedArray[b].segment[0])))[0] ?? -1;
|
||||||
|
// Store extra indexes for the non-auto skipping segments if others occur at the exact same start time
|
||||||
|
const extraIndexes = minSponsorTimeIndexes.filter((i) => i !== minSponsorTimeIndex && autoSkipSorter(submittedArray[i]) !== 0);
|
||||||
|
|
||||||
const endTimeIndex = getLatestEndTimeIndex(submittedArray, minSponsorTimeIndex);
|
const endTimeIndex = getLatestEndTimeIndex(submittedArray, minSponsorTimeIndex);
|
||||||
|
|
||||||
const { includedTimes: unsubmittedArray, scheduledTimes: unsubmittedSponsorStartTimes } =
|
const { includedTimes: unsubmittedArray, scheduledTimes: unsubmittedSponsorStartTimes } =
|
||||||
@@ -1214,6 +1250,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
|||||||
array: submittedArray,
|
array: submittedArray,
|
||||||
index: minSponsorTimeIndex,
|
index: minSponsorTimeIndex,
|
||||||
endIndex: endTimeIndex,
|
endIndex: endTimeIndex,
|
||||||
|
extraIndexes, // Segments at same time that need seperate notices
|
||||||
openNotice: true
|
openNotice: true
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -1221,6 +1258,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
|||||||
array: unsubmittedArray,
|
array: unsubmittedArray,
|
||||||
index: minUnsubmittedSponsorTimeIndex,
|
index: minUnsubmittedSponsorTimeIndex,
|
||||||
endIndex: previewEndTimeIndex,
|
endIndex: previewEndTimeIndex,
|
||||||
|
extraIndexes: [], // No manual things for unsubmitted
|
||||||
openNotice: false
|
openNotice: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1799,10 +1837,7 @@ async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNo
|
|||||||
skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
|
skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
|
||||||
} else if (response.successType == -1) {
|
} else if (response.successType == -1) {
|
||||||
if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a warning from a moderator.")) {
|
if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a warning from a moderator.")) {
|
||||||
skipNotice.setNoticeInfoMessageWithOnClick.bind(skipNotice)(() => {
|
openWarningDialog(skipNoticeContentContainer);
|
||||||
Chat.openWarningChat(response.responseText);
|
|
||||||
skipNotice.closeListener.call(skipNotice);
|
|
||||||
}, chrome.i18n.getMessage("voteRejectedWarning"));
|
|
||||||
} else {
|
} else {
|
||||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)(GenericUtils.getErrorMessage(response.statusCode, response.responseText))
|
skipNotice.setNoticeInfoMessage.bind(skipNotice)(GenericUtils.getErrorMessage(response.statusCode, response.responseText))
|
||||||
}
|
}
|
||||||
@@ -1990,7 +2025,7 @@ async function sendSubmitMessage() {
|
|||||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
||||||
|
|
||||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
|
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
|
||||||
Chat.openWarningChat(response.responseText);
|
openWarningDialog(skipNoticeContentContainer);
|
||||||
} else {
|
} else {
|
||||||
alert(GenericUtils.getErrorMessage(response.status, response.responseText));
|
alert(GenericUtils.getErrorMessage(response.status, response.responseText));
|
||||||
}
|
}
|
||||||
@@ -2170,7 +2205,8 @@ function checkForPreloadedSegment() {
|
|||||||
const navigationApiAvailable = "navigation" in window;
|
const navigationApiAvailable = "navigation" in window;
|
||||||
if (navigationApiAvailable) {
|
if (navigationApiAvailable) {
|
||||||
// TODO: Remove type cast once type declarations are updated
|
// TODO: Remove type cast once type declarations are updated
|
||||||
(window as unknown as { navigation: EventTarget }).navigation.addEventListener("navigate", () => videoIDChange(getYouTubeVideoID(document)));
|
(window as unknown as { navigation: EventTarget }).navigation.addEventListener("navigate", (e) =>
|
||||||
|
videoIDChange(getYouTubeVideoID(document, (e as unknown as Record<string, Record<string, string>>).destination.url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record availability of Navigation API
|
// Record availability of Navigation API
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import Config from "../config";
|
|
||||||
import Utils from "../utils";
|
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
export interface ChatConfig {
|
|
||||||
displayName: string,
|
|
||||||
composerInitialValue?: string,
|
|
||||||
customDescription?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openChat(config: ChatConfig): void {
|
|
||||||
const chat = document.createElement("div");
|
|
||||||
chat.classList.add("sbChatNotice");
|
|
||||||
chat.style.zIndex = "2000";
|
|
||||||
|
|
||||||
const iframe= document.createElement("iframe");
|
|
||||||
iframe.src = "https://chat.sponsor.ajay.app/#" + utils.objectToURI("", config, false);
|
|
||||||
chat.appendChild(iframe);
|
|
||||||
|
|
||||||
const closeButton = document.createElement("img");
|
|
||||||
closeButton.classList.add("sbChatClose");
|
|
||||||
closeButton.src = chrome.extension.getURL("icons/close.png");
|
|
||||||
closeButton.addEventListener("click", () => {
|
|
||||||
chat.remove();
|
|
||||||
closeButton.remove();
|
|
||||||
});
|
|
||||||
chat.appendChild(closeButton);
|
|
||||||
|
|
||||||
const referenceNode = utils.findReferenceNode();
|
|
||||||
referenceNode.prepend(chat);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openWarningChat(warningMessage: string): Promise<void> {
|
|
||||||
const warningReasonMatch = warningMessage.match(/Warning reason: '(.+)'/);
|
|
||||||
alert(chrome.i18n.getMessage("warningChatInfo") + `\n\n${warningReasonMatch ? ` Warning reason: ${warningReasonMatch[1]}` : ``}`);
|
|
||||||
|
|
||||||
const userNameData = await utils.asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID);
|
|
||||||
const userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : "";
|
|
||||||
const publicUserID = await utils.getHash(Config.config.userID);
|
|
||||||
|
|
||||||
openChat({
|
|
||||||
displayName: `${userName ? userName : ``}${userName !== publicUserID ? ` | ${publicUserID}` : ``}`,
|
|
||||||
composerInitialValue: `I got a warning and confirm I [REMOVE THIS CAPITAL TEXT TO CONFIRM] reread the guidelines.` +
|
|
||||||
warningReasonMatch ? ` Warning reason: ${warningReasonMatch[1]}` : ``,
|
|
||||||
customDescription: chrome.i18n.getMessage("warningChatInfo")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -64,7 +64,13 @@ export default class GenericNotice {
|
|||||||
extraClass={options.extraClass}
|
extraClass={options.extraClass}
|
||||||
closeListener={() => this.close()} >
|
closeListener={() => this.close()} >
|
||||||
|
|
||||||
{this.getMessageBox(this.idSuffix, options.textBoxes)}
|
<tr id={"sponsorSkipNoticeMiddleRow" + this.idSuffix}
|
||||||
|
className="sponsorTimeMessagesRow"
|
||||||
|
style={{maxHeight: (this.contentContainer().v.offsetHeight - 200) + "px"}}>
|
||||||
|
<td style={{width: "100%"}}>
|
||||||
|
{this.getMessageBoxes(this.idSuffix, options.textBoxes)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr id={"sponsorSkipNoticeSpacer" + this.idSuffix}
|
<tr id={"sponsorSkipNoticeSpacer" + this.idSuffix}
|
||||||
className="sponsorBlockSpacer">
|
className="sponsorBlockSpacer">
|
||||||
@@ -81,7 +87,7 @@ export default class GenericNotice {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageBox(idSuffix: string, textBoxes: TextBox[]): JSX.Element[] {
|
getMessageBoxes(idSuffix: string, textBoxes: TextBox[]): JSX.Element[] {
|
||||||
if (textBoxes) {
|
if (textBoxes) {
|
||||||
const result = [];
|
const result = [];
|
||||||
for (let i = 0; i < textBoxes.length; i++) {
|
for (let i = 0; i < textBoxes.length; i++) {
|
||||||
|
|||||||
13
src/utils.ts
13
src/utils.ts
@@ -376,19 +376,6 @@ export default class Utils {
|
|||||||
return referenceNode;
|
return referenceNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
objectToURI<T>(url: string, data: T, includeQuestionMark: boolean): string {
|
|
||||||
let counter = 0;
|
|
||||||
for (const key in data) {
|
|
||||||
const seperator = (url.includes("?") || counter > 0) ? "&" : (includeQuestionMark ? "?" : "");
|
|
||||||
const value = (typeof(data[key]) === "string") ? data[key] as unknown as string : JSON.stringify(data[key]);
|
|
||||||
url += seperator + encodeURIComponent(key) + "=" + encodeURIComponent(value);
|
|
||||||
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFormattedTime(seconds: number, precise?: boolean): string {
|
getFormattedTime(seconds: number, precise?: boolean): string {
|
||||||
seconds = Math.max(seconds, 0);
|
seconds = Math.max(seconds, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -64,8 +64,31 @@ function hexToRgb(hex: string): {r: number, g: number, b: number} {
|
|||||||
} : null;
|
} : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all indexes that have the specified value
|
||||||
|
* https://stackoverflow.com/a/54954694/1985387
|
||||||
|
*/
|
||||||
|
function indexesOf<T>(array: T[], value: T): number[] {
|
||||||
|
return array.map((v, i) => v === value ? i : -1).filter(i => i !== -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectToURI<T>(url: string, data: T, includeQuestionMark: boolean): string {
|
||||||
|
let counter = 0;
|
||||||
|
for (const key in data) {
|
||||||
|
const seperator = (url.includes("?") || counter > 0) ? "&" : (includeQuestionMark ? "?" : "");
|
||||||
|
const value = (typeof(data[key]) === "string") ? data[key] as unknown as string : JSON.stringify(data[key]);
|
||||||
|
url += seperator + encodeURIComponent(key) + "=" + encodeURIComponent(value);
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
export const GenericUtils = {
|
export const GenericUtils = {
|
||||||
wait,
|
wait,
|
||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
getLuminance
|
getLuminance,
|
||||||
|
indexesOf,
|
||||||
|
objectToURI
|
||||||
}
|
}
|
||||||
66
src/utils/warnings.ts
Normal file
66
src/utils/warnings.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import Config from "../config";
|
||||||
|
import GenericNotice, { NoticeOptions } from "../render/GenericNotice";
|
||||||
|
import { ContentContainer } from "../types";
|
||||||
|
import Utils from "../utils";
|
||||||
|
import { GenericUtils } from "./genericUtils";
|
||||||
|
const utils = new Utils();
|
||||||
|
|
||||||
|
export interface ChatConfig {
|
||||||
|
displayName: string,
|
||||||
|
composerInitialValue?: string,
|
||||||
|
customDescription?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openWarningDialog(contentContainer: ContentContainer): Promise<void> {
|
||||||
|
const userInfo = await utils.asyncRequestToServer("GET", "/api/userInfo", {
|
||||||
|
userID: Config.config.userID,
|
||||||
|
values: ["warningReason"]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userInfo.ok) {
|
||||||
|
const warningReason = JSON.parse(userInfo.responseText)?.warningReason;
|
||||||
|
const userNameData = await utils.asyncRequestToServer("GET", "/api/getUsername?userID=" + Config.config.userID);
|
||||||
|
const userName = userNameData.ok ? JSON.parse(userNameData.responseText).userName : "";
|
||||||
|
const publicUserID = await utils.getHash(Config.config.userID);
|
||||||
|
|
||||||
|
let notice: GenericNotice = null;
|
||||||
|
const options: NoticeOptions = {
|
||||||
|
title: chrome.i18n.getMessage("warningTitle"),
|
||||||
|
textBoxes: [{
|
||||||
|
text: chrome.i18n.getMessage("warningChatInfo"),
|
||||||
|
icon: null
|
||||||
|
}, ...warningReason.split("\n").map((reason) => ({
|
||||||
|
text: reason,
|
||||||
|
icon: null
|
||||||
|
}))],
|
||||||
|
buttons: [{
|
||||||
|
name: chrome.i18n.getMessage("questionButton"),
|
||||||
|
listener: () => openChat({
|
||||||
|
displayName: `${userName ? userName : ``}${userName !== publicUserID ? ` | ${publicUserID}` : ``}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: chrome.i18n.getMessage("warningConfirmButton"),
|
||||||
|
listener: async () => {
|
||||||
|
const result = await utils.asyncRequestToServer("POST", "/api/warnUser", {
|
||||||
|
userID: Config.config.userID,
|
||||||
|
enabled: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.ok) {
|
||||||
|
notice?.close();
|
||||||
|
} else {
|
||||||
|
alert(`${chrome.i18n.getMessage("warningError")} ${result.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
timed: false
|
||||||
|
};
|
||||||
|
|
||||||
|
notice = new GenericNotice(contentContainer, "warningNotice", options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openChat(config: ChatConfig): void {
|
||||||
|
window.open("https://chat.sponsor.ajay.app/#" + GenericUtils.objectToURI("", config, false));
|
||||||
|
}
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
import webpack from "webpack"
|
const webpack = require("webpack");
|
||||||
import path from "path"
|
const path = require('path');
|
||||||
import { fileURLToPath } from "url"
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
import CopyPlugin from "copy-webpack-plugin"
|
const BuildManifest = require('./webpack.manifest');
|
||||||
import BuildManifest from "./webpack.manifest.cjs";
|
const srcDir = '../src/';
|
||||||
const srcDir = "../src/";
|
const fs = require("fs");
|
||||||
import fs from "fs";
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
|
||||||
const __dirname = path.dirname(__filename)
|
|
||||||
|
|
||||||
const edgeLanguages = [
|
const edgeLanguages = [
|
||||||
"de",
|
"de",
|
||||||
@@ -27,7 +24,7 @@ const edgeLanguages = [
|
|||||||
"zh_CN"
|
"zh_CN"
|
||||||
]
|
]
|
||||||
|
|
||||||
export default env => ({
|
module.exports = env => ({
|
||||||
entry: {
|
entry: {
|
||||||
popup: path.join(__dirname, srcDir + 'popup.ts'),
|
popup: path.join(__dirname, srcDir + 'popup.ts'),
|
||||||
background: path.join(__dirname, srcDir + 'background.ts'),
|
background: path.join(__dirname, srcDir + 'background.ts'),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { merge } from "webpack-merge";
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import common from './webpack.common.js';
|
const { merge } = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
export default env => merge(common(env), {
|
module.exports = env => merge(common(env), {
|
||||||
devtool: 'inline-source-map',
|
devtool: 'inline-source-map',
|
||||||
mode: 'development'
|
mode: 'development'
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { merge } from "webpack-merge";
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import common from './webpack.common.js';
|
const { merge } = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
export default env => {
|
module.exports = env => {
|
||||||
let mode = "production";
|
let mode = "production";
|
||||||
env.mode = mode;
|
env.mode = mode;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user