Compare commits

...

76 Commits

Author SHA1 Message Date
Ajay Ramachandran
1676e50e15 Merge pull request #357 from ajayyy/react
Categories Improvements
2020-05-20 23:52:40 -04:00
Ajay Ramachandran
f165b3b602 Removed autoUpvote config 2020-05-20 23:50:26 -04:00
Ajay Ramachandran
0f82c16940 Fixed "voted" text. 2020-05-20 23:47:13 -04:00
Ajay Ramachandran
9ef3ef03a4 Added padding to vote buttons 2020-05-20 23:36:03 -04:00
Ajay Ramachandran
ef17cae9a1 Changed to upvote/downvote from report 2020-05-20 23:32:57 -04:00
Ajay Ramachandran
05acb1669e Added segment chooser when skipping multiple segments.
Now the skip notice supports skipping multiple segments.
2020-05-19 23:21:51 -04:00
Ajay Ramachandran
0850421afb Update config.json.example 2020-05-17 19:42:11 -04:00
Ajay Ramachandran
edf06ac908 Fixed typo: sever -> server 2020-05-17 11:31:35 -04:00
Ajay Ramachandran
842d35235a Gave web-ext the beta name and made signing happen at the end. 2020-05-17 00:23:44 -04:00
Ajay Ramachandran
45d20574d9 Merge pull request #356 from ajayyy/react
Fix preview sponsors not skipping
2020-05-17 00:00:08 -04:00
Ajay Ramachandran
252da8c56a Added the ability to stop the notice timer by clicking it 2020-05-16 23:58:02 -04:00
Ajay Ramachandran
99373c3e55 Added category names to notice 2020-05-16 23:48:41 -04:00
Ajay Ramachandran
dce69b3642 Incorrect -> Incorrect/Wrong Timing 2020-05-16 23:42:10 -04:00
Ajay Ramachandran
bb670b93e9 Fix preview sponsors not skipping 2020-05-16 23:39:04 -04:00
Ajay Ramachandran
c011ad105d Merge branch 'rafern' 2020-05-15 21:27:37 -04:00
Ajay Ramachandran
856125f7fd Merge pull request #350 from rafern/master
Don't skip ads, hide controls & sponsor times preview bar when ads are playing
2020-05-15 21:25:30 -04:00
Ajay Ramachandran
178b122ab8 Added back requested changes 2020-05-15 21:20:32 -04:00
Ajay Ramachandran
7b0488d068 Reverse "requested changes" 2020-05-15 21:15:50 -04:00
Ajay Ramachandran
02a9238869 Merge branch 'master' into master 2020-05-15 21:12:31 -04:00
Ajay Ramachandran
2fb97409a1 Changed where cd is used in release workflow 2020-05-15 21:08:52 -04:00
Ajay Ramachandran
3640463112 New Crowdin translations (#353)
* New translations messages.json (Portuguese, Brazilian)

* New translations messages.json (Portuguese, Brazilian)
2020-05-15 21:00:07 -04:00
Ajay Ramachandran
d872ed642d Merge pull request #351 from ajayyy/react
Category Improvements
2020-05-15 20:56:09 -04:00
Ajay Ramachandran
1ab1f33caf Change zip binary in release workflow 2020-05-15 20:51:24 -04:00
Ajay Ramachandran
ba13f5951e Increase version number 2020-05-15 19:40:24 -04:00
Ajay Ramachandran
f66e8ddf92 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into react 2020-05-15 19:40:12 -04:00
Ajay Ramachandran
cff72b19c7 Enable category vote on main server 2020-05-15 19:39:37 -04:00
rafern
c046df7d18 Requested changes 2020-05-14 10:58:30 +01:00
Ajay Ramachandran
941bd41cdb Added another check to prevent phantom skips 2020-05-13 21:11:00 -04:00
Rafael
dd5ed6ce42 Remove duplicate code 2020-05-13 17:45:08 +01:00
rafern
3a0d5221f6 Don't skip ads, hide controls & bar when ad playing 2020-05-13 17:29:34 +01:00
Ajay Ramachandran
a72f571bd4 Update manifest.json 2020-05-12 22:03:38 -04:00
Ajay Ramachandran
50c9c9fe8a Merge pull request #349 from ajayyy/react
Hotfixes for 1.2.28.1
2020-05-12 21:56:14 -04:00
Ajay Ramachandran
7f8badb34d New Crowdin translations (#348)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (Chinese Simplified)
2020-05-12 21:55:43 -04:00
Ajay Ramachandran
f9d8daeca0 Upgraded web-ext 2020-05-12 21:18:07 -04:00
Ajay Ramachandran
55070d5852 Fixed mutation listener stack overflow on Firefox. 2020-05-12 21:17:52 -04:00
Ajay Ramachandran
789bd5939b Fixed zipping command in release workflow 2020-05-11 21:13:19 -04:00
Ajay Ramachandran
b591fbfc4b Removed log 2020-05-11 21:04:28 -04:00
Ajay Ramachandran
57eb122fce Changed rough time calculation to only use real time or end time.
Also fixed getting the replay button element.
2020-05-11 21:04:10 -04:00
Ajay Ramachandran
aec287f0cf Fixed preview bar hover text error 2020-05-11 20:59:30 -04:00
Ajay Ramachandran
c68aabaa40 Added null coalescence checks to video info 2020-05-11 20:45:48 -04:00
Ajay Ramachandran
167cff4693 New Crowdin translations (#346)
* New translations messages.json (French)

* New translations messages.json (German)

* New translations messages.json (Polish)

* New translations messages.json (Russian)

* New translations messages.json (Swedish)

* New translations messages.json (Chinese Simplified)

* New translations messages.json (Ukrainian)

* New translations messages.json (Turkish)

* New translations messages.json (Slovak)

* New translations messages.json (Turkish)
2020-05-11 19:31:11 -04:00
Ajay Ramachandran
99c343c2ce Merge pull request #347 from ajayyy/react
1.2.28 Hotfixes
2020-05-11 19:30:59 -04:00
Ajay Ramachandran
70239bc045 Increase version number 2020-05-11 19:28:02 -04:00
Ajay Ramachandran
d1748f1758 Changed endscreen check to use replay button instead. 2020-05-11 19:27:26 -04:00
Ajay Ramachandran
ac3d27bf88 Changed instances of splice to only remove one 2020-05-11 18:36:07 -04:00
Ajay Ramachandran
3fb5c1c14f Only splice 1 when removing offtopic category selection 2020-05-11 18:35:07 -04:00
Ajay Ramachandran
b1d9cde715 Added code to remove off-topic from category selections.
That category has been removed.
2020-05-11 18:28:27 -04:00
Ajay Ramachandran
aa8225c196 Fixed release CI 2020-05-10 23:16:35 -04:00
Ajay Ramachandran
918adc7c28 New Crowdin translations (#345)
* New translations messages.json (Turkish)

* New translations messages.json (Czech)

* New translations messages.json (Slovak)

* New translations messages.json (Turkish)

* New translations messages.json (Turkish)

* New translations messages.json (Hungarian)
2020-05-10 23:09:52 -04:00
Ajay Ramachandran
ab84b9d2e0 Merge pull request #332 from ajayyy/react
Categories Improvements
2020-05-10 23:09:40 -04:00
Ajay Ramachandran
9e0a9b4601 Increase version number 2020-05-10 22:12:13 -04:00
Ajay Ramachandran
98fd841a54 Fixed null whitelisted channel check happening after use 2020-05-10 21:28:01 -04:00
Ajay Ramachandran
3816313f1e Removed log 2020-05-10 21:26:21 -04:00
Ajay Ramachandran
a433dad080 Fix edit text on old YouTube dark theme 2020-05-10 21:23:25 -04:00
Ajay Ramachandran
dc04e045aa Use duration as current time if endcards or autoplay is visible when submitting 2020-05-10 21:06:15 -04:00
Ajay Ramachandran
2f842a0455 Remove config listener from EditComponent properly 2020-05-10 20:32:49 -04:00
Ajay Ramachandran
7c38b1f8f2 Fix new vote options table layout 2020-05-10 20:27:33 -04:00
Ajay Ramachandran
6942bcb418 Remove livestream category. 2020-05-10 20:12:35 -04:00
Ajay Ramachandran
a335ae2b72 Added warning when submitting non-music category on music video.
Also added a color for the music category.
2020-05-10 18:48:28 -04:00
Ajay Ramachandran
52020bcac6 Renamed outro category and added music and livestream categories 2020-05-10 16:59:42 -04:00
Ajay Ramachandran
5454861c78 Adjusted category names 2020-05-10 16:46:53 -04:00
Ajay Ramachandran
1217b9ed0b Added change category button for testing server 2020-05-10 16:44:53 -04:00
Ajay Ramachandran
e78a1b0fed New Crowdin translations (#342)
* New translations messages.json (Ukrainian)

* New translations messages.json (Turkish)
2020-05-07 23:34:07 -04:00
Ajay Ramachandran
a259949cd6 Update README.md 2020-05-06 20:22:11 -04:00
Ajay Ramachandran
ebf333b8c6 Removed cancel button 2020-05-05 23:36:06 -04:00
Ajay Ramachandran
9a5cb5ae0f Added another check to prevent scheduled skips from old videos 2020-05-04 16:39:01 -04:00
Ajay Ramachandran
20e1a96a84 Removed off-topic category 2020-05-04 16:27:36 -04:00
Ajay Ramachandran
822c7bda89 Added a category tooltip on hover. 2020-05-03 11:42:39 -04:00
Ajay Ramachandran
1526fbcbe8 New translations messages.json (Swedish) (#337) 2020-05-02 15:14:40 -04:00
Ajay Ramachandran
b8f4f8bebc New Crowdin translations (#334)
* New translations messages.json (Ukrainian)

* New translations messages.json (Chinese Simplified)
2020-04-29 22:50:36 -04:00
Ajay Ramachandran
e3698dcdca Fix release CI not zipping things properly 2020-04-27 16:32:26 -04:00
Ajay Ramachandran
fc3abc2b56 Fixed invidious message typo 2020-04-27 15:23:55 -04:00
Ajay Ramachandran
2a8a425627 Removed invidiou.sh from default instances 2020-04-27 15:22:19 -04:00
Ajay Ramachandran
9069f7cee6 Fixed indentation in release workflow 2020-04-27 14:07:09 -04:00
Ajay Ramachandran
a217831c22 Removed February info 2020-04-27 14:04:40 -04:00
Ajay Ramachandran
44e8b316a5 Added Firefox signing to CI 2020-04-27 00:01:45 -04:00
34 changed files with 4085 additions and 1840 deletions

View File

@@ -26,9 +26,8 @@ jobs:
name: ChromeExtension name: ChromeExtension
path: dist path: dist
- run: mkdir ./builds - run: mkdir ./builds
- uses: montudor/action-zip@v0.1.0 - name: Zip Artifacts
with: run: cd ./dist ; zip -r ../builds/ChromeExtension.zip *
args: zip -qq -r ./builds/ChromeExtension.zip ./dist
# Create Firefox artifacts # Create Firefox artifacts
- name: Create Firefox artifacts - name: Create Firefox artifacts
@@ -37,9 +36,8 @@ jobs:
with: with:
name: FirefoxExtension name: FirefoxExtension
path: dist path: dist
- uses: montudor/action-zip@v0.1.0 - name: Zip Artifacts
with: run: cd ./dist ; zip -r ../builds/FirefoxExtension.zip *
args: zip -qq -r ./builds/FirefoxExtension.zip ./dist
# Create Beta artifacts (Builds with the name changed to beta) # Create Beta artifacts (Builds with the name changed to beta)
- name: Create Chrome Beta artifacts - name: Create Chrome Beta artifacts
@@ -48,29 +46,25 @@ jobs:
with: with:
name: ChromeExtensionBeta name: ChromeExtensionBeta
path: dist path: dist
- uses: montudor/action-zip@v0.1.0 - name: Zip Artifacts
with: run: cd ./dist ; zip -r ../builds/ChromeExtensionBeta.zip *
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 # Upload each release asset
- name: Upload to release - name: Upload ChromeExtension to release
uses: Shopify/upload-to-release@master uses: Shopify/upload-to-release@master
with: with:
args: builds/ChromeExtension.zip args: builds/ChromeExtension.zip
name: ChromeExtension.zip name: ChromeExtension.zip
path: ./builds/ChromeExtension.zip path: ./builds/ChromeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to release - name: Upload ChromeExtensionBeta to release
uses: Shopify/upload-to-release@master
with:
args: builds/ChromeExtensionBeta.zip
name: ChromeExtensionBeta.zip
path: ./builds/ChromeExtensionBeta.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload FirefoxExtension to release
uses: Shopify/upload-to-release@master uses: Shopify/upload-to-release@master
with: with:
args: builds/FirefoxExtension.zip args: builds/FirefoxExtension.zip
@@ -78,3 +72,36 @@ jobs:
path: ./builds/FirefoxExtension.zip path: ./builds/FirefoxExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
# Firefox Beta
- name: Create Firefox Beta artifacts
run: npm run build:firefox -- --env.stream=beta
- uses: actions/upload-artifact@v1
with:
name: FirefoxExtensionBeta
path: dist
- name: Zip Artifacts
run: cd ./dist ; zip -r ../builds/FirefoxExtensionBeta.zip *
# Create Firefox Signed Beta version
- name: Create Firefox Signed Beta artifacts
run: npm run web-sign
env:
WEB_EXT_API_KEY: ${{ secrets.WEB_EXT_API_KEY }}
WEB_EXT_API_SECRET: ${{ secrets.WEB_EXT_API_SECRET }}
- name: Install rename
run: sudo apt-get install rename
- name: Rename signed file
run: cd ./web-ext-artifacts ; rename 's/.*/FirefoxSignedInstaller.xpi/' *
- uses: actions/upload-artifact@v1
with:
name: FirefoxExtensionSigned.xpi
path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
- name: Upload FirefoxSignedInstaller.xpi to release
uses: Shopify/upload-to-release@master
with:
args: web-ext-artifacts/FirefoxSignedInstaller.xpi
name: FirefoxSignedInstaller.xpi
path: ./web-ext-artifacts/FirefoxSignedInstaller.xpi
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -58,6 +58,8 @@ You can read the API docs [here](https://github.com/ajayyy/SponsorBlockServer#ap
# Building # Building
Rename `config.json.example` to `config.json` and adjust configuration as desired.
There are also other build scripts available. Install `npm`, then run `npm install` in the repository to install dependencies. There are also other build scripts available. Install `npm`, then run `npm install` in the repository to install dependencies.
Run `npm run build` to generate a Chrome extension. Run `npm run build` to generate a Chrome extension.

View File

@@ -1,6 +1,6 @@
{ {
"serverAddress": "https://sponsor.ajay.app", "serverAddress": "https://sponsor.ajay.app",
"testingServerAddress": "https://sponsor.ajay.app/test", "testingServerAddress": "https://sponsor.ajay.app/test",
"serverAddressComment": "This specifies the default SponsorBlock server to conect to", "serverAddressComment": "This specifies the default SponsorBlock server to connect to",
"categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "offtopic"] "categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"]
} }

View File

@@ -1,7 +1,7 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "__MSG_Name__", "short_name": "__MSG_Name__",
"version": "1.2.27", "version": "1.2.29",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_Description__", "description": "__MSG_Description__",
"content_scripts": [{ "content_scripts": [{
@@ -30,6 +30,8 @@
"icons/PlayerUploadFailedIconSponsorBlocker256px.png", "icons/PlayerUploadFailedIconSponsorBlocker256px.png",
"icons/upvote.png", "icons/upvote.png",
"icons/downvote.png", "icons/downvote.png",
"icons/thumbs_down.svg",
"icons/thumbs_up.svg",
"icons/report.png", "icons/report.png",
"icons/close.png", "icons/close.png",
"icons/beep.ogg", "icons/beep.ogg",

3171
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
"react-dom": "^16.12.0" "react-dom": "^16.12.0"
}, },
"devDependencies": { "devDependencies": {
"web-ext": "^4.0.0", "web-ext": "^4.2.0",
"@types/chrome": "0.0.91", "@types/chrome": "0.0.91",
"@types/firefox-webext-browser": "70.0.1", "@types/firefox-webext-browser": "70.0.1",
"@types/jest": "^24.0.23", "@types/jest": "^24.0.23",
@@ -32,6 +32,7 @@
}, },
"scripts": { "scripts": {
"web-run": "npm run web-run:chrome", "web-run": "npm run web-run:chrome",
"web-sign": "web-ext sign -s dist --id sponsorBlockerBETA@ajay.app",
"web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/", "web-run:firefox": "cd dist && web-ext run --start-url https://addons.mozilla.org/firefox/addon/ublock-origin/",
"web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium", "web-run:chrome": "cd dist && web-ext run --start-url https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm -t chromium",
"build": "npm run build:chrome", "build": "npm run build:chrome",

View File

@@ -1 +1,8 @@
{} {
"Sponsor": {
"message": "sponzor"
},
"Sponsors": {
"message": "sponzoři"
}
}

View File

@@ -350,9 +350,6 @@
"supportInvidious": { "supportInvidious": {
"message": "Unterstütze Invidious" "message": "Unterstütze Invidious"
}, },
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) ist ein Drittanbieter-YouTube-Client. Um Unterstützung zu aktivieren, müssen Sie die zusätzlichen Berechtigungen akzeptieren. Dies funktioniert NICHT im Incongnito-modus auf Chrome und anderen Chromium-Varianten."
},
"optionsInfo": { "optionsInfo": {
"message": "Invidious Support aktivieren, Autoskip deaktivieren, Tasten ausblenden und vieles mehr." "message": "Invidious Support aktivieren, Autoskip deaktivieren, Tasten ausblenden und vieles mehr."
}, },
@@ -489,21 +486,9 @@
"category_sponsor": { "category_sponsor": {
"message": "Sponsor" "message": "Sponsor"
}, },
"category_intro": {
"message": "Intro"
},
"category_outro": {
"message": "Outro"
},
"category_interaction": {
"message": "Interaktion (Überflüssiges Liked, Abonniert, Folgt, usw.)"
},
"category_selfpromo": { "category_selfpromo": {
"message": "Eigenwerbung und Merchandise" "message": "Eigenwerbung und Merchandise"
}, },
"category_offtopic": {
"message": "Vom Thema Abweichende Tangente (Subjektiv)"
},
"disable": { "disable": {
"message": "Deaktivieren" "message": "Deaktivieren"
}, },

View File

@@ -35,14 +35,14 @@
"Segments": { "Segments": {
"message": "sponsor segments" "message": "sponsor segments"
}, },
"noticeTitle": { "upvoteButtonInfo": {
"message": "Sponsor Skipped" "message": "Upvote this submission"
}, },
"reportButtonTitle": { "reportButtonTitle": {
"message": "Report" "message": "Report"
}, },
"reportButtonInfo": { "reportButtonInfo": {
"message": "Report this sponsor submission as incorrect." "message": "Report this submission as incorrect."
}, },
"Dismiss": { "Dismiss": {
"message": "Dismiss" "message": "Dismiss"
@@ -71,6 +71,9 @@
"paused": { "paused": {
"message": "Paused" "message": "Paused"
}, },
"manualPaused": {
"message": "Timer Stopped"
},
"confirmMSG": { "confirmMSG": {
"message": "To edit or delete individual values, click the info button or open the extension popup by clicking the extension icon in the top right corner." "message": "To edit or delete individual values, click the info button or open the extension popup by clicking the extension icon in the top right corner."
}, },
@@ -105,7 +108,7 @@
"message": "You have already voted this way before." "message": "You have already voted this way before."
}, },
"serverDown": { "serverDown": {
"message": "It seems the sever is down. Contact the dev immediately." "message": "It seems the server is down. Contact the dev immediately."
}, },
"connectionError": { "connectionError": {
"message": "A connection error has occured. Error code: " "message": "A connection error has occured. Error code: "
@@ -272,12 +275,12 @@
"errorCode": { "errorCode": {
"message": "Error Code: " "message": "Error Code: "
}, },
"noticeTitleNotSkipped": {
"message": "Skip Sponsor?"
},
"skip": { "skip": {
"message": "Skip" "message": "Skip"
}, },
"skipped": {
"message": "Skipped"
},
"disableAutoSkip": { "disableAutoSkip": {
"message": "Disable Auto Skip" "message": "Disable Auto Skip"
}, },
@@ -351,7 +354,7 @@
"message": "Support Invidious" "message": "Support Invidious"
}, },
"supportInvidiousDescription": { "supportInvidiousDescription": {
"message": "Invidious (invidio.us) is a third party YouTube client. To enable support, you must accept the extra permissions. This does NOT work in incongnito on Chrome and other Chromium variants." "message": "Invidious (invidio.us) is a third party YouTube client. To enable support, you must accept the extra permissions. This does NOT work in incognito on Chrome and other Chromium variants."
}, },
"optionsInfo": { "optionsInfo": {
"message": "Enable Invidious support, disable autoskip, hide buttons and more." "message": "Enable Invidious support, disable autoskip, hide buttons and more."
@@ -377,12 +380,6 @@
"currentInstances": { "currentInstances": {
"message": "Current Instances:" "message": "Current Instances:"
}, },
"enableAutoUpvote": {
"message": "Auto Upvote"
},
"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."
},
"minDuration": { "minDuration": {
"message": "Minimum duration (seconds):" "message": "Minimum duration (seconds):"
}, },
@@ -490,19 +487,22 @@
"message": "Sponsor" "message": "Sponsor"
}, },
"category_intro": { "category_intro": {
"message": "Intro" "message": "Intro Animation"
}, },
"category_outro": { "category_outro": {
"message": "Outro" "message": "Endcards/Credits"
}, },
"category_interaction": { "category_interaction": {
"message": "Interaction (Redundant Like, Subscribe, Follow, etc.)" "message": "Interaction Reminder (Subscribe)"
}, },
"category_selfpromo": { "category_selfpromo": {
"message": "Self-Promotion and Merchandise" "message": "Self-Promotion and Merchandise"
}, },
"category_offtopic": { "category_music_offtopic": {
"message": "Offtopic tangent (Subjective)" "message": "Music: Non-Music Section"
},
"category_livestream_messages": {
"message": "Livestream: Donation/Message Readings"
}, },
"disable": { "disable": {
"message": "Disable" "message": "Disable"
@@ -554,5 +554,17 @@
}, },
"forceChannelCheckPopup": { "forceChannelCheckPopup": {
"message": "Consider Enabling Force Channel Check Before Skipping Sponsors" "message": "Consider Enabling Force Channel Check Before Skipping Sponsors"
},
"downvoteDescription": {
"message": "Incorrect/Wrong Timing"
},
"incorrectCategory": {
"message": "Wrong Category"
},
"nonMusicCategoryOnMusic": {
"message": "This video is categorized as music. Are you sure you would like to submit segments with non-music categories? Unless this video is not actually music, you should not be submitting this segment. Please read the guidelines if you are confused."
},
"multipleSegments": {
"message": "Multiple Segments"
} }
} }

View File

@@ -350,9 +350,6 @@
"supportInvidious": { "supportInvidious": {
"message": "Soutenir Invidious" "message": "Soutenir Invidious"
}, },
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) est un client YouTube tiers. Pour l'activer, vous devez accepter les autorisations supplémentaires. Cela ne fonctionne PAS en mode incongnito sur Chrome et d'autres variantes de Chromium."
},
"optionsInfo": { "optionsInfo": {
"message": "Activer Invidious, désactiver le passage automatique, masquer les boutons et plus encore." "message": "Activer Invidious, désactiver le passage automatique, masquer les boutons et plus encore."
}, },

View File

@@ -1 +1,29 @@
{} {
"Loading": {
"message": "Betöltés..."
},
"Hide": {
"message": "Sose mutassa"
},
"paused": {
"message": "Szüneteltetve"
},
"save": {
"message": "Mentés"
},
"reset": {
"message": "Visszaállítás"
},
"cancel": {
"message": "Mégse"
},
"delete": {
"message": "Törlés"
},
"preview": {
"message": "Előnézet"
},
"edit": {
"message": "Szerkesztés"
}
}

View File

@@ -337,9 +337,6 @@
"supportInvidious": { "supportInvidious": {
"message": "Wesprzyj Invidious" "message": "Wesprzyj Invidious"
}, },
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) to nieoficjalny klient YouTube. Aby go wesprzeć musisz przyznać dodatkowe uprawnienia rozszerzeniowi. Ta opcja nie działa w incognito i innych wersjach Chromium."
},
"optionsInfo": { "optionsInfo": {
"message": "Wesprzyj Invidious, wyłącz auto przewijanie, ukryj guziki i więcej." "message": "Wesprzyj Invidious, wyłącz auto przewijanie, ukryj guziki i więcej."
}, },

View File

@@ -1,4 +1,8 @@
{ {
"Name": {
"message": "SponsorBlock",
"description": "Name of the extension."
},
"fullName": { "fullName": {
"message": "SponsorBlock para YouTube - Pule patrocínios", "message": "SponsorBlock para YouTube - Pule patrocínios",
"description": "Name of the extension." "description": "Name of the extension."
@@ -149,22 +153,22 @@
"message": "aqui" "message": "aqui"
}, },
"recordTimesDescription": { "recordTimesDescription": {
"message": "Carregue neste botão abaixo quando o patrocínio começar e quando\n acabar para registar e submetê-lo à base de dados" "message": "Clique no botão abaixo quando o patrocínio começar e quando terminar para registrar e submetê-lo à base de dados."
}, },
"popupHint": { "popupHint": {
"message": "Dica: Carregue na tecla ; enquanto num vídeo para registar o começo/fim de um patrocínio e \" para submeter" "message": "Dica: Aperte a tecla ; enquanto reproduzir o vídeo para registar o começo/fim de um patrocínio e \" para enviar. (Essa configuração pode ser mudada em opções.)"
}, },
"lastTimes": { "lastTimes": {
"message": "Intervalos de Patrocínios Escolhidos mais Recentemente" "message": "Últimos Intervalos de Patrocínios Seleciados"
}, },
"clearTimesButton": { "clearTimesButton": {
"message": "Limpar Intervalos" "message": "Limpar Intervalos"
}, },
"submitTimesButton": { "submitTimesButton": {
"message": "Submeter Intervalos" "message": "Enviar Intervalos"
}, },
"publicStats": { "publicStats": {
"message": "Isto é usado na página pública de estatísticas que mostra o quanto já contríbuíu. Veje-a" "message": "Isso é usado na página pública de estatísticas que mostra o quanto você já contríbuíu. Veja-a"
}, },
"setUsername": { "setUsername": {
"message": "Criar nomde de utilizador" "message": "Criar nomde de utilizador"
@@ -228,5 +232,339 @@
"sourceCode": { "sourceCode": {
"message": "Código fonte", "message": "Código fonte",
"description": "Used on Firefox Store Page" "description": "Used on Firefox Store Page"
},
"noticeUpdate": {
"message": "A notificação foi atualizada!",
"description": "The first line of the message displayed after the notice was upgraded."
},
"noticeUpdate2": {
"message": "Se você ainda não gostar dessa, aperte o botão não mostrar novamente.",
"description": "The second line of the message displayed after the notice was upgraded."
},
"setStartSponsorShortcut": {
"message": "Defina a tecla para que marca o início do patrocínio"
},
"setSubmitKeybind": {
"message": "Defina a tecla para enviar o segmento de patrocínio"
},
"keybindDescription": {
"message": "Selecione uma tecla apertando-a"
},
"keybindDescriptionComplete": {
"message": "A tecla foi definida para: "
},
"0": {
"message": "Tempo limite de conexão excedida. Cheque a sua conexão de internet. Se a sua internet estiver funcionando, o servidor está sobrecarregado ou fora do ar."
},
"disableSkipping": {
"message": "Desativar SponsorBlock"
},
"enableSkipping": {
"message": "Ativar SponsorBlock"
},
"yourWork": {
"message": "Suas submissões",
"description": "Used to describe the section that will show you the statistics from your submissions."
},
"502": {
"message": "O servidor parece estar sobrecarregado. Tente novamente em alguns segundos."
},
"errorCode": {
"message": "Código de erro: "
},
"noticeTitleNotSkipped": {
"message": "Pular patrocinador?"
},
"skip": {
"message": "Pular"
},
"disableAutoSkip": {
"message": "Desativar Salto Automático"
},
"enableAutoSkip": {
"message": "Ativar Salto Automático"
},
"autoSkipDescription": {
"message": "Pular automaticamente irá pular patrocínios por você. Se desabilitado, um aviso irá aparecer perguntando se deseja pular o anúncio."
},
"audioNotification": {
"message": "Notificação de áudio ao pular"
},
"audioNotificationDescription": {
"message": "A notificação de áudio ao pular irá tocar um som sempre que um patrocínio for ignorado. Se desativado (ou o pulo automático estiver desativado), nenhum som será reproduzido."
},
"youHaveSkipped": {
"message": "Você pulou "
},
"youHaveSaved": {
"message": "Você poupou "
},
"minLower": {
"message": "minuto"
},
"minsLower": {
"message": "minutos"
},
"hourLower": {
"message": "hora"
},
"hoursLower": {
"message": "horas"
},
"youHaveSavedTime": {
"message": "Você poupou outros"
},
"youHaveSavedTimeEnd": {
"message": " de suas vidas."
},
"guildlinesSummary": {
"message": "- Certifique-se de que seu segmento contém apenas patrocínio, nada mais.\n- Certifique-se de que pular esse segmento não vai pular conteúdo importante.\n- Se todo o vídeo for patrocinado, por favor não o denuncie. Um sistema completo de relatório de vídeo virá em breve.\n- Por favor, não denuncie avisos de parcialidade do vídeo (se um vídeo de avaliação for patrocinado, não pule quando eles mencionarem que é patrocinado)."
},
"statusReminder": {
"message": "Verifique status.sponsor.ajay.app para o status do servidor."
},
"changeUserID": {
"message": "Importar/Exportar seu ID de usuário"
},
"whatChangeUserID": {
"message": "Isso deve ser mantido em segredo. É como se fosse uma senha e não deve ser compartilhado com ninguém. Se alguém tiver isso, poderá se passar por você."
},
"setUserID": {
"message": "Definir ID de usuário"
},
"userIDChangeWarning": {
"message": "Atenção: A alteração do ID de usuário é permanente. Você tem certeza que deseja fazer isso? Certifique-se de fazer backup de seu ID antigo por precaução."
},
"createdBy": {
"message": "Criado por"
},
"autoSkip": {
"message": "Pular automaticamente"
},
"showSkipNotice": {
"message": "Mostrar aviso após um patrocínio ser ignorado"
},
"keybindCurrentlySet": {
"message": ". Atualmente, está definido para:"
},
"supportInvidious": {
"message": "Apoiar Invidious"
},
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) é um cliente para YouTube de terceiros. Para ativar o apoio, você precisa aceitar as permissões adicionais. Isso não funciona em modo anônimo no Chrome ou em outras variantes do Chromium."
},
"optionsInfo": {
"message": "Ativar apoio ao Invidious, desabilitar pular automaticamente, ocultar botões e mais."
},
"addInvidiousInstance": {
"message": "Adicionar instância do Invidious"
},
"addInvidiousInstanceDescription": {
"message": "Adicionar uma instância personalizada do Invidious. Deve ser formatado com APENAS o domínio. Exemplo: invidious.ajay.app"
},
"add": {
"message": "Adicionar"
},
"addInvidiousInstanceError": {
"message": "Este é um domínio inválido. Ele deve incluir APENAS a parte do domínio. Exemplo: invidious.ajay.app"
},
"resetInvidiousInstance": {
"message": "Redefinir Lista de Instâncias do Invidious"
},
"resetInvidiousInstanceAlert": {
"message": "Você está prestes a redefinir a lista de instâncias do Invidious"
},
"currentInstances": {
"message": "Instâncias Atuais:"
},
"enableAutoUpvote": {
"message": "Upvote automático"
},
"whatAutoUpvote": {
"message": "Com isto habilitado, a extensão dará upvote em todas as submissões que você ver se você não reportar. Não funcionará se o aviso estiver desativado."
},
"minDuration": {
"message": "Duração mínima (segundos):"
},
"minDurationDescription": {
"message": "Segmentos de patrocinadores menores do que o valor definido não serão pulados ou mostrados no reprodutor."
},
"shortCheck": {
"message": "A seguinte submissão é mais curta do que sua opção de duração mínima. Isto significa que já foi enviada e que está sendo ignorada devido a esta opção. Tem certeza que deseja enviar mesmo assim?"
},
"showUploadButton": {
"message": "Mostrar botão de envio"
},
"whatUploadButton": {
"message": "Este botão aparece no reprodutor do YouTube depois de ter selecionado um carimbo de data/hora e está pronto para ser enviado."
},
"customServerAddress": {
"message": "Endereço do servidor do SponsorBlock"
},
"customServerAddressDescription": {
"message": "Endereço que o SponsorBlock usa para fazer chamadas ao servidor.\nA menos que você tenha sua própria instância de servidor, isso não deve ser alterado."
},
"save": {
"message": "Salvar"
},
"reset": {
"message": "Redefinir"
},
"customAddressError": {
"message": "Este endereço não está na forma correta. Certifique-se de que possui http:// ou https:// no início e sem barras no final."
},
"areYouSureReset": {
"message": "Tem certeza que deseja redefinir?"
},
"confirmPrivacy": {
"message": "O este vídeo está marcado como não listado. Clique em cancelar se você não deseja verificar se há patrocínios."
},
"unlistedCheck": {
"message": "Ignorar vídeos não listados/privados"
},
"whatUnlistedCheck": {
"message": "Esta configuração irá diminuir um pouco o desempenho do SponsorBlock. As pesquisas do patrocinador exigem que se envie o ID do vídeo para o servidor. Se você estiver preocupado com o envio de IDs de vídeo não listados pela internet, habilite essa opção."
},
"mobileUpdateInfo": {
"message": "m.youtube.com agora é suportado"
},
"exportOptions": {
"message": "Importar/Exportar Todas as Opções"
},
"whatExportOptions": {
"message": "Essa suas preferências em JSON. Isso inclui seu ID de usuário, então lembre-se de compartilhar com cuidado."
},
"setOptions": {
"message": "Definir Opções"
},
"exportOptionsWarning": {
"message": "Aviso: Alterar as opções é permanente e pode fazer a extensão parar de funcionar. Tem certeza que deseja fazer isso? Certifique-se de fazer um backup de seu antigo por precaução."
},
"incorrectlyFormattedOptions": {
"message": "Este JSON não está formatado corretamente. Suas opções não foram alteradas."
},
"confirmNoticeTitle": {
"message": "Enviar Segmento"
},
"submit": {
"message": "Enviar"
},
"cancel": {
"message": "Cancelar"
},
"delete": {
"message": "Deletar"
},
"preview": {
"message": "Pré-visualizar"
},
"edit": {
"message": "Editar"
},
"copyDebugInformation": {
"message": "Copiar Informações de Depuração Para Área de Transferência"
},
"copyDebugInformationFailed": {
"message": "Erro ao copiar para a área de transferência"
},
"copyDebugInformationOptions": {
"message": "Copia informações para a área de transferência para serem fornecidas a um desenvolvedor quando houver um bug / quando um solicitado pelo desenvolvedor. Informações sensíveis como seu ID de usuário, canais na lista de permissões e endereço personalizado do servidor foram removidos. No entanto, ele contém informações como seu useragent, navegador, sistema operacional e número de versão de extensão. "
},
"copyDebugInformationComplete": {
"message": "A informação de depuração foi copiada para a área de transferência. Sinta-se à vontade para remover qualquer informação que prefira não compartilhar. Salve em um arquivo de texto ou cole-a no relatório de bug."
},
"theKey": {
"message": "A tecla"
},
"keyAlreadyUsedByYouTube": {
"message": "já está sendo usado pelo youtube. Por favor, selecione outra tecla."
},
"keyAlreadyUsed": {
"message": "está vinculado a outra ação. Por favor, selecione outra tecla."
},
"to": {
"message": "até",
"description": "Used between sponsor times. Example: 1:20 to 1:30"
},
"category_sponsor": {
"message": "Patrocinador"
},
"category_intro": {
"message": "Animação de Introdução"
},
"category_outro": {
"message": "Finalização/Créditos"
},
"category_interaction": {
"message": "Lembrete de interação (inscrever-se)"
},
"category_selfpromo": {
"message": "Auto-Promoção e Mercadorias"
},
"category_music_offtopic": {
"message": "Música: Seção sem música"
},
"category_livestream_messages": {
"message": "Livestream: Leituras de Doação/Mensagem"
},
"disable": {
"message": "Desativar"
},
"manualSkip": {
"message": "Pular manualmente"
},
"showOverlay": {
"message": "Mostrar barra de progresso"
},
"enableTestingServer": {
"message": "Habilitar Servidor em teste Beta"
},
"whatEnableTestingServer": {
"message": "Seus envios e votos NÃO SERÃO ENVIADOS para o servidor principal. Use isso apenas para testes."
},
"testingServerWarning": {
"message": "Todas os envios e votos NÃO SERÃO ENVIADOS para o servidor principal enquanto se conecta ao servidor de teste. Certifique-se de desativar isso quando você quiser fazer envios reais."
},
"bracketNow": {
"message": "(agora)"
},
"moreCategories": {
"message": "Mais categorias"
},
"bracketEnd": {
"message": "(Fim)"
},
"hiddenDueToDownvote": {
"message": "oculto: Downvote"
},
"hiddenDueToDuration": {
"message": "oculto: muito curto"
},
"channelDataNotFound": {
"message": "ID do canal ainda não carregado."
},
"adblockerIssue": {
"message": "Parece que algo está bloqueando o SponsorBlock de obter dados de vídeo. Isso é provavelmente o seu bloqueador de anúncios. Por favor, verifique https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock-Requests"
},
"itCouldBeAdblockerIssue": {
"message": "Se isso continuar acontecendo, pode ser causado pelo seu bloqueador de anúncios. Por favor, verifique https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock-Requests"
},
"forceChannelCheck": {
"message": "Forçar verificação do canal antes de pular os patrocínios"
},
"whatForceChannelCheck": {
"message": "Por padrão, isso pulará os patrocínios imediatamente mesmo antes de saber qual é o canal. Por padrão, alguns patrocinadores de zero segundo podem ser ignorados nos canais da lista branca. Habilitar esta opção evitará isso, mas irá fazer com que todos os saltos tenham um ligeiro atraso, já que obter o channelID pode levar algum tempo. Este atraso pode não ser perceptível se você tiver internet rápida."
},
"forceChannelCheckPopup": {
"message": "Considere habilitar a verificação de canal forçada antes de pular os patrocinadores"
},
"downvoteDescription": {
"message": "Incorreto"
},
"incorrectCategory": {
"message": "Categoria errada"
},
"nonMusicCategoryOnMusic": {
"message": "Este vídeo é classificado como música. Você tem certeza que deseja enviar segmentos com categorias que não são músicas? A menos que esse vídeo não seja de fato música, você não deve enviar esse segmento. Por favor leia as orientações se estiver em dúvidas."
} }
} }

View File

@@ -331,9 +331,6 @@
"supportInvidious": { "supportInvidious": {
"message": "Поддержка Invidious" "message": "Поддержка Invidious"
}, },
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) — это неофициальный клиент YouTube. Чтобы включить поддержку, Вам понадобится принять дополнительные разрешения. Это НЕ работает в приватном режиме в Chrome и других вариантах Chromium."
},
"optionsInfo": { "optionsInfo": {
"message": "Включить поддержку Invidious, выключить автоматический пропуск, скрыть кнопки и не только." "message": "Включить поддержку Invidious, выключить автоматический пропуск, скрыть кнопки и не только."
}, },

View File

@@ -1 +1,148 @@
{} {
"Name": {
"message": "SponsorBlock",
"description": "Name of the extension."
},
"Sponsor": {
"message": "sponzor"
},
"Sponsors": {
"message": "sponzori"
},
"noticeTitle": {
"message": "Sponzor bol Preskočený"
},
"reportButtonTitle": {
"message": "Nahlásiť"
},
"Loading": {
"message": "Načitávanie..."
},
"Mins": {
"message": "Minúty"
},
"Secs": {
"message": "Sekundy"
},
"Hide": {
"message": "Nikdy nezobrazovať"
},
"paused": {
"message": "Pozastavené"
},
"here": {
"message": "tu"
},
"hideThis": {
"message": "Skryť Toto"
},
"Options": {
"message": "Nastavenia"
},
"website": {
"message": "Webstránka",
"description": "Used on Firefox Store Page"
},
"sourceCode": {
"message": "Zdrojový Kód",
"description": "Used on Firefox Store Page"
},
"yourWork": {
"message": "Vaša Práca",
"description": "Used to describe the section that will show you the statistics from your submissions."
},
"errorCode": {
"message": "Kód chyby: "
},
"noticeTitleNotSkipped": {
"message": "Preskočiť Sponzor?"
},
"skip": {
"message": "Preskočiť"
},
"disableAutoSkip": {
"message": "Zakázať Automatické Preskočenie"
},
"enableAutoSkip": {
"message": "Povoliť Automatické Preskočenie"
},
"minLower": {
"message": "minúta"
},
"minsLower": {
"message": "minúty"
},
"hourLower": {
"message": "hodina"
},
"hoursLower": {
"message": "hodiny"
},
"setUserID": {
"message": "Nastaviť Používateľove ID"
},
"createdBy": {
"message": "Vytvoril"
},
"autoSkip": {
"message": "Automatické Preskočenie"
},
"add": {
"message": "Pridať"
},
"showUploadButton": {
"message": "Ukázať Nahrávacie Tlačidlo"
},
"save": {
"message": "Uložiť"
},
"reset": {
"message": "Vynulovať"
},
"setOptions": {
"message": "Nastaviť Nastavenia"
},
"submit": {
"message": "Poslať"
},
"cancel": {
"message": "Zrušiť"
},
"delete": {
"message": "Odstrániť"
},
"preview": {
"message": "Ukážka"
},
"edit": {
"message": "Upraviť"
},
"theKey": {
"message": "Kľúč"
},
"to": {
"message": "pre",
"description": "Used between sponsor times. Example: 1:20 to 1:30"
},
"category_sponsor": {
"message": "Sponzor"
},
"disable": {
"message": "Zakázať"
},
"manualSkip": {
"message": "Manuálne Preskočenie"
},
"bracketNow": {
"message": "(Teraz)"
},
"moreCategories": {
"message": "Viac Kategórií"
},
"bracketEnd": {
"message": "(Koniec)"
},
"channelDataNotFound": {
"message": "ID kanála nie je zatiaľ načítané."
}
}

View File

@@ -340,9 +340,6 @@
"supportInvidious": { "supportInvidious": {
"message": "Stöd Invidious" "message": "Stöd Invidious"
}, },
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) är en tredjeparts YouTubeklient. För att aktivera stöd måste du acceptera de utökade rättigheterna. Detta kommer INTE att funka i incognito i Chrome och andra Cromium-varianter."
},
"optionsInfo": { "optionsInfo": {
"message": "Aktivera Invidious stöd, avaktivera hoppa över automatiskt, dölj knappar och mer." "message": "Aktivera Invidious stöd, avaktivera hoppa över automatiskt, dölj knappar och mer."
}, },
@@ -479,15 +476,9 @@
"category_sponsor": { "category_sponsor": {
"message": "Sponsormeddelande" "message": "Sponsormeddelande"
}, },
"category_interaction": {
"message": "Interaktion (Redundant Gilla, Prenumerera, Följ, etc.)"
},
"category_selfpromo": { "category_selfpromo": {
"message": "Självreklam och egna produkter" "message": "Självreklam och egna produkter"
}, },
"category_offtopic": {
"message": "Orelevant tangent (Subjektivt)"
},
"disable": { "disable": {
"message": "Avaktivera" "message": "Avaktivera"
}, },
@@ -508,5 +499,20 @@
}, },
"bracketNow": { "bracketNow": {
"message": "(Nu)" "message": "(Nu)"
},
"moreCategories": {
"message": "Fler Kategorier"
},
"bracketEnd": {
"message": "(Slut)"
},
"channelDataNotFound": {
"message": "Kanal-ID är inte inladdat än."
},
"adblockerIssue": {
"message": "Det verkar som om något blockerar SponsorBlocks från att hämta videodata. Det beror förmodligen på din annonsblockerare. Vänligen kontrollera https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
},
"itCouldBeAdblockerIssue": {
"message": "Om detta fortsätter att inträffa, kan orsaken vara din annonsblockerare. Vänligen kontrollera https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
} }
} }

View File

@@ -1 +1,570 @@
{} {
"Name": {
"message": "SponsorBlock",
"description": "Name of the extension."
},
"fullName": {
"message": "Youtube için SponsorBlock - Sponsorlukları Atla",
"description": "Name of the extension."
},
"Description": {
"message": "YouTube videolarındaki sponsorlukları atla. İzlediğin videolardaki sponsorları diğerlerine zaman kazandırmak için raporla.",
"description": "Description of the extension."
},
"400": {
"message": "Sunucu isteğin geçersiz olduğunu iletti"
},
"429": {
"message": "Sadece bu video için çok fazla sponsor süresi belirtti, bu kadar fazla olduğuna emin misin?"
},
"409": {
"message": "Bu daha önce zaten gönderilmiş"
},
"channelWhitelisted": {
"message": "Kanal beyazlistede!"
},
"Sponsor": {
"message": "sponsor"
},
"Sponsors": {
"message": "sponsorlar"
},
"Segment": {
"message": "sponsor bölümü"
},
"Segments": {
"message": "sponsor bölümleri"
},
"noticeTitle": {
"message": "Sponsor Geçildi"
},
"reportButtonTitle": {
"message": "Raporla"
},
"reportButtonInfo": {
"message": "Bu sponsor bölümünü hatalı olarak rapor et."
},
"Dismiss": {
"message": "Yoksay"
},
"Loading": {
"message": "Yükleniyor..."
},
"Mins": {
"message": "Dakika"
},
"Secs": {
"message": "Saniye"
},
"Hide": {
"message": "Asla Gösterme"
},
"hitGoBack": {
"message": "Geldiğiniz yere dönmek istiyorsanız atlama'ya basın."
},
"unskip": {
"message": "Atlama"
},
"reskip": {
"message": "Tekrar atla"
},
"paused": {
"message": "Duraklatıldı"
},
"confirmMSG": {
"message": "Değerleri tek başına düzenlemek ve silmek için, bilgi butonuna basın veya sağ yukarıdaki eklenti simgesine tıklayarak eklenti menüsünü açın."
},
"clearThis": {
"message": "Bunu silmek istediğinize emin misiniz?\n\n"
},
"Unknown": {
"message": "Sponsor sürelerini yollarken bir sorun oluştur, lütfen tekrar deneyin."
},
"sponsorFound": {
"message": "Bu videonun sponsorları veritabanında mevcut!"
},
"sponsor404": {
"message": "Sponsor bulunamadı"
},
"sponsorStart": {
"message": "Sponsorluk Şimdi Başlıyor"
},
"sponsorEnd": {
"message": "Sponsorluk Şimdi Bitiyor"
},
"noVideoID": {
"message": "Bu sekmede YouTube videosu bulunamadı. Bu sekmenin bir YouTube sekmesi olduğundan eminseniz, bu pencereyi kapatıp, tekrar açın. Eğer o da işe yaramazsa, sekmeyi yenilemeyi deneyin."
},
"success": {
"message": "Başarılı!"
},
"voted": {
"message": "Oylandı!"
},
"voteFail": {
"message": "Bu şekilde zaten oy kullanmıştın."
},
"serverDown": {
"message": "Sunucu çökmüş gibi duruyor. Derhal geliştiriciyle iletişime geçin."
},
"connectionError": {
"message": "Bağlantı hatası oluştu. Hata kodu: "
},
"wantToSubmit": {
"message": "Bu video için sponsor sürelerini göndermek istiyor musunuz"
},
"leftTimes": {
"message": "Bazı sponsor sürelerini göndermediğinizi görüyoruz. Göndermek için sayfaya geri dönebilirsiniz (hala mevcutlar)."
},
"clearTimes": {
"message": "Sponsor Sürelerini Temizle"
},
"openPopup": {
"message": "SponsorBlock Penceresini Aç"
},
"SubmitTimes": {
"message": "Sponsor Sürelerini Gönder"
},
"submitCheck": {
"message": "Bunu göndermek istediğinize emin misiniz?"
},
"whitelistChannel": {
"message": "Kanalı Beyazlisteye Ekle"
},
"removeFromWhitelist": {
"message": "Kanalı Beyazlisteden Kaldır"
},
"voteOnTime": {
"message": "Sponsor Süresi için Oy Kullan"
},
"recordTimes": {
"message": "Sponsor Süresini Kaydet"
},
"soFarUHSubmited": {
"message": "Şu an kadar yolladığınız"
},
"savedPeopleFrom": {
"message": "İnsanların şu kadar vaktini kurtardınız "
},
"viewLeaderboard": {
"message": "Liderlik tablosunu görüntüle"
},
"here": {
"message": "burada"
},
"recordTimesDescription": {
"message": "Kaydetmek ve veritabanına yollamak için sponsorluk başladığında ve bittiğinde aşağıdaki butona tıklayın."
},
"popupHint": {
"message": "İpuçu: Sponsorluk süresinin başını ve sonunu raporlamak için video üzerindeyken noktalı virgül tuşuna, yollamak için tırnak tuşuna basınız. (Bu ayarlardan değiştirilebilir.)"
},
"lastTimes": {
"message": "Son Sponsor Mesajı Süresi Seçildi"
},
"clearTimesButton": {
"message": "Süreleri Temizle"
},
"submitTimesButton": {
"message": "Süreleri Gönder"
},
"publicStats": {
"message": "Bu, ne kadar katkı sağladığınızı göstermek için herkese açık istatistik sayfasında kullanılacaktır. Görün"
},
"setUsername": {
"message": "Kullanıcı Adı Belirle"
},
"discordAdvert": {
"message": "Öneri ve geri bildirimleriniz için resmi Discord serverımıza katılın!"
},
"hideThis": {
"message": "Bunu sakla"
},
"Options": {
"message": "Ayarlar"
},
"showButtons": {
"message": "YouTube oynatıcısında Butonları Göster"
},
"hideButtons": {
"message": "YouTube Oynatıcısındaki Butonları Gizle"
},
"hideButtonsDescription": {
"message": "Bu YouTube oynatıcısındaki bölüm geçişlerini yolladığınız butonları saklayacaktır."
},
"showInfoButton": {
"message": "YouTube Oynatıcısındaki Bilgi Butonunu Göster"
},
"hideInfoButton": {
"message": "YouTube Oynatıcısındaki Bilgi Butonunu Gizle"
},
"whatInfoButton": {
"message": "Bu, YouTube sayfasında açılan pencereyi açan butondur."
},
"hideDeleteButton": {
"message": "YouTube Oynatıcısında Silme Tuşunu Gizle"
},
"showDeleteButton": {
"message": "YouTube Oynatıcısında Silme Tuşunu Göster"
},
"whatDeleteButton": {
"message": "Bu, YouTube oynatıcısındaki mevcut video için gönderilmemiş bölümleri temizleyen butondur."
},
"disableViewTracking": {
"message": "Sponsor Atlama Takip Sayacını Devredışı Bırak"
},
"enableViewTracking": {
"message": "Sponsor Atlama Takip Sayacını Devreye Sok"
},
"whatViewTracking": {
"message": "Bu özellik, önerinizin diğerlerine ne kadar işe yaradığını bildirmek için atladığınız sponsorları kaydeder ve istenmeyen önerilerin veritabanına girmesini engellemek adına bir ölçü olarak kullanılır. Her sponsor atladığınızda eklenti sunucuya bir mesaj yollar. Umarım bu ayarı birçok kişi değiştirmez ve böylece görüntülenme sayıları doğru kalır. :)"
},
"showNotice": {
"message": "Uyarıyı Tekrar Göster"
},
"longDescription": {
"message": "SponsorBlock, YouTube'da sponsorlu bölümleri atlamanıza yardımcı olan bir eklentidir. SponsorBlock, herkesin YouTube videolarında bulunan sponsorlu bölümlerin başını ve sonunu göndermesine izin veren kitlekaynaklı bir tarayıcı eklentisidir. Bu bilgiyi biri yolladığında, bu sponsorlu bölüm herkeste atlanır.",
"description": "Full description of the extension on the store pages."
},
"website": {
"message": "Website",
"description": "Used on Firefox Store Page"
},
"sourceCode": {
"message": "Kaynak Kodu",
"description": "Used on Firefox Store Page"
},
"noticeUpdate": {
"message": "Uyarı güncellendi!",
"description": "The first line of the message displayed after the notice was upgraded."
},
"noticeUpdate2": {
"message": "Eğer hala beğenmediyseniz, asla gösterme butonuna basın.",
"description": "The second line of the message displayed after the notice was upgraded."
},
"setStartSponsorShortcut": {
"message": "Sponsor bölümünün başlangıcı için bir tuş belirleyin"
},
"setSubmitKeybind": {
"message": "Gönderim için bir tuş belirleyin"
},
"keybindDescription": {
"message": "Yazarak bir tuş seçin"
},
"keybindDescriptionComplete": {
"message": "Tuş seçimi şu tuşa ayarlandı: "
},
"0": {
"message": "Bağlantı zaman aşımına uğradı. İnternet bağlantınızı kontrol ediniz. Eğer internetiniz çalışıyor ise, büyük ihtimalle sunucuya erişilemiyor veya sunucuya aşırı yüklenilmiş olabilir."
},
"disableSkipping": {
"message": "SponsorBlock'u Devredışı Bırak"
},
"enableSkipping": {
"message": "SponsorBlock'u Devreye Sok"
},
"yourWork": {
"message": "Çalışmalarınız",
"description": "Used to describe the section that will show you the statistics from your submissions."
},
"502": {
"message": "Sunucuya aşırı yüklenilmiş gibi gözüküyor. Birazdan tekrar deneyin."
},
"errorCode": {
"message": "Hata Kodu: "
},
"noticeTitleNotSkipped": {
"message": "Sponsoru Atla?"
},
"skip": {
"message": "Atla"
},
"disableAutoSkip": {
"message": "Otomatik Atlamayı Devredışı Bırak"
},
"enableAutoSkip": {
"message": "Otomatik Atlamayı Devreye Sok"
},
"autoSkipDescription": {
"message": "Otomatik atlama sponsorları sizin için atlayacak. Kapalı olduğunda, atlamak istediğinizi soran bir uyarı ekranı belirecek."
},
"audioNotification": {
"message": "Atlamada Sesli Bildirim"
},
"audioNotificationDescription": {
"message": "Atlamada sesli bildirim, bir sponsor bölümü atlandığında bir ses çalar. Eğer devredışı bırakıldıysa (veya otomatik atlama devredışı bırakıldıysa), herhangi bir ses çalmayacak."
},
"youHaveSkipped": {
"message": "Bunu atladınız "
},
"youHaveSaved": {
"message": "Şu kadar süre kazandınız "
},
"minLower": {
"message": "dakika"
},
"minsLower": {
"message": "dakika"
},
"hourLower": {
"message": "saat"
},
"hoursLower": {
"message": "saat"
},
"youHaveSavedTime": {
"message": "İnsanların"
},
"youHaveSavedTimeEnd": {
"message": " kadar vaktini kurtardınız."
},
"guildlinesSummary": {
"message": "- Bölümünüzün sadece ücretli tanıtım bölümü olduğundan emin olun.\n- Bu bölümü atlamanın önemli içerik bölümlerini de atlamayacağından emin olun. \n- Eğer tüm bölüm sponsor ise, lütfen raporlamayın. Tüm video rapor sistemimiz yakında gelecek.\n- Eğer videoda taraflılık içeren bir uyarı var raporlamayın (Eğer bir inceleme videosu sponsorluysa, bundan bahsettikleri kısmı atlatmayın)."
},
"statusReminder": {
"message": "Sunucu durumu için status.sponsor.ajay.app kontrol edin."
},
"changeUserID": {
"message": "Kullanıcı kimliğini Dışarı/İçeri Aktar"
},
"whatChangeUserID": {
"message": "Bu gizli tutulmalıdır. Bu bir şifreye benzer ve diğerleriyle paylaşılmaması gerekir. Birinin eline geçerse, sizi taklit edebilir."
},
"setUserID": {
"message": "Kullanıcı kimliği Belirle"
},
"userIDChangeWarning": {
"message": "Uyarı: Kullanıcı kimliği değiştirmek kalıcıdır. Bunu yapmak istediğinizden emin misiniz? Eskisini yedeklediğinizden emin olun."
},
"createdBy": {
"message": "Oluşturan"
},
"autoSkip": {
"message": "Otomatik Atla"
},
"showSkipNotice": {
"message": "Sponsor Atladıktan Sonra Uyarı Göster"
},
"keybindCurrentlySet": {
"message": ". Şu an buna ayarlı:"
},
"supportInvidious": {
"message": "Invidious'a Destek Ver"
},
"supportInvidiousDescription": {
"message": "Invidious (invidio.us) üçüncü parti YouTube istemcisidir. Desteği etkinleştirmek için fazladan izinlere onay vermelisiniz. Bu Chrome ve Chromium bazlı tarayıcılarda gizli pencere modunda ÇALIŞMAZ."
},
"optionsInfo": {
"message": "Invidious desteğini, otomatik atlamayı, butonları saklamayı ve daha fazlasını etkinleştir."
},
"addInvidiousInstance": {
"message": "Invidious Oluşumu Ekle"
},
"addInvidiousInstanceDescription": {
"message": "Özel Indivious oluşumu ekle. Bu SADECE alan adıyla düzenlenmelidir. Örnek: invidious.ajay.app"
},
"add": {
"message": "Ekle"
},
"addInvidiousInstanceError": {
"message": "Bu geçersiz bir alan adı. Bu SADECE alan adı kısmını içermelidir. Örnek: invidious.ajay.app"
},
"resetInvidiousInstance": {
"message": "Invidious Oluşum Listesini Sıfırla"
},
"resetInvidiousInstanceAlert": {
"message": "Invidious Oluşum listesini sıfırlamak üzeresiniz"
},
"currentInstances": {
"message": "Mevcut Oluşumlar:"
},
"enableAutoUpvote": {
"message": "Otomatik Oy Ver"
},
"whatAutoUpvote": {
"message": "Eğer bu ayar açıksa, eklenti eğer rapor etmediyseniz gördüğünüz tüm önerileri oylayacaktır. Eğer bildirim kapalıysa, bu gerçekleşmeyecektir."
},
"minDuration": {
"message": "Minimum süre (saniye):"
},
"minDurationDescription": {
"message": "Belirlenen değerden kısa olan sponsor bölümleri atlanmayacak veya oynatıcıda gözükmeyecektir."
},
"shortCheck": {
"message": "Sıradaki öneri belirlediğiniz minimum süre ayarından daha kısa. Bu zaten yollandığı ve bu ayardan dolayı yok sayıldığı anlamına gelebilir. Göndermek istediğinizden emin misiniz?"
},
"showUploadButton": {
"message": "Karşıya Yükleme Butonunu Göster"
},
"whatUploadButton": {
"message": "Bu buton, YouTube oynatıcısında bir zaman seçtiğiniz ve göndermeye hazır olduğunuzda gözükür."
},
"customServerAddress": {
"message": "SponsorBlock Sunucu Adresi"
},
"customServerAddressDescription": {
"message": "SponsorBlock'un sunucu ile iletişimi sağlamak için kullandığı adres.\nKendi sunucu kopyanız olmadığı sürece bu değiştirilmemelidir."
},
"save": {
"message": "Kaydet"
},
"reset": {
"message": "Sıfırla"
},
"customAddressError": {
"message": "Bu adres doğru formatta değil. Başında http:// veya https:// olduğundan ve sonda / işareti olmadığından emin olun."
},
"areYouSureReset": {
"message": "Bunu sıfırlamak istediğinize emin misiniz?"
},
"confirmPrivacy": {
"message": "Bu videonun listede olmadığı belirlendi. Bu videoyu sponsorlar için kontrol etmek istemiyorsanız iptale basınız."
},
"unlistedCheck": {
"message": "Listedışı/Özel Videoları Görmezden Gel"
},
"whatUnlistedCheck": {
"message": "Bu ayar SponsorBlock eklentisini az miktarda yavaşlatacaktır. Sponsor kontrolleri, sunucuya video kimlik numarası göndermeyi gerektirir. Eğer listedışı videoların, video kimlik numaralarının internet üzerinden gönderilmesini istemiyorsanız bu seçeneği aktive edin."
},
"mobileUpdateInfo": {
"message": "m.youtube.com şu an desteklenmektedir"
},
"exportOptions": {
"message": "Bütün Ayarlarını İçe/Dışa Aktar"
},
"whatExportOptions": {
"message": "Bu, JSON formatında bütün kurulumunuzu gösterir. Kullanıcı kimliğinizi içerir, bu sebeple paylaşırken dikkatli olun."
},
"setOptions": {
"message": "Seçenekleri Ayarla"
},
"exportOptionsWarning": {
"message": "Uyarı: Ayarları değiştirmek kalıcıdır ve yüklemenizi bozabilir. Bunu yapmak istediğinizden emin misiniz? Eskisini yenilediğinizden emin olun."
},
"incorrectlyFormattedOptions": {
"message": "Bu JSON doğru formatlanmamış. Ayarlarınız değiştirilmedi."
},
"confirmNoticeTitle": {
"message": "Bölüm Gönder"
},
"submit": {
"message": "Gönder"
},
"cancel": {
"message": "İptal"
},
"delete": {
"message": "Sil"
},
"preview": {
"message": "Önizle"
},
"edit": {
"message": "Düzenle"
},
"copyDebugInformation": {
"message": "Onarım Bilgisini Panoya Kopyala"
},
"copyDebugInformationFailed": {
"message": "Panoya kopyalanamadı"
},
"copyDebugInformationOptions": {
"message": "Panoya, geliştiricinin hata gideriminde veya ulaşmak istediğinde geliştiriciye sağlamak üzerine bilgileri kaydeder. Kullanıcı kimliği, beyaz listenizdeki kanallar ve düzenlenmiş sunucu adresleri gibi hassas bilgiler silinmiştir. Ancak tarayıcı bilgileri, işletim sisteminiz ve eklenti numaranız gibi bilgileri içerebilir. "
},
"copyDebugInformationComplete": {
"message": "Bu çözüm bilgisi panoya kopyalandı. Paylaşmak istemediğiniz herhangi bir bilgiyi silmekte özgürsünüz. Bir yazı dosyası olarak kaydedin veya hata raporuna kopyalayın."
},
"theKey": {
"message": "Anahtar"
},
"keyAlreadyUsedByYouTube": {
"message": "YouTube tarafından zaten kullanımda. Lütfen başka bir anahtar seçin."
},
"keyAlreadyUsed": {
"message": "başka bir eyleme bağlı. Lütfen başka bir anahtar seçin."
},
"to": {
"message": "'e",
"description": "Used between sponsor times. Example: 1:20 to 1:30"
},
"category_sponsor": {
"message": "Sponsor"
},
"category_intro": {
"message": "Giriş Animasyonu"
},
"category_outro": {
"message": "Bitiş Ekranı/Jenerik"
},
"category_interaction": {
"message": "Etkileşim Hatırlatıcısı (Abonelik)"
},
"category_selfpromo": {
"message": "Kendi Reklamını Yapma ve Ürün"
},
"category_music_offtopic": {
"message": "Müzik: Müzik Olmayan Bölüm"
},
"category_livestream_messages": {
"message": "Canlı Yayın: Bağış/Mesaj Okuma"
},
"disable": {
"message": "Devredışı"
},
"manualSkip": {
"message": "Elle Atla"
},
"showOverlay": {
"message": "Arama Çubuğunda Göster"
},
"enableTestingServer": {
"message": "Beta Deneme Sunucusunu Devreye Sok"
},
"whatEnableTestingServer": {
"message": "Önerileriniz ve oylarınız ana sunucuya GÖNDERİLMEYECEKTİR. Bunu sadece deneme amacıyla kullanın."
},
"testingServerWarning": {
"message": "Tüm öneriler ve oylar, test sunucusuna bağlandığınız sürece ana sunucuya iletilmeyecektir. Gerçek öneriler yapmak istediğinizde bu ayarı kapatmayı unutmayın."
},
"bracketNow": {
"message": "(Şimdi)"
},
"moreCategories": {
"message": "Daha Fazla Kategori"
},
"bracketEnd": {
"message": "(Son)"
},
"hiddenDueToDownvote": {
"message": "gizlendi: eksile"
},
"hiddenDueToDuration": {
"message": "gizlendi: çok kısa"
},
"channelDataNotFound": {
"message": "Kanal kimliği henüz yüklenmedi."
},
"adblockerIssue": {
"message": "Bir şeyin SponsorBlock'un video bilgisi almasını engelliyor gibi görünüyor. Bu reklam engelleyiciniz olabilir. Lütfen şu adresi kontrol edin https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
},
"itCouldBeAdblockerIssue": {
"message": "Bunu sürekli yaşıyorsanız, reklam engelleyiciniz tarafından gerçekleşiyor olabilir. https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests adresini kontrol edin."
},
"forceChannelCheck": {
"message": "Sponsorları Atlamadan Önce Kanal Kontrolünü Zorla"
},
"whatForceChannelCheck": {
"message": "Varsayılan olarak, eklenti kanalın ne olduğunu bilmeden önce sponsorları atlayacaktır. Varsayılan olarak, beyaz listede olan kanallarda sıfır saniyelik sponsor bölümleri atlanacaktır. Bu ayarı aktifleştirmek bunu engelleyecektir ancak kanal kimlik bilgisini almak biraz vakit alacağından atlamalarda gecikmeler yaratabilir. Eğer hızlı bir internetiniz varsa bu gecikmeler farkedilmeyebilir."
},
"forceChannelCheckPopup": {
"message": "Sponsorları Atlamadan Önce Kanal Kontrolünü Zorlamayı Gözden Geçir"
},
"downvoteDescription": {
"message": "Hatalı"
},
"incorrectCategory": {
"message": "Yanlış Kategori"
},
"nonMusicCategoryOnMusic": {
"message": "Bu videonun kategorisi müzik olarak belirlenmiş. Müzik içermeyen bölümleri göndermek istediğinize emin misiniz? Eğer bu bir müzik videosu değilse, bu bölümleri göndermemelisiniz. Eğer ayırt edemiyorsanız, lütfen rehberi okuyunuz."
}
}

View File

@@ -1 +1,449 @@
{} {
"Name": {
"message": "SponsorBlock",
"description": "Name of the extension."
},
"fullName": {
"message": "SponsorBlock для YouTube - Пропускайте спонсорські вставки",
"description": "Name of the extension."
},
"Description": {
"message": "Пропускайте спонсорські вставки в відео на YouTube. Повідомляйте про спонсорські вставки в відео, які Ви дивіться, щоб заощадити час інших користувачів.",
"description": "Description of the extension."
},
"400": {
"message": "Сервер відхилив цей запит"
},
"429": {
"message": "Ви відправили занадто багато спонсорів для цього відео. Ви впевнені, що їх так багато?"
},
"409": {
"message": "Цей запит був відправлений раніше"
},
"channelWhitelisted": {
"message": "Канал доданий у білий список!"
},
"Sponsor": {
"message": "спонсори"
},
"Sponsors": {
"message": "спонсорів"
},
"Segment": {
"message": "спонсорська вставка"
},
"Segments": {
"message": "спонсорські вставки"
},
"noticeTitle": {
"message": "Спонсор пропущений"
},
"reportButtonTitle": {
"message": "Помилка"
},
"reportButtonInfo": {
"message": "Повідомити, що інформація про це спонсорський сегменті є хибною."
},
"Dismiss": {
"message": "Закрити"
},
"Loading": {
"message": "Завантаження..."
},
"Mins": {
"message": "хв"
},
"Secs": {
"message": "сек"
},
"Hide": {
"message": "Не відображати"
},
"hitGoBack": {
"message": "Натисніть «Назад», щоб повернутися назад."
},
"unskip": {
"message": "Назад"
},
"reskip": {
"message": "Пропустити"
},
"paused": {
"message": "Пауза"
},
"confirmMSG": {
"message": "Щоб змінити або видалити окремі значення, натисніть кнопку «Інформація» або відкрийте спливаюче вікно розширення, клацнувши значок розширення в правому верхньому куті."
},
"clearThis": {
"message": "Ви впевнені, що хочете видалити цю інформацію?\n\n"
},
"Unknown": {
"message": "При надсиланні звіту про спонсорський сегмент сталася помилка. Спробуйте надіслати його пізніше."
},
"sponsorFound": {
"message": "Спонсори цього відео вже знаходяться в базі даних!"
},
"sponsor404": {
"message": "Спонсорські вставки не знайдені"
},
"sponsorStart": {
"message": "Спонсорська вставка починається зараз"
},
"sponsorEnd": {
"message": "Спонсорська вставка закінчується зараз"
},
"noVideoID": {
"message": "Можливо, це не вкладка YouTube, або Ви натиснули занадто рано.\n  Якщо це вкладка YouTube,\n  закрийте це спливаюче вікно і відкрийте його знову."
},
"success": {
"message": "Успіх!"
},
"voted": {
"message": "Голос зарахований!"
},
"voteFail": {
"message": "Ви вже проголосували таким чином раніше."
},
"serverDown": {
"message": "Здається, сервер не працює. Зв'яжіться з розробником."
},
"connectionError": {
"message": "Помилка з'єднання. Код помилки: "
},
"wantToSubmit": {
"message": "Ви точно хочете надіслати звіт про спонсорські вставки у відео з ідентифікатором"
},
"leftTimes": {
"message": "Ви ще не надіслали звіти про деякі спонсорські вставки. Хочете повернутися на цю сторінку, щоб надіслати їх (вони не видаляються)."
},
"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."
},
"showInfoButton": {
"message": "Показувати кнопку інформації в плеєрі YouTube"
},
"hideInfoButton": {
"message": "Приховати кнопку інформації в плеєрі YouTube"
},
"whatInfoButton": {
"message": "Ця кнопка відкриває спливаюче вікно на сторінці YouTube."
},
"hideDeleteButton": {
"message": "Приховати кнопку видалення в плеєрі YouTube"
},
"showDeleteButton": {
"message": "Показувати кнопку видалення в плеєрі YouTube"
},
"whatDeleteButton": {
"message": "Ця кнопка дозволяє очистити всі спонсорські вставки в плеєрі YouTube."
},
"disableViewTracking": {
"message": "Вимкнути відстеження кількості пропусків спонсорських вставок"
},
"enableViewTracking": {
"message": "Увімкнути відстеження кількості пропусків спонсорських вставок"
},
"whatViewTracking": {
"message": "Ця можливість відстежує, які спонсорські вставки Ви пропустили, щоб допомогти користувачам дізнатися, наскільки їхвнесок допоміг іншим, і використовується як метрика, щоб переконатися, що спам не потрапляє у базу даних. Розширення відправляє повідомлення на сервер кожен раз, коли Ви пропускаєте спонсорську вставку. Сподіваємося, велика частина користувачів не поміняє це налаштування, так що у нас буде точна статистика переглядів :)"
},
"showNotice": {
"message": "Показувати сповіщення знову"
},
"longDescription": {
"message": "SponsorBlock - це розширення, яке пропускає спонсорські вставки в відео на YouTube. SponsorBlock - це краудсорсінгове розширення, яке дозволяє кожному надіслати час початку і кінця спонсорських сегментів в відео на YouTube. Після того, як хто-небудь надсилає цю інформацію, всі інші користувачі розширення будуть автоматично пропускати спонсорські сегменти.",
"description": "Full description of the extension on the store pages."
},
"website": {
"message": "Сайт",
"description": "Used on Firefox Store Page"
},
"sourceCode": {
"message": "Вихідний код",
"description": "Used on Firefox Store Page"
},
"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"
},
"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 тепер підтримується"
},
"confirmNoticeTitle": {
"message": "Надіслати сегмент"
},
"submit": {
"message": "Надіслати"
},
"cancel": {
"message": "Скасувати"
},
"delete": {
"message": "Видалити"
},
"preview": {
"message": "Попередній перегляд"
},
"edit": {
"message": "Редагувати"
},
"category_sponsor": {
"message": "Спонсор"
},
"disable": {
"message": "Вимкнути"
},
"manualSkip": {
"message": "Пропуск вручну"
},
"bracketNow": {
"message": "(Зараз)"
},
"moreCategories": {
"message": "Більше категорій"
},
"bracketEnd": {
"message": "(Кінець)"
}
}

View File

@@ -490,19 +490,19 @@
"message": "赞助商广告" "message": "赞助商广告"
}, },
"category_intro": { "category_intro": {
"message": "开头" "message": "介绍动画"
},
"category_outro": {
"message": "结尾"
}, },
"category_interaction": { "category_interaction": {
"message": "互动(重复多次提醒您点击喜欢,订阅,关注等" "message": "互动提醒(订阅"
}, },
"category_selfpromo": { "category_selfpromo": {
"message": "自我推销和商品" "message": "自我推销和商品"
}, },
"category_offtopic": { "category_music_offtopic": {
"message": "离题(主观的)" "message": "音乐:非音乐部分"
},
"category_livestream_messages": {
"message": "直播:捐赠/消息阅读"
}, },
"disable": { "disable": {
"message": "禁用" "message": "禁用"
@@ -527,5 +527,41 @@
}, },
"moreCategories": { "moreCategories": {
"message": "更多类别" "message": "更多类别"
},
"bracketEnd": {
"message": "(结束)"
},
"hiddenDueToDownvote": {
"message": "隐藏:差评"
},
"hiddenDueToDuration": {
"message": "隐藏:过短"
},
"channelDataNotFound": {
"message": "频道 ID 尚未加载。"
},
"adblockerIssue": {
"message": "似乎某些东西正在阻止 SponsorBlock 获取视频数据的功能。它很可能是您的广告拦截器。请查看 https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
},
"itCouldBeAdblockerIssue": {
"message": "如果这种情况不断发生,它可能是由您的广告拦截器引起的。请查看 https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
},
"forceChannelCheck": {
"message": "跳过赞助商广告前强制进行频道检查"
},
"whatForceChannelCheck": {
"message": "默认情况下,即使还未检测出当前是什么频道,也会立即跳过赞助商广告。默认情况下,即使在白名单中的频道,一些在开头的赞助商广告也可能被跳过。启用此选项将防止这种情况,但所有的跳过将会产生轻微的延迟,因为获取频道 ID 需要一点时间。如果您的网速很快,那延迟可能非常短。"
},
"forceChannelCheckPopup": {
"message": "请考虑启用跳过赞助商广告前强制进行频道检查"
},
"downvoteDescription": {
"message": "不正确"
},
"incorrectCategory": {
"message": "错误的类别"
},
"nonMusicCategoryOnMusic": {
"message": "此视频被归类为音乐。您确定要提交带有非音乐类别的片段吗?除非此视频实际上不是音乐,否则您不应提交此片段。如果您感到困惑,请阅读指南。"
} }
} }

View File

@@ -11,11 +11,28 @@
z-index: 40; z-index: 40;
} }
.sbHidden {
display: none !important;
}
.previewbar { .previewbar {
display: inline-block; display: inline-block;
height: 100%; height: 100%;
} }
/* Preview Bar page hacks */
.sbTooltipTwoTitleThumbnailOffset {
bottom: -5px !important;
}
.sbTooltipOneTitleThumbnailOffset {
bottom: 10px !important;
}
/* */
.popup { .popup {
z-index: 10; z-index: 10;
width: 100%; width: 100%;

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="thumbs_down.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="730"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"
id="path4"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 24 24"
fill="black"
width="18px"
height="18px"
version="1.1"
id="svg6"
sodipodi:docname="thumbs_up.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="730"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
inkscape:zoom="13.111111"
inkscape:cx="9"
inkscape:cy="9"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0V0z"
fill="none"
id="path2" />
<path
d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"
id="path4"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -237,23 +237,6 @@
<br/> <br/>
<br/> <br/>
<div option-type="toggle" sync-option="autoUpvote">
<label class="switch-container" label-name="__MSG_enableAutoUpvote__">
<label class="switch">
<input type="checkbox" checked>
<span class="slider round"></span>
</label>
</label>
<br/>
<br/>
<div class="small-description">__MSG_whatAutoUpvote__</div>
</div>
<br/>
<br/>
<div option-type="toggle" sync-option="trackViewCount"> <div option-type="toggle" sync-option="trackViewCount">
<label class="switch-container" label-name="__MSG_enableViewTracking__"> <label class="switch-container" label-name="__MSG_enableViewTracking__">
<label class="switch"> <label class="switch">

View File

@@ -135,7 +135,7 @@
<span id="sponsorTimesSkipsDoneDisplay" class="popupElement"> <span id="sponsorTimesSkipsDoneDisplay" class="popupElement">
0 0
</span> </span>
<span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span> (since February). <span id="sponsorTimesSkipsDoneEndWord" class="popupElement">__MSG_Segments__</span>
</div> </div>
<div id="sponsorTimeSavedContainer" class="popupElement" style="display: none"> <div id="sponsorTimeSavedContainer" class="popupElement" style="display: none">

View File

@@ -47,7 +47,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
//this allows the callback to be called later //this allows the callback to be called later
return true; return true;
case "submitVote": case "submitVote":
submitVote(request.type, request.UUID, callback); submitVote(request.type, request.UUID, request.category, callback);
//this allows the callback to be called later //this allows the callback to be called later
return true; return true;
@@ -147,7 +147,7 @@ function addSponsorTime(time, videoID, callback) {
}); });
} }
function submitVote(type, UUID, callback) { function submitVote(type, UUID, category, callback) {
let userID = Config.config.userID; let userID = Config.config.userID;
if (userID == undefined || userID === "undefined") { if (userID == undefined || userID === "undefined") {
@@ -156,8 +156,10 @@ function submitVote(type, UUID, callback) {
Config.config.userID = userID; Config.config.userID = userID;
} }
let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
//publish this vote //publish this vote
utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + "&type=" + type, function(xmlhttp, error) { utils.sendRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
callback({ callback({
successType: 1 successType: 1

View File

@@ -23,6 +23,7 @@ export interface NoticeState {
countdownTime: number, countdownTime: number,
countdownText: string, countdownText: string,
countdownManuallyPaused: boolean,
} }
class NoticeComponent extends React.Component<NoticeProps, NoticeState> { class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
@@ -55,6 +56,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
//the countdown until this notice closes //the countdown until this notice closes
countdownTime: maxCountdownTime(), countdownTime: maxCountdownTime(),
countdownText: null, countdownText: null,
countdownManuallyPaused: false
} }
} }
@@ -71,8 +73,8 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
<table id={"sponsorSkipNotice" + this.idSuffix} <table id={"sponsorSkipNotice" + this.idSuffix}
className={"sponsorSkipObject sponsorSkipNotice" + (this.props.fadeIn ? " sponsorSkipNoticeFadeIn" : "")} className={"sponsorSkipObject sponsorSkipNotice" + (this.props.fadeIn ? " sponsorSkipNoticeFadeIn" : "")}
style={noticeStyle} style={noticeStyle}
onMouseEnter={this.pauseCountdown.bind(this)} onMouseEnter={() => this.timerMouseEnter()}
onMouseLeave={this.startCountdown.bind(this)}> onMouseLeave={() => this.timerMouseLeave()}>
<tbody> <tbody>
{/* First row */} {/* First row */}
@@ -99,6 +101,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
{/* Time left */} {/* Time left */}
{this.props.timed ? ( {this.props.timed ? (
<span id={"sponsorSkipNoticeTimeLeft" + this.idSuffix} <span id={"sponsorSkipNoticeTimeLeft" + this.idSuffix}
onClick={() => this.toggleManualPause()}
className="sponsorSkipObject sponsorSkipNoticeTimeLeft"> className="sponsorSkipObject sponsorSkipNoticeTimeLeft">
{this.state.countdownText || (this.state.countdownTime + "s")} {this.state.countdownText || (this.state.countdownTime + "s")}
@@ -121,6 +124,30 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
); );
} }
timerMouseEnter() {
if (this.state.countdownManuallyPaused) return;
this.pauseCountdown();
}
timerMouseLeave() {
if (this.state.countdownManuallyPaused) return;
this.startCountdown();
}
toggleManualPause() {
this.setState({
countdownManuallyPaused: !this.state.countdownManuallyPaused
}, () => {
if (this.state.countdownManuallyPaused) {
this.pauseCountdown();
} else {
this.startCountdown();
}
});
}
//called every second to lower the countdown before hiding the notice //called every second to lower the countdown before hiding the notice
countdown() { countdown() {
if (!this.props.timed) return; if (!this.props.timed) return;
@@ -159,7 +186,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
//reset countdown and inform the user //reset countdown and inform the user
this.setState({ this.setState({
countdownTime: this.state.maxCountdownTime(), countdownTime: this.state.maxCountdownTime(),
countdownText: chrome.i18n.getMessage("paused") countdownText: this.state.countdownManuallyPaused ? chrome.i18n.getMessage("manualPaused") : chrome.i18n.getMessage("paused")
}); });
//remove the fade out class if it exists //remove the fade out class if it exists

View File

@@ -1,6 +1,7 @@
import * as React from "react"; import * as React from "react";
import * as CompileConfig from "../../config.json";
import Config from "../config" import Config from "../config"
import { ContentContainer, SponsorHideType } from "../types"; import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
import Utils from "../utils"; import Utils from "../utils";
var utils = new Utils(); var utils = new Utils();
@@ -8,9 +9,17 @@ var utils = new Utils();
import NoticeComponent from "./NoticeComponent"; import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent"; import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
enum SkipNoticeAction {
None,
Upvote,
Downvote,
CategoryVote,
Unskip
}
export interface SkipNoticeProps { export interface SkipNoticeProps {
UUID: string; segments: SponsorTime[];
autoSkip: boolean; autoSkip: boolean;
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
@@ -19,20 +28,26 @@ export interface SkipNoticeProps {
} }
export interface SkipNoticeState { export interface SkipNoticeState {
noticeTitle: string, noticeTitle: string;
messages: string[], messages: string[];
countdownTime: number, countdownTime: number;
maxCountdownTime: () => number; maxCountdownTime: () => number;
countdownText: string, countdownText: string;
unskipText: string, unskipText: string;
unskipCallback: () => void unskipCallback: (index: number) => void;
downvoting: boolean;
choosingCategory: boolean;
thanksForVotingText: boolean; //null until the voting buttons should be hidden
actionState: SkipNoticeAction;
} }
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> { class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
UUID: string; segments: SponsorTime[];
autoSkip: boolean; autoSkip: boolean;
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
@@ -43,27 +58,40 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
idSuffix: any; idSuffix: any;
noticeRef: React.MutableRefObject<NoticeComponent>; noticeRef: React.MutableRefObject<NoticeComponent>;
categoryOptionRef: React.RefObject<HTMLSelectElement>;
// Used to update on config change
configListener: () => void;
constructor(props: SkipNoticeProps) { constructor(props: SkipNoticeProps) {
super(props); super(props);
this.noticeRef = React.createRef(); this.noticeRef = React.createRef();
this.categoryOptionRef = React.createRef();
this.UUID = props.UUID; this.segments = props.segments;
this.autoSkip = props.autoSkip; this.autoSkip = props.autoSkip;
this.contentContainer = props.contentContainer; this.contentContainer = props.contentContainer;
this.audio = null; this.audio = null;
let noticeTitle = chrome.i18n.getMessage("noticeTitle"); let categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments" : "category_" + this.segments[0].category);
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
if (!this.autoSkip) { if (!this.autoSkip) {
noticeTitle = chrome.i18n.getMessage("noticeTitleNotSkipped"); noticeTitle = chrome.i18n.getMessage("skip") + " " + categoryName + "?";
} }
//add notice //add notice
this.amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length; this.amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
// Sort segments
if (this.segments.length > 1) {
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
}
//this is the suffix added at the end of every id //this is the suffix added at the end of every id
this.idSuffix = this.UUID + this.amountOfPreviousNotices; for (const segment of this.segments) {
this.idSuffix += segment.UUID;
}
this.idSuffix += this.amountOfPreviousNotices;
if (this.amountOfPreviousNotices > 0) { if (this.amountOfPreviousNotices > 0) {
//another notice exists //another notice exists
@@ -83,11 +111,18 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
countdownText: null, countdownText: null,
unskipText: chrome.i18n.getMessage("unskip"), unskipText: chrome.i18n.getMessage("unskip"),
unskipCallback: this.unskip.bind(this) unskipCallback: (index) => this.unskip(index),
downvoting: false,
choosingCategory: false,
thanksForVotingText: null,
actionState: SkipNoticeAction.None
} }
if (!this.autoSkip) { if (!this.autoSkip) {
Object.assign(this.state, this.getUnskippedModeInfo(chrome.i18n.getMessage("skip"))); // Assume manual skip is only skipping 1 submission
Object.assign(this.state, this.getUnskippedModeInfo(0, chrome.i18n.getMessage("skip")));
} }
} }
@@ -115,7 +150,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
timed={true} timed={true}
maxCountdownTime={this.state.maxCountdownTime} maxCountdownTime={this.state.maxCountdownTime}
ref={this.noticeRef} ref={this.noticeRef}
closeListener={this.props.closeListener}> closeListener={() => this.closeListener()}>
{(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}> {(Config.config.audioNotificationOnSkip) && <audio ref={(source) => { this.audio = source; }}>
<source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></source> <source src={chrome.extension.getURL("icons/beep.ogg")} type="audio/ogg"></source>
@@ -124,39 +159,50 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
{/* Text Boxes */} {/* Text Boxes */}
{this.getMessageBoxes()} {this.getMessageBoxes()}
{/* Last Row */} {/* Bottom Row */}
<tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}> <tr id={"sponsorSkipNoticeSecondRow" + this.idSuffix}>
{/* Vote Button Container */} {/* Vote Button Container */}
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix} {!this.state.thanksForVotingText ?
className="sponsorTimesVoteButtonsContainer"> <td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
className="sponsorTimesVoteButtonsContainer">
{/* Report Text */} {/* Upvote Button */}
<span id={"sponsorTimesReportText" + this.idSuffix} <img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage" className="sponsorSkipObject voteButton"
title={chrome.i18n.getMessage("reportButtonInfo")} style={{marginRight: "10px"}}
style={{marginRight: "5px"}}> src={chrome.extension.getURL("icons/thumbs_up.svg")}
title={chrome.i18n.getMessage("upvoteButtonInfo")}
onClick={() => this.prepAction(SkipNoticeAction.Upvote)}>
{chrome.i18n.getMessage("reportButtonTitle")} </img>
</span>
{/* Report Button */} {/* Report Button */}
<img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix} <img id={"sponsorTimesDownvoteButtonsContainer" + this.idSuffix}
className="sponsorSkipObject voteButton" className="sponsorSkipObject voteButton"
src={chrome.extension.getURL("icons/report.png")} src={chrome.extension.getURL("icons/thumbs_down.svg")}
title={chrome.i18n.getMessage("reportButtonInfo")} title={chrome.i18n.getMessage("reportButtonInfo")}
onClick={() => this.contentContainer().vote(0, this.UUID, this)}> onClick={() => this.adjustDownvotingState(true)}>
</img> </img>
</td> </td>
:
<td id={"sponsorTimesVoteButtonInfoMessage" + this.idSuffix}
className="sponsorTimesInfoMessage sponsorTimesVoteButtonMessage"
style={{marginRight: "10px"}}>
{this.state.thanksForVotingText}
</td>
}
{/* Unskip Button */} {/* Unskip Button */}
<td className="sponsorSkipNoticeUnskipSection"> <td className="sponsorSkipNoticeUnskipSection">
<button id={"sponsorSkipUnskipButton" + this.idSuffix} <button id={"sponsorSkipUnskipButton" + this.idSuffix}
className="sponsorSkipObject sponsorSkipNoticeButton" className="sponsorSkipObject sponsorSkipNoticeButton"
style={{marginLeft: "4px"}} style={{marginLeft: "4px"}}
onClick={this.state.unskipCallback}> onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
{this.state.unskipText} {this.state.unskipText}
</button> </button>
@@ -174,10 +220,94 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
</tr> </tr>
{/* Downvote Options Row */}
{this.state.downvoting &&
<tr id={"sponsorSkipNoticeDownvoteOptionsRow" + this.idSuffix}>
<td id={"sponsorTimesDownvoteOptionsContainer" + this.idSuffix}>
{/* Normal downvote */}
<button className="sponsorSkipObject sponsorSkipNoticeButton"
onClick={() => this.prepAction(SkipNoticeAction.Downvote)}>
{chrome.i18n.getMessage("downvoteDescription")}
</button>
{/* Category vote */}
<button className="sponsorSkipObject sponsorSkipNoticeButton"
onClick={() => this.openCategoryChooser()}>
{chrome.i18n.getMessage("incorrectCategory")}
</button>
</td>
</tr>
}
{/* Category Chooser Row */}
{this.state.choosingCategory &&
<tr id={"sponsorSkipNoticeCategoryChooserRow" + this.idSuffix}>
<td>
{/* Category Selector */}
<select id={"sponsorTimeCategories" + this.idSuffix}
className="sponsorTimeCategories"
defaultValue={this.segments[0].category} //Just default to the first segment, as we don't know which they'll choose
ref={this.categoryOptionRef}
onChange={this.categorySelectionChange.bind(this)}>
{this.getCategoryOptions()}
</select>
{/* Submit Button */}
{this.segments.length === 1 &&
<button className="sponsorSkipObject sponsorSkipNoticeButton"
onClick={() => this.prepAction(SkipNoticeAction.CategoryVote)}>
{chrome.i18n.getMessage("submit")}
</button>
}
</td>
</tr>
}
{/* Segment Chooser Row */}
{this.state.actionState !== SkipNoticeAction.None &&
<tr id={"sponsorSkipNoticeSubmissionOptionsRow" + this.idSuffix}>
<td id={"sponsorTimesSubmissionOptionsContainer" + this.idSuffix}>
{this.getSubmissionChooser()}
</td>
</tr>
}
</NoticeComponent> </NoticeComponent>
); );
} }
getSubmissionChooser(): JSX.Element[] {
let elements: JSX.Element[] = [];
for (let i = 0; i < this.segments.length; i++) {
elements.push(
<button className="sponsorSkipObject sponsorSkipNoticeButton"
onClick={() => this.performAction(i)}
key={"submission" + i + this.segments[i].category + this.idSuffix}>
{(i + 1) + ". " + chrome.i18n.getMessage("category_" + this.segments[i].category)}
</button>
);
}
return elements;
}
prepAction(action: SkipNoticeAction) {
if (this.segments.length === 1) {
this.performAction(0, action);
} else {
this.setState({
actionState: action
});
}
}
getMessageBoxes(): JSX.Element[] | JSX.Element { getMessageBoxes(): JSX.Element[] | JSX.Element {
if (this.state.messages.length === 0) { if (this.state.messages.length === 0) {
// Add a spacer if there is no text // Add a spacer if there is no text
@@ -202,32 +332,130 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements; return elements;
} }
unskip() { /**
this.contentContainer().unskipSponsorTime(this.UUID); * Performs the action from the current state
*
* @param index
*/
performAction(index: number, action?: SkipNoticeAction) {
switch (action ?? this.state.actionState) {
case SkipNoticeAction.None:
break;
case SkipNoticeAction.Upvote:
this.contentContainer().vote(1, this.segments[index].UUID, undefined, this);
break;
case SkipNoticeAction.Downvote:
this.contentContainer().vote(0, this.segments[index].UUID, undefined, this);
break;
case SkipNoticeAction.CategoryVote:
this.contentContainer().vote(undefined, this.segments[index].UUID, this.categoryOptionRef.current.value, this)
break;
case SkipNoticeAction.Unskip:
this.state.unskipCallback(index);
break;
}
this.unskippedMode(chrome.i18n.getMessage("reskip")); this.setState({
actionState: SkipNoticeAction.None
});
}
adjustDownvotingState(value: boolean) {
if (!value) this.clearConfigListener();
this.setState({
downvoting: value,
choosingCategory: false
});
}
clearConfigListener() {
if (this.configListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
this.configListener = null;
}
}
openCategoryChooser() {
// Add as a config listener
this.configListener = () => this.forceUpdate();
Config.configListeners.push(this.configListener);
this.setState({
choosingCategory: true,
downvoting: false
}, () => {
if (this.segments.length > 1) {
// Use the action selectors as a submit button
this.prepAction(SkipNoticeAction.CategoryVote);
}
});
}
getCategoryOptions() {
let elements = [];
for (const category of Config.config.categorySelections) {
elements.push(
<option value={category.name}
key={category.name}>
{chrome.i18n.getMessage("category_" + category.name)}
</option>
);
}
if (elements.length < CompileConfig.categoryList.length) {
// Add show more button
elements.push(
<option value={"moreCategories"}
key={"moreCategories"}>
{chrome.i18n.getMessage("moreCategories")}
</option>
);
}
return elements;
}
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
// See if show more categories was pressed
if (event.target.value === "moreCategories") {
// Open options page
chrome.runtime.sendMessage({"message": "openConfig"});
// Reset option to original
event.target.value = this.segments[0].category;
return;
}
}
unskip(index: number) {
this.contentContainer().unskipSponsorTime(this.segments[index]);
this.unskippedMode(index, chrome.i18n.getMessage("reskip"));
} }
/** Sets up notice to be not skipped yet */ /** Sets up notice to be not skipped yet */
unskippedMode(buttonText: string) { unskippedMode(index: number, buttonText: string) {
//setup new callback and reset countdown //setup new callback and reset countdown
this.setState(this.getUnskippedModeInfo(buttonText), () => { this.setState(this.getUnskippedModeInfo(index, buttonText), () => {
this.noticeRef.current.resetCountdown(); this.noticeRef.current.resetCountdown();
}); });
} }
getUnskippedModeInfo(buttonText: string) { getUnskippedModeInfo(index: number, buttonText: string) {
let self = this;
let maxCountdownTime = function() { let maxCountdownTime = function() {
let sponsorTime = utils.getSponsorTimeFromUUID(this.contentContainer().sponsorTimes, this.UUID); let sponsorTime = self.segments[index];
let duration = Math.round((sponsorTime.segment[1] - this.contentContainer().v.currentTime) * (1 / this.contentContainer().v.playbackRate)); let duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
return Math.max(duration, 4); return Math.max(duration, 4);
}.bind(this); };
return { return {
unskipText: buttonText, unskipText: buttonText,
unskipCallback: this.reskip.bind(this), unskipCallback: (index) => this.reskip(index),
//change max duration to however much of the sponsor is left //change max duration to however much of the sponsor is left
maxCountdownTime: maxCountdownTime, maxCountdownTime: maxCountdownTime,
@@ -236,8 +464,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
} }
reskip() { reskip(index: number) {
this.contentContainer().reskipSponsorTime(this.UUID); this.contentContainer().reskipSponsorTime(this.segments[index]);
//reset countdown //reset countdown
this.setState({ this.setState({
@@ -253,68 +481,51 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.setState({ this.setState({
noticeTitle: chrome.i18n.getMessage("noticeTitle") noticeTitle: chrome.i18n.getMessage("noticeTitle")
}); });
if(Config.config.autoUpvote) this.contentContainer().vote(1, this.UUID);
} }
} }
afterDownvote() { afterVote(segment: SponsorTime, type: number, category: string) {
this.addVoteButtonInfo(chrome.i18n.getMessage("voted")); this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
//remove this sponsor from the sponsors looked up if (type === 0) {
//find which one it is this.setNoticeInfoMessage(chrome.i18n.getMessage("hitGoBack"));
for (let i = 0; i < this.contentContainer().sponsorTimes.length; i++) { this.adjustDownvotingState(false);
if (this.contentContainer().sponsorTimes[i].UUID == this.UUID) { }
//this one is the one to hide
//add this as a hidden sponsorTime // Change the sponsor locally
this.contentContainer().sponsorTimes[i].hidden = SponsorHideType.Downvoted; if (segment) {
if (type === 0) {
this.contentContainer().updatePreviewBar(); segment.hidden = SponsorHideType.Downvoted;
break; } else if (category) {
segment.category = category;
} }
this.contentContainer().updatePreviewBar();
} }
} }
setNoticeInfoMessage(...messages: string[]) { setNoticeInfoMessage(...messages: string[]) {
this.setState({ this.setState({
messages messages
}) });
} }
addVoteButtonInfo(message) { addVoteButtonInfo(message) {
this.resetVoteButtonInfo(); this.setState({
thanksForVotingText: message
//hide report button and text for it });
let downvoteButton = document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix);
if (downvoteButton != null) {
downvoteButton.style.display = "none";
}
let downvoteButtonText = document.getElementById("sponsorTimesReportText" + this.idSuffix);
if (downvoteButtonText != null) {
downvoteButtonText.style.display = "none";
}
//add info
let thanksForVotingText = document.createElement("td");
thanksForVotingText.id = "sponsorTimesVoteButtonInfoMessage" + this.idSuffix;
thanksForVotingText.className = "sponsorTimesInfoMessage sponsorTimesVoteButtonMessage";
thanksForVotingText.innerText = message;
//add element to div
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).prepend(thanksForVotingText);
} }
resetVoteButtonInfo() { resetVoteButtonInfo() {
let previousInfoMessage = document.getElementById("sponsorTimesVoteButtonInfoMessage" + this.idSuffix); this.setState({
if (previousInfoMessage != null) { thanksForVotingText: null
//remove it });
document.getElementById("sponsorSkipNoticeSecondRow" + this.idSuffix).removeChild(previousInfoMessage); }
}
//show button again closeListener() {
document.getElementById("sponsorTimesDownvoteButtonsContainer" + this.idSuffix).style.removeProperty("display"); this.clearConfigListener();
this.props.closeListener();
} }
} }

View File

@@ -29,6 +29,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
categoryOptionRef: React.RefObject<HTMLSelectElement>; categoryOptionRef: React.RefObject<HTMLSelectElement>;
configUpdateListener: () => void;
constructor(props: SponsorTimeEditProps) { constructor(props: SponsorTimeEditProps) {
super(props); super(props);
@@ -49,7 +51,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}); });
// Add as a config listener // Add as a config listener
Config.configListeners.push(this.configUpdate.bind(this)); if (!this.configUpdateListener) {
this.configUpdateListener = () => this.configUpdate();
Config.configListeners.push(this.configUpdate.bind(this));
}
}
componentWillUnmount() {
if (this.configUpdateListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)), 1);
}
} }
render() { render() {
@@ -61,6 +72,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
style.marginTop = "15px"; style.marginTop = "15px";
} }
// This method is required to get !important
// https://stackoverflow.com/a/45669262/1985387
let oldYouTubeDarkStyles = (node) => {
if (node) {
node.style.setProperty("color", "black", "important");
node.style.setProperty("text-shadow", "none", "important");
}
};
// Create time display // Create time display
let timeDisplay: JSX.Element; let timeDisplay: JSX.Element;
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
@@ -78,6 +98,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeMinutes0" + this.idSuffix} <input id={"submittingTimeMinutes0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes" className="sponsorTimeEdit sponsorTimeEditMinutes"
ref={oldYouTubeDarkStyles}
type="number" type="number"
value={this.state.sponsorTimeEdits[0][0]} value={this.state.sponsorTimeEdits[0][0]}
onChange={(e) => { onChange={(e) => {
@@ -90,6 +111,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds0" + this.idSuffix} <input id={"submittingTimeSeconds0" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds" className="sponsorTimeEdit sponsorTimeEditSeconds"
ref={oldYouTubeDarkStyles}
type="number" type="number"
value={this.state.sponsorTimeEdits[0][1]} value={this.state.sponsorTimeEdits[0][1]}
onChange={(e) => { onChange={(e) => {
@@ -106,6 +128,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeMinutes1" + this.idSuffix} <input id={"submittingTimeMinutes1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditMinutes" className="sponsorTimeEdit sponsorTimeEditMinutes"
ref={oldYouTubeDarkStyles}
type="text" type="text"
value={this.state.sponsorTimeEdits[1][0]} value={this.state.sponsorTimeEdits[1][0]}
onChange={(e) => { onChange={(e) => {
@@ -118,6 +141,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<input id={"submittingTimeSeconds1" + this.idSuffix} <input id={"submittingTimeSeconds1" + this.idSuffix}
className="sponsorTimeEdit sponsorTimeEditSeconds" className="sponsorTimeEdit sponsorTimeEditSeconds"
ref={oldYouTubeDarkStyles}
type="text" type="text"
value={this.state.sponsorTimeEdits[1][1]} value={this.state.sponsorTimeEdits[1][1]}
onChange={(e) => { onChange={(e) => {
@@ -236,7 +260,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
setTimeToNow(index: number) { setTimeToNow(index: number) {
this.setTimeTo(index, this.props.contentContainer().v.currentTime); this.setTimeTo(index, this.props.contentContainer().getRealCurrentTime());
} }
setTimeToEnd() { setTimeToEnd() {

View File

@@ -93,13 +93,6 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
<td className="sponsorSkipNoticeRightSection" <td className="sponsorSkipNoticeRightSection"
style={{position: "relative"}}> style={{position: "relative"}}>
{/* Cancel Button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={this.cancel.bind(this)}>
{chrome.i18n.getMessage("cancel")}
</button>
{/* Submit Button */} {/* Submit Button */}
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton" <button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
onClick={this.submit.bind(this)}> onClick={this.submit.bind(this)}>
@@ -167,6 +160,18 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
ref.current.saveEditTimes(); ref.current.saveEditTimes();
} }
// Check if any non music categories are being used on a music video
if (this.contentContainer().videoInfo?.microformat?.playerMicroformatRenderer?.category === "Music") {
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
for (const sponsorTime of sponsorTimesSubmitting) {
if (!sponsorTime.category.startsWith("music_")) {
if (!confirm(chrome.i18n.getMessage("nonMusicCategoryOnMusic"))) return;
break;
}
}
}
this.props.callback(); this.props.callback();
this.cancel(); this.cancel();

View File

@@ -24,7 +24,6 @@ interface SBConfig {
hideDiscordLaunches: number, hideDiscordLaunches: number,
hideDiscordLink: boolean, hideDiscordLink: boolean,
invidiousInstances: string[], invidiousInstances: string[],
autoUpvote: boolean,
supportInvidious: boolean, supportInvidious: boolean,
serverAddress: string, serverAddress: string,
minDuration: number, minDuration: number,
@@ -123,8 +122,7 @@ var Config: SBObject = {
hideUploadButtonPlayerControls: false, hideUploadButtonPlayerControls: false,
hideDiscordLaunches: 0, hideDiscordLaunches: 0,
hideDiscordLink: false, hideDiscordLink: false,
invidiousInstances: ["invidio.us", "invidiou.sh", "invidious.snopyta.org"], invidiousInstances: ["invidio.us", "invidious.snopyta.org"],
autoUpvote: true,
supportInvidious: false, supportInvidious: false,
serverAddress: CompileConfig.serverAddress, serverAddress: CompileConfig.serverAddress,
minDuration: 0, minDuration: 0,
@@ -252,9 +250,14 @@ async function migrateOldFormats() {
} }
} }
// Auto vote removal
if (Config.config["autoUpvote"]) {
chrome.storage.sync.remove("autoUpvote");
}
// Channel URLS // Channel URLS
if (Config.config.whitelistedChannels.length > 0 && if (Config.config.whitelistedChannels.length > 0 &&
(Config.config.whitelistedChannels[0].includes("/") || Config.config.whitelistedChannels[0] == null)) { (Config.config.whitelistedChannels[0] == null || Config.config.whitelistedChannels[0].includes("/"))) {
let newChannelList: string[] = []; let newChannelList: string[] = [];
for (const item of Config.config.whitelistedChannels) { for (const item of Config.config.whitelistedChannels) {
if (item != null) { if (item != null) {
@@ -281,6 +284,16 @@ async function migrateOldFormats() {
Config.config.whitelistedChannels = newChannelList; Config.config.whitelistedChannels = newChannelList;
} }
// Check if off-topic category needs to be removed
for (let i = 0; i < Config.config.categorySelections.length; i++) {
if (Config.config.categorySelections[i].name === "offtopic") {
Config.config.categorySelections.splice(i, 1);
// Call set listener
Config.config.categorySelections = Config.config.categorySelections;
break;
}
}
} }
async function setupConfig() { async function setupConfig() {

View File

@@ -41,9 +41,6 @@ var sponsorSkipped: boolean[] = [];
//the video //the video
var video: HTMLVideoElement; var video: HTMLVideoElement;
/** The last time this video was seeking to */
var lastVideoTime: number = null;
var onInvidious; var onInvidious;
var onMobileYouTube; var onMobileYouTube;
@@ -81,12 +78,6 @@ utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYo
//this only happens if there is an error //this only happens if there is an error
var sponsorLookupRetries = 0; var sponsorLookupRetries = 0;
//the last time in the video a sponsor was skipped
//used for the go back button
var lastSponsorTimeSkipped: number = null;
//used for ratings
var lastSponsorTimeSkippedUUID: string = null;
//if showing the start sponsor button or the end sponsor button on the player //if showing the start sponsor button or the end sponsor button on the player
var showingStartSponsor = true; var showingStartSponsor = true;
@@ -99,6 +90,9 @@ var popupInitialised = false;
var submissionNotice: SubmissionNotice = null; var submissionNotice: SubmissionNotice = null;
// If there is an advert playing (or about to be played), this is true
var isAdPlaying = false;
// Contains all of the functions and variables needed by the skip notice // Contains all of the functions and variables needed by the skip notice
var skipNoticeContentContainer: ContentContainer = () => ({ var skipNoticeContentContainer: ContentContainer = () => ({
vote, vote,
@@ -114,7 +108,9 @@ var skipNoticeContentContainer: ContentContainer = () => ({
sponsorSubmissionNotice: submissionNotice, sponsorSubmissionNotice: submissionNotice,
resetSponsorSubmissionNotice, resetSponsorSubmissionNotice,
changeStartSponsorButton, changeStartSponsorButton,
previewTime previewTime,
videoInfo,
getRealCurrentTime: getRealCurrentTime
}); });
//get messages from the background script and the popup //get messages from the background script and the popup
@@ -178,7 +174,7 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
return return
case "getCurrentTime": case "getCurrentTime":
sendResponse({ sendResponse({
currentTime: video.currentTime currentTime: getRealCurrentTime()
}); });
break; break;
@@ -279,6 +275,9 @@ function resetValues() {
} else { } else {
switchingVideos = true; switchingVideos = true;
} }
// Reset advert playing flag
isAdPlaying = false;
} }
async function videoIDChange(id) { async function videoIDChange(id) {
@@ -444,6 +443,7 @@ function createPreviewBar(): void {
* This happens when the resolution changes or at random time to clear memory. * This happens when the resolution changes or at random time to clear memory.
*/ */
function durationChangeListener() { function durationChangeListener() {
updateAdFlag();
updatePreviewBar(); updatePreviewBar();
} }
@@ -461,13 +461,23 @@ function cancelSponsorSchedule(): void {
*/ */
function startSponsorSchedule(includeIntersectingSegments: boolean = false, currentTime?: number): void { function startSponsorSchedule(includeIntersectingSegments: boolean = false, currentTime?: number): void {
cancelSponsorSchedule(); cancelSponsorSchedule();
// Don't skip if advert playing and reset last checked time
if (isAdPlaying) {
// Reset lastCheckVideoTime
lastCheckVideoTime = -1;
lastCheckTime = 0;
return;
}
if (video.paused) return; if (video.paused) return;
if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){ if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){
return; return;
} }
if (incorrectVideoIDCheck()) return; if (incorrectVideoCheck()) return;
if (currentTime === undefined || currentTime === null) currentTime = video.currentTime; if (currentTime === undefined || currentTime === null) currentTime = video.currentTime;
@@ -478,6 +488,20 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let currentSkip = skipInfo.array[skipInfo.index]; let currentSkip = skipInfo.array[skipInfo.index];
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]]; let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
let timeUntilSponsor = skipTime[0] - currentTime; let timeUntilSponsor = skipTime[0] - currentTime;
let videoID = sponsorVideoID;
// Find all indexes in between the start and end
let skippingSegments = [skipInfo.array[skipInfo.index]];
if (skipInfo.index !== skipInfo.endIndex) {
skippingSegments = [];
for (const segment of skipInfo.array) {
if (utils.getCategorySelection(segment.category).option === CategorySkipOption.AutoSkip &&
segment.segment[0] >= skipTime[0] && segment.segment[1] <= skipTime[1]) {
skippingSegments.push(segment);
}
}
}
// Don't skip if this category should not be skipped // Don't skip if this category should not be skipped
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return; if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return;
@@ -486,10 +510,10 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let forcedSkipTime: number = null; let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false; let forcedIncludeIntersectingSegments = false;
if (incorrectVideoIDCheck()) return; if (incorrectVideoCheck(videoID, currentSkip)) return;
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) { if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
skipToTime(video, skipInfo.endIndex, skipInfo.array, skipInfo.openNotice); skipToTime(video, skipTime, skippingSegments, skipInfo.openNotice);
// TODO: Know the autoSkip settings for ALL items being skipped // TODO: Know the autoSkip settings for ALL items being skipped
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ManualSkip) { if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ManualSkip) {
@@ -511,13 +535,11 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
} }
/** /**
* This makes sure the videoID is still correct * This makes sure the videoID is still correct and if the sponsorTime is included
*
* TODO: Remove this bug catching if statement when the bug is found
*/ */
function incorrectVideoIDCheck(): boolean { function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean {
let currentVideoID = getYouTubeVideoID(document.URL); let currentVideoID = getYouTubeVideoID(document.URL);
if (currentVideoID !== sponsorVideoID) { if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime && (!sponsorTimes || !sponsorTimes.includes(sponsorTime)) && !sponsorTimesSubmitting.includes(sponsorTime))) {
// Something has really gone wrong // Something has really gone wrong
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be."); console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID); console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID);
@@ -552,6 +574,9 @@ function sponsorsLookup(id: string) {
video.addEventListener('play', () => { video.addEventListener('play', () => {
switchingVideos = false; switchingVideos = false;
// Check if an ad is playing
updateAdFlag();
// Make sure it doesn't get double called with the playing event // Make sure it doesn't get double called with the playing event
if (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000) { if (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000) {
lastCheckTime = Date.now(); lastCheckTime = Date.now();
@@ -559,6 +584,7 @@ function sponsorsLookup(id: string) {
startSponsorSchedule(); startSponsorSchedule();
} }
}); });
video.addEventListener('playing', () => { video.addEventListener('playing', () => {
// Make sure it doesn't get double called with the play event // Make sure it doesn't get double called with the play event
@@ -574,8 +600,6 @@ function sponsorsLookup(id: string) {
lastCheckVideoTime = -1 lastCheckVideoTime = -1
lastCheckTime = 0; lastCheckTime = 0;
lastVideoTime = video.currentTime;
if (!video.paused){ if (!video.paused){
startSponsorSchedule(); startSponsorSchedule();
} }
@@ -586,8 +610,6 @@ function sponsorsLookup(id: string) {
lastCheckVideoTime = -1; lastCheckVideoTime = -1;
lastCheckTime = 0; lastCheckTime = 0;
lastVideoTime = video.currentTime;
cancelSponsorSchedule(); cancelSponsorSchedule();
}); });
@@ -656,7 +678,7 @@ function sponsorsLookup(id: string) {
//check if this video was uploaded recently //check if this video was uploaded recently
utils.wait(() => !!videoInfo).then(() => { utils.wait(() => !!videoInfo).then(() => {
let dateUploaded = videoInfo.microformat.playerMicroformatRenderer.uploadDate; let dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
//if less than 3 days old //if less than 3 days old
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) { if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
@@ -779,6 +801,11 @@ function updatePreviewBarPositionMobile(parent: Element) {
} }
function updatePreviewBar() { function updatePreviewBar() {
if(isAdPlaying) {
previewBar.set([], [], 0);
return;
}
if (previewBar === null || video === null) return; if (previewBar === null || video === null) return;
let localSponsorTimes = sponsorTimes; let localSponsorTimes = sponsorTimes;
@@ -808,7 +835,12 @@ function updatePreviewBar() {
//checks if this channel is whitelisted, should be done only after the channelID has been loaded //checks if this channel is whitelisted, should be done only after the channelID has been loaded
function whitelistCheck() { function whitelistCheck() {
channelID = videoInfo.videoDetails.channelId; channelID = videoInfo?.videoDetails?.channelId;
if (!channelID) {
channelID = null;
return;
}
//see if this is a whitelisted channel //see if this is a whitelisted channel
let whitelistedChannels = Config.config.whitelistedChannels; let whitelistedChannels = Config.config.whitelistedChannels;
@@ -948,58 +980,58 @@ function previewTime(time: number) {
} }
//skip from the 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: HTMLVideoElement, index: number, sponsorTimes: SponsorTime[], openNotice: boolean) { function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
let autoSkip: boolean = utils.getCategorySelection(sponsorTimes[index].category).option === CategorySkipOption.AutoSkip; // There will only be one submission if it is manual skip
let autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category).option === CategorySkipOption.AutoSkip;
if (autoSkip || previewResetter !== null) { if (autoSkip || previewResetter !== null) {
v.currentTime = sponsorTimes[index].segment[1]; v.currentTime = skipTime[1];
} }
lastSponsorTimeSkipped = sponsorTimes[index].segment[0];
let currentUUID: string = sponsorTimes[index].UUID;
lastSponsorTimeSkippedUUID = currentUUID;
if (openNotice) { if (openNotice) {
//send out the message saying that a sponsor message was skipped //send out the message saying that a sponsor message was skipped
if (!Config.config.dontShowNotice || !autoSkip) { if (!Config.config.dontShowNotice || !autoSkip) {
let skipNotice = new SkipNotice(currentUUID, autoSkip, skipNoticeContentContainer); let skipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer);
//auto-upvote this sponsor
if (Config.config.trackViewCount && autoSkip && Config.config.autoUpvote) {
vote(1, currentUUID, null);
}
} }
//send telemetry that a this sponsor was skipped //send telemetry that a this sponsor was skipped
if (Config.config.trackViewCount && !sponsorSkipped[index] && autoSkip) { if (Config.config.trackViewCount && autoSkip) {
utils.sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + currentUUID); let alreadySkipped = false;
let isPreviewSegment = false;
for (const segment of skippingSegments) {
let index = sponsorTimes.indexOf(segment);
if (index !== -1 && !sponsorSkipped[index]) {
utils.sendRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
sponsorSkipped[index] = true;
} else if (sponsorSkipped[index]) {
alreadySkipped = true;
}
if (index !== -1) isPreviewSegment = true;
}
// Count this as a skip // Count this as a skip
Config.config.minutesSaved = Config.config.minutesSaved + (sponsorTimes[index].segment[1] - sponsorTimes[index].segment[0]) / 60; if (!alreadySkipped && !isPreviewSegment) {
Config.config.skipCount = Config.config.skipCount + 1; Config.config.minutesSaved = Config.config.minutesSaved + (skipTime[1] - skipTime[0]) / 60;
Config.config.skipCount = Config.config.skipCount + 1;
sponsorSkipped[index] = true; }
} }
} }
} }
function unskipSponsorTime(UUID) { function unskipSponsorTime(segment: SponsorTime) {
if (sponsorTimes != null) { if (sponsorTimes != null) {
//add a tiny bit of time to make sure it is not skipped again //add a tiny bit of time to make sure it is not skipped again
video.currentTime = utils.getSponsorTimeFromUUID(sponsorTimes, UUID).segment[0] + 0.001; video.currentTime = segment.segment[0] + 0.001;
checkIfInsideSegment(); checkIfInsideSegment();
} }
} }
function reskipSponsorTime(UUID) { function reskipSponsorTime(segment: SponsorTime) {
if (sponsorTimes != null) { video.currentTime = segment.segment[1];
video.currentTime = utils.getSponsorTimeFromUUID(sponsorTimes, UUID).segment[1];
// See if any skips need to be done if this is inside of another segment
startSponsorSchedule(true, utils.getSponsorTimeFromUUID(sponsorTimes, UUID).segment[1]);
}
} }
/** /**
@@ -1117,6 +1149,24 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
return createdButtons; return createdButtons;
} }
/**
* Used for submitting. This will use the HTML displayed number when required as the video's
* current time is out of date while scrubbing or at the end of the video. This is not needed
* for sponsor skipping as the video is not playing during these times.
*/
function getRealCurrentTime(): number {
// Used to check if replay button
let playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d");
let replaceSVGData = "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z";
if (playButtonSVGData === replaceSVGData) {
// At the end of the video
return video.duration;
} else {
return video.currentTime;
}
}
function startSponsorClicked() { function startSponsorClicked() {
//it can't update to this info yet //it can't update to this info yet
closeInfoMenu(); closeInfoMenu();
@@ -1126,11 +1176,11 @@ function startSponsorClicked() {
//add to sponsorTimes //add to sponsorTimes
if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) { if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) {
//it is an end time //it is an end time
sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = video.currentTime; sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = getRealCurrentTime();
} else { } else {
//it is a start time //it is a start time
sponsorTimesSubmitting.push({ sponsorTimesSubmitting.push({
segment: [video.currentTime], segment: [getRealCurrentTime()],
UUID: null, UUID: null,
// Default to sponsor // Default to sponsor
category: "sponsor" category: "sponsor"
@@ -1306,7 +1356,7 @@ function clearSponsorTimes() {
} }
//if skipNotice is null, it will not affect the UI //if skipNotice is null, it will not affect the UI
function vote(type, UUID, skipNotice?: SkipNoticeComponent) { function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNoticeComponent) {
if (skipNotice !== null && skipNotice !== undefined) { if (skipNotice !== null && skipNotice !== undefined) {
//add loading info //add loading info
skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...") skipNotice.addVoteButtonInfo.bind(skipNotice)("Loading...")
@@ -1319,7 +1369,7 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return; if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return;
// See if the local time saved count and skip count should be saved // See if the local time saved count and skip count should be saved
if (type == 0 && sponsorSkipped[sponsorIndex] || type == 1 && !sponsorSkipped[sponsorIndex]) { if (type === 0 && sponsorSkipped[sponsorIndex] || type === 1 && !sponsorSkipped[sponsorIndex]) {
let factor = 1; let factor = 1;
if (type == 0) { if (type == 0) {
factor = -1; factor = -1;
@@ -1336,16 +1386,15 @@ function vote(type, UUID, skipNotice?: SkipNoticeComponent) {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
message: "submitVote", message: "submitVote",
type: type, type: type,
UUID: UUID UUID: UUID,
category: category
}, function(response) { }, function(response) {
if (response != undefined) { if (response != undefined) {
//see if it was a success or failure //see if it was a success or failure
if (skipNotice != null) { if (skipNotice != null) {
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) { if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
//success (treat rate limits as a success) //success (treat rate limits as a success)
if (type == 0) { skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
skipNotice.afterDownvote.bind(skipNotice)();
}
} else if (response.successType == 0) { } else if (response.successType == 0) {
//failure: duplicate vote //failure: duplicate vote
skipNotice.setNoticeInfoMessage.bind(skipNotice)(chrome.i18n.getMessage("voteFail")) skipNotice.setNoticeInfoMessage.bind(skipNotice)(chrome.i18n.getMessage("voteFail"))
@@ -1507,7 +1556,7 @@ function getSegmentsMessage(segments: number[][]): string {
* Assumes that the the privacy info is available. * Assumes that the the privacy info is available.
*/ */
function isUnlisted(): boolean { function isUnlisted(): boolean {
return videoInfo.microformat.playerMicroformatRenderer.isUnlisted || videoInfo.videoDetails.isPrivate; return videoInfo?.microformat?.playerMicroformatRenderer?.isUnlisted || videoInfo?.videoDetails?.isPrivate;
} }
/** /**
@@ -1549,3 +1598,16 @@ function sendRequestToCustomServer(type, fullAddress, callback) {
//submit this request //submit this request
xmlhttp.send(); xmlhttp.send();
} }
/**
* Update the isAdPlaying flag and hide preview bar/controls if ad is playing
*/
function updateAdFlag() {
let wasAdPlaying = isAdPlaying;
isAdPlaying = document.getElementsByClassName('ad-showing').length > 0;
if(wasAdPlaying != isAdPlaying) {
updatePreviewBar();
updateVisibilityOfPlayerControlsButton();
}
}

View File

@@ -50,11 +50,11 @@ let barTypes = {
color: "#bfbf35", color: "#bfbf35",
opacity: "0.7" opacity: "0.7"
}, },
"offtopic": { "music_offtopic": {
color: "#ff9900", color: "#ff9900",
opacity: "0.7" opacity: "0.7"
}, },
"preview-offtopic": { "preview-music_offtopic": {
color: "#a6634a", color: "#a6634a",
opacity: "0.7" opacity: "0.7"
} }
@@ -65,6 +65,9 @@ class PreviewBar {
parent: any; parent: any;
onMobileYouTube: boolean; onMobileYouTube: boolean;
timestamps: number[][];
types: string;
constructor(parent, onMobileYouTube) { constructor(parent, onMobileYouTube) {
this.container = document.createElement('ul'); this.container = document.createElement('ul');
this.container.id = 'previewbar'; this.container.id = 'previewbar';
@@ -73,6 +76,87 @@ class PreviewBar {
this.onMobileYouTube = onMobileYouTube; this.onMobileYouTube = onMobileYouTube;
this.updatePosition(parent); this.updatePosition(parent);
this.setupHoverText();
}
setupHoverText() {
let seekBar = document.querySelector(".ytp-progress-bar-container");
// Create label placeholder
let tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
let titleTooltip = document.querySelector(".ytp-tooltip-title");
let categoryTooltip = document.createElement("div");
categoryTooltip.className = "sbHidden ytp-tooltip-title";
categoryTooltip.id = "sponsor-block-category-tooltip"
tooltipTextWrapper.insertBefore(categoryTooltip, titleTooltip.nextSibling);
let mouseOnSeekBar = false;
seekBar.addEventListener("mouseenter", (event) => {
mouseOnSeekBar = true;
});
seekBar.addEventListener("mouseleave", (event) => {
mouseOnSeekBar = false;
categoryTooltip.classList.add("sbHidden");
});
const observer = new MutationObserver((mutations, observer) => {
if (!mouseOnSeekBar) return;
// See if mutation observed is only this ID (if so, ignore)
if (mutations.length == 1 && (mutations[0].target as HTMLElement).id === "sponsor-block-category-tooltip") {
return;
}
let tooltips = document.querySelectorAll(".ytp-tooltip-text");
for (const tooltip of tooltips) {
let splitData = tooltip.textContent.split(":");
if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) {
// Add label
let timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
// Find category at that location
let category = null;
for (let i = 0; i < this.timestamps?.length; i++) {
if (this.timestamps[i][0] < timeInSeconds && this.timestamps[i][1] > timeInSeconds){
category = this.types[i];
}
}
if (category === null && !categoryTooltip.classList.contains("sbHidden")) {
categoryTooltip.classList.add("sbHidden");
tooltipTextWrapper.classList.remove("sbTooltipTwoTitleThumbnailOffset");
tooltipTextWrapper.classList.remove("sbTooltipOneTitleThumbnailOffset");
} else if (category !== null) {
categoryTooltip.classList.remove("sbHidden");
categoryTooltip.textContent = chrome.i18n.getMessage("category_" + category)
|| (chrome.i18n.getMessage("preview") + " " + chrome.i18n.getMessage("category_" + category.split("preview-")[1]));
// There is a title now
tooltip.classList.remove("ytp-tooltip-text-no-title");
// Add the correct offset for the number of titles there are
if (titleTooltip.textContent !== "") {
if (!tooltipTextWrapper.classList.contains("sbTooltipTwoTitleThumbnailOffset")) {
tooltipTextWrapper.classList.add("sbTooltipTwoTitleThumbnailOffset");
}
} else if (!tooltipTextWrapper.classList.contains("sbTooltipOneTitleThumbnailOffset")) {
tooltipTextWrapper.classList.add("sbTooltipOneTitleThumbnailOffset");
}
}
break;
}
}
});
observer.observe(tooltipTextWrapper, {
childList: true,
subtree: true
});
} }
updatePosition(parent) { updatePosition(parent) {
@@ -109,6 +193,9 @@ class PreviewBar {
return; return;
} }
this.timestamps = timestamps;
this.types = types;
// to avoid rounding error resulting in width more than 100% // to avoid rounding error resulting in width more than 100%
duration = Math.floor(duration * 100) / 100; duration = Math.floor(duration * 100) / 100;
let width; let width;

View File

@@ -2,17 +2,18 @@ import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import SkipNoticeComponent from "../components/SkipNoticeComponent"; import SkipNoticeComponent from "../components/SkipNoticeComponent";
import { SponsorTime } from "../types";
class SkipNotice { class SkipNotice {
UUID: string; segments: SponsorTime[];
autoSkip: boolean; autoSkip: boolean;
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: () => any; contentContainer: () => any;
noticeElement: HTMLDivElement; noticeElement: HTMLDivElement;
constructor(UUID: string, autoSkip: boolean = false, contentContainer) { constructor(segments: SponsorTime[], autoSkip: boolean = false, contentContainer) {
this.UUID = UUID; this.segments = segments;
this.autoSkip = autoSkip; this.autoSkip = autoSkip;
this.contentContainer = contentContainer; this.contentContainer = contentContainer;
@@ -35,7 +36,11 @@ class SkipNotice {
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length; let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
//this is the suffix added at the end of every id //this is the suffix added at the end of every id
let idSuffix = this.UUID + amountOfPreviousNotices; let idSuffix = "";
for (const segment of this.segments) {
idSuffix += segment.UUID;
}
idSuffix += amountOfPreviousNotices;
this.noticeElement = document.createElement("div"); this.noticeElement = document.createElement("div");
this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix; this.noticeElement.id = "sponsorSkipNoticeContainer" + idSuffix;
@@ -43,7 +48,7 @@ class SkipNotice {
referenceNode.prepend(this.noticeElement); referenceNode.prepend(this.noticeElement);
ReactDOM.render( ReactDOM.render(
<SkipNoticeComponent UUID={UUID} <SkipNoticeComponent segments={segments}
autoSkip={autoSkip} autoSkip={autoSkip}
contentContainer={contentContainer} contentContainer={contentContainer}
closeListener={() => this.close()} />, closeListener={() => this.close()} />,

View File

@@ -3,20 +3,22 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
interface ContentContainer { interface ContentContainer {
(): { (): {
vote: (type: any, UUID: any, skipNotice?: SkipNoticeComponent) => void, vote: (type: any, UUID: any, category?: string, skipNotice?: SkipNoticeComponent) => void,
dontShowNoticeAgain: () => void, dontShowNoticeAgain: () => void,
unskipSponsorTime: (UUID: any) => void, unskipSponsorTime: (segment: SponsorTime) => void,
sponsorTimes: SponsorTime[], sponsorTimes: SponsorTime[],
sponsorTimesSubmitting: SponsorTime[], sponsorTimesSubmitting: SponsorTime[],
v: HTMLVideoElement, v: HTMLVideoElement,
sponsorVideoID, sponsorVideoID,
reskipSponsorTime: (UUID: any) => void, reskipSponsorTime: (segment: SponsorTime) => void,
updatePreviewBar: () => void, updatePreviewBar: () => void,
onMobileYouTube: boolean, onMobileYouTube: boolean,
sponsorSubmissionNotice: SubmissionNotice, sponsorSubmissionNotice: SubmissionNotice,
resetSponsorSubmissionNotice: () => void, resetSponsorSubmissionNotice: () => void,
changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>, changeStartSponsorButton: (showStartSponsor: any, uploadButtonVisible: any) => Promise<boolean>,
previewTime: (time: number) => void previewTime: (time: number) => void,
videoInfo: any,
getRealCurrentTime: () => number
} }
} }