Compare commits

...

40 Commits

Author SHA1 Message Date
Ajay
b2e6e4f076 Fix tests 2023-02-01 19:55:01 -05:00
Ajay
6348bdccde bump version 2023-02-01 19:51:50 -05:00
Ajay
c3213eb040 Add invidious instances by default for safari
https://github.com/ajayyy/SponsorBlock/issues/798
2023-02-01 19:48:24 -05:00
Ajay
78a006544a Fix icon not square 2023-01-31 01:07:00 -05:00
Ajay
3ab2361f45 bump version 2023-01-31 00:58:40 -05:00
Ajay
6068276644 Fix icon on firefox being wrong 2023-01-31 00:58:31 -05:00
Ajay
e3bedbf5f2 Fix chapter importing and lock fetching after 404 2023-01-29 17:53:37 -05:00
Ajay Ramachandran
c9fc22e51e Merge pull request #1654 from mchangrh/fix-large-img
load shared.css on third party sites
2023-01-28 16:42:52 -05:00
Michael C
146c1fc889 load shared.css on third party sites 2023-01-26 03:07:43 -05:00
Ajay
63a5b6fb6f Store fonts locally 2023-01-23 01:05:33 -05:00
Ajay
254261b83a Don't hide chevron if segments are null 2023-01-21 17:37:12 -05:00
Ajay
9301a0914d Add info that piped is supported 2023-01-21 17:26:43 -05:00
Ajay Ramachandran
8e3bae6396 bump version 2023-01-21 17:24:55 -05:00
Ajay Ramachandran
1a4d48503a Merge pull request #1560 from mchangrh/piped-support
add selector for piped
2023-01-21 17:23:45 -05:00
Ajay Ramachandran
508d51d582 New Crowdin updates (#1619) 2023-01-21 17:23:10 -05:00
Ajay
8f91b83195 Remove next chapter from youtube hotkeys warning 2023-01-21 17:17:05 -05:00
Ajay
6adac26a3d Fix keybinds from popup 2023-01-21 17:13:48 -05:00
Ajay
140e816979 Change next chapter keybind to be same as YouTube and disable YouTube's one
Fix #1508
2023-01-21 17:11:08 -05:00
Ajay
f89682c2d6 give more time for chapters to load 2023-01-21 16:59:36 -05:00
Ajay
35e0f7ede9 Fix warnings 2023-01-21 16:20:18 -05:00
Ajay
8383699a68 Fix chapter name sometimes disappearing 2023-01-21 16:18:25 -05:00
dependabot[bot]
76aa472745 Bump json5 from 2.2.1 to 2.2.3 (#1640)
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-08 15:41:36 -05:00
Michael C
6959bdbee1 Add piped support
- add playerInit listener
- made popup container generic for possible piped support
- explicitly add css files to web_accessible_resources
2023-01-03 17:10:51 -05:00
Michael C
c3d4914f57 add selector for piped 2023-01-02 18:28:37 -05:00
github-actions[bot]
55b6b74590 Update Invidious List (#1631)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-01 19:44:34 -05:00
Ajay
4a4b281495 Remove log 2022-12-30 23:13:39 -05:00
Ajay Ramachandran
b9b43513b4 Merge pull request #1627 from mchangrh/partialRequredSegment
highlight partial required segments
2022-12-27 12:42:06 -05:00
Michael C
f68429487e add brackets for operator precedence 2022-12-27 00:24:46 -05:00
Michael C
c5178b4c99 highlight partial required segments 2022-12-26 20:18:51 -05:00
Ajay
27f3ced338 Remove lookbehind because safari
Resolves https://github.com/ajayyy/SponsorBlock/issues/1626
2022-12-26 16:28:17 -05:00
Ajay
06a112a030 Fix category pill disappearing on mobile 2022-12-25 00:38:49 -05:00
github-actions[bot]
89b210ffd5 Update OSS Attribution (#1624)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-24 00:56:55 -05:00
Ajay
7dd2c9eb3e Move some generic functions to a new package 2022-12-24 00:54:56 -05:00
Ajay
7bf17e1746 bump version 2022-12-22 02:20:32 -05:00
Ajay
3f7f671a3b Fix early skips on firefox when seeking while playing 2022-12-22 02:20:19 -05:00
Ajay
f764869cc8 Add another test case 2022-12-19 16:19:01 -05:00
Ajay
2ba5fa6954 bump version 2022-12-19 16:14:41 -05:00
Ajay
35b8a34162 Don't remove trailing end bracket when not starting with bracket 2022-12-19 16:12:51 -05:00
Ajay Ramachandran
3379189ea8 remove serious part of warning message 2022-12-19 13:13:38 -05:00
Ajay Ramachandran
e6be13b583 make warning message nicer 2022-12-19 13:11:03 -05:00
63 changed files with 876 additions and 363 deletions

View File

@@ -1 +1 @@
["inv.bp.projectsegfau.lt","inv.odyssey346.dev","inv.riverside.rocks","inv.vern.cc","invidio.xamh.de","invidious.baczek.me","invidious.esmailelbob.xyz","invidious.flokinet.to","invidious.namazso.eu","invidious.nerdvpn.de","invidious.rhyshl.live","invidious.sethforprivacy.com","invidious.slipfox.xyz","invidious.snopyta.org","invidious.tiekoetter.com","invidious.weblibre.org","vid.puffyan.us","y.com.sb","yewtu.be","yt.artemislena.eu"] ["inv.bp.projectsegfau.lt","inv.odyssey346.dev","inv.riverside.rocks","inv.vern.cc","invidio.xamh.de","invidious.baczek.me","invidious.dhusch.de","invidious.drivet.xyz","invidious.esmailelbob.xyz","invidious.flokinet.to","invidious.namazso.eu","invidious.nerdvpn.de","invidious.privacydev.net","invidious.silur.me","invidious.slipfox.xyz","invidious.snopyta.org","invidious.tiekoetter.com","invidious.weblibre.org","vid.puffyan.us","y.com.sb","yewtu.be","yt.artemislena.eu","yt.funami.tech"]

View File

@@ -1,7 +1,7 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "SponsorBlock", "short_name": "SponsorBlock",
"version": "5.1.9", "version": "5.1.14",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_Description__", "description": "__MSG_Description__",
"homepage_url": "https://sponsor.ajay.app", "homepage_url": "https://sponsor.ajay.app",
@@ -69,8 +69,15 @@
"icons/PlayerInfoIconSponsorBlocker.svg", "icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg", "icons/PlayerDeleteIconSponsorBlocker.svg",
"popup.html", "popup.html",
"popup.css",
"content.css", "content.css",
"js/document.js" "shared.css",
"js/document.js",
"libs/Source+Sans+Pro.css",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2",
"libs/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2"
], ],
"permissions": [ "permissions": [
"storage", "storage",
@@ -85,8 +92,8 @@
"default_icon": { "default_icon": {
"16": "icons/IconSponsorBlocker16px.png", "16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png", "32": "icons/IconSponsorBlocker32px.png",
"64": "icons/LogoSponsorBlocker64px.png", "64": "icons/IconSponsorBlocker64px.png",
"128": "icons/LogoSponsorBlocker128px.png" "128": "icons/IconSponsorBlocker128px.png"
}, },
"theme_icons": [ "theme_icons": [
{ {
@@ -100,13 +107,13 @@
"size": 32 "size": 32
}, },
{ {
"light": "icons/LogoSponsorBlocker64px.png", "light": "icons/IconSponsorBlocker64px.png",
"dark": "icons/LogoSponsorBlocker64px.png", "dark": "icons/IconSponsorBlocker64px.png",
"size": 64 "size": 64
}, },
{ {
"light": "icons/LogoSponsorBlocker128px.png", "light": "icons/IconSponsorBlocker128px.png",
"dark": "icons/LogoSponsorBlocker128px.png", "dark": "icons/IconSponsorBlocker128px.png",
"size": 128 "size": 128
} }
] ]
@@ -120,11 +127,11 @@
"icons": { "icons": {
"16": "icons/IconSponsorBlocker16px.png", "16": "icons/IconSponsorBlocker16px.png",
"32": "icons/IconSponsorBlocker32px.png", "32": "icons/IconSponsorBlocker32px.png",
"64": "icons/LogoSponsorBlocker64px.png", "64": "icons/IconSponsorBlocker64px.png",
"128": "icons/LogoSponsorBlocker128px.png", "128": "icons/IconSponsorBlocker128px.png",
"256": "icons/LogoSponsorBlocker256px.png", "256": "icons/IconSponsorBlocker256px.png",
"512": "icons/LogoSponsorBlocker512px.png", "512": "icons/IconSponsorBlocker512px.png",
"1024": "icons/LogoSponsorBlocker1024px.png" "1024": "icons/IconSponsorBlocker1024px.png"
}, },
"options_ui": { "options_ui": {
"page": "options/options.html", "page": "options/options.html",

File diff suppressed because one or more lines are too long

44
package-lock.json generated
View File

@@ -27,6 +27,7 @@
], ],
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@ajayyy/maze-utils": "^1.0.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
@@ -65,6 +66,32 @@
"node": ">=16" "node": ">=16"
} }
}, },
"node_modules/@ajayyy/maze-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@ajayyy/maze-utils/-/maze-utils-1.0.3.tgz",
"integrity": "sha512-sdQyU/2VAmJ9FiyUIdjE8FbO5b5IofN9vK/7lkZiUw91V+NZi7aSG/LSYMqmQ3OuTYRE5PLN9Jyknuo2ZnljjA==",
"funding": [
{
"type": "individual",
"url": "https://sponsor.ajay.app/donate"
},
{
"type": "github",
"url": "https://github.com/sponsors/ajayyy-org"
},
{
"type": "patreon",
"url": "https://www.patreon.com/ajayyy"
},
{
"type": "individual",
"url": "https://paypal.me/ajayyy"
}
],
"engines": {
"node": ">=16"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -9266,9 +9293,9 @@
"dev": true "dev": true
}, },
"node_modules/json5": { "node_modules/json5": {
"version": "2.2.1", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true, "dev": true,
"bin": { "bin": {
"json5": "lib/cli.js" "json5": "lib/cli.js"
@@ -13336,6 +13363,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@ajayyy/maze-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@ajayyy/maze-utils/-/maze-utils-1.0.3.tgz",
"integrity": "sha512-sdQyU/2VAmJ9FiyUIdjE8FbO5b5IofN9vK/7lkZiUw91V+NZi7aSG/LSYMqmQ3OuTYRE5PLN9Jyknuo2ZnljjA=="
},
"@ampproject/remapping": { "@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -20151,9 +20183,9 @@
"dev": true "dev": true
}, },
"json5": { "json5": {
"version": "2.2.1", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true "dev": true
}, },
"jsonfile": { "jsonfile": {

View File

@@ -5,7 +5,8 @@
"main": "background.js", "main": "background.js",
"dependencies": { "dependencies": {
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0",
"@ajayyy/maze-utils": "^1.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/chrome": "^0.0.199", "@types/chrome": "^0.0.199",

View File

@@ -942,7 +942,7 @@
"message": "Скриване завинаги" "message": "Скриване завинаги"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Забелязахме, че правите някои често срещани грешки, които не са злонамерени" "message": "Забелязахме, че допускате някои често срещани грешки. Оценяваме високо досегашната ви работа, но тук се стремим към съвършенство, така че дори и много малките грешки са от значение :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Имате предупреждение" "message": "Имате предупреждение"

View File

@@ -4,7 +4,7 @@
"description": "Name of the extension." "description": "Name of the extension."
}, },
"Description": { "Description": {
"message": "Přeskoč sponzorství, žadonění o odběr a další v YouTube videích. Nahlaš sponzorované segmenty na videích co sleduješ a ušetři ostatním čas.", "message": "Přeskočte sponzorství, žádosti o odběr a další ve videích na YouTube. Nahlaste sponzorované segmenty ve videích, která sledujete a ušetřete ostatním čas.",
"description": "Description of the extension." "description": "Description of the extension."
}, },
"400": { "400": {
@@ -99,7 +99,7 @@
"message": "Zrušit vytváření segmentu" "message": "Zrušit vytváření segmentu"
}, },
"noVideoID": { "noVideoID": {
"message": "Nebylo nalezeno žádné YouTube video.\nPokud je to špatně, obnovte záložku." "message": "Nebylo nalezeno žádné YouTube video.\nPokud je to špatně, obnovte kartu."
}, },
"refreshSegments": { "refreshSegments": {
"message": "Aktualizovat segmenty" "message": "Aktualizovat segmenty"
@@ -111,7 +111,7 @@
"message": "Hlasováno!" "message": "Hlasováno!"
}, },
"serverDown": { "serverDown": {
"message": "Zdá se, že server nefunguje. Obraťte se okamžitě na vývojáře." "message": "Zdá se, že server je offline. Okamžitě kontaktujte vývojáře."
}, },
"connectionError": { "connectionError": {
"message": "Došlo k chybě připojení. Kód chyby: " "message": "Došlo k chybě připojení. Kód chyby: "
@@ -250,7 +250,7 @@
"message": "Místo žádání o segmenty ze serveru pomocí videoID jsou poslány první 4 charaktery hashe ve videoID. Tento server odešle zpět data pro všechna videa s podobnými hashy." "message": "Místo žádání o segmenty ze serveru pomocí videoID jsou poslány první 4 charaktery hashe ve videoID. Tento server odešle zpět data pro všechna videa s podobnými hashy."
}, },
"enableRefetchWhenNotFound": { "enableRefetchWhenNotFound": {
"message": "Refetovat segmenty na nových videích" "message": "Automaticky znovu načítat segmenty na nových videích"
}, },
"whatRefetchWhenNotFound": { "whatRefetchWhenNotFound": {
"message": "Pokud je video nové a nejsou nalezeny žádné segmenty, bude rozšíření každých několik minut refetchovat data, zatímco sledujete video." "message": "Pokud je video nové a nejsou nalezeny žádné segmenty, bude rozšíření každých několik minut refetchovat data, zatímco sledujete video."
@@ -942,7 +942,7 @@
"message": "Skrýt napořád" "message": "Skrýt napořád"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Všimli jsme si, že děláte některé běžné chyby, které nejsou úmyslně zlé" "message": "Všimli jsme si, že děláte některé běžné chyby. Velmi si vážíme vaší dosavadní práce, ale snažíme se zde o dokonalost, takže i na velmi malých chybách záleží :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Dostali jste varování" "message": "Dostali jste varování"

View File

@@ -941,9 +941,6 @@
"hideForever": { "hideForever": {
"message": "Skjul for evigt" "message": "Skjul for evigt"
}, },
"warningChatInfo": {
"message": "Vi bemærkede, at du lavede nogle almindelige fejl, som ikke er ondsindede"
},
"warningTitle": { "warningTitle": {
"message": "Du fik en advarsel" "message": "Du fik en advarsel"
}, },

View File

@@ -942,7 +942,7 @@
"message": "Dauerhaft verbergen" "message": "Dauerhaft verbergen"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Wir haben bemerkt, dass du einige häufige Fehler machst welche nicht bösartig sind" "message": "Wir haben festgestellt, dass du einige häufig gemacht Fehler machst. Wir schätzen deine bisherige Arbeit sehr, streben aber hier aber auch nach Perfektion, sodass es auch wichtig ist kleine Fehler zu korrigieren :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Du hast eine Verwarnung erhalten" "message": "Du hast eine Verwarnung erhalten"

View File

@@ -855,9 +855,6 @@
"hideForever": { "hideForever": {
"message": "Απόκρυψη για πάντα" "message": "Απόκρυψη για πάντα"
}, },
"warningChatInfo": {
"message": "Παρατηρήσαμε ότι κάνατε συνηθισμένα μη κακόβουλα λάθη"
},
"warningTitle": { "warningTitle": {
"message": "Λάβατε προειδοποίηση" "message": "Λάβατε προειδοποίηση"
}, },

View File

@@ -942,7 +942,7 @@
"message": "Hide forever" "message": "Hide forever"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "We noticed you were making some common mistakes that are not malicious" "message": "We noticed you were making some common mistakes. We very much appreciate your work so far, but we strive towards perfection here, so even very small mistakes matter :)"
}, },
"warningTitle": { "warningTitle": {
"message": "You got a warning" "message": "You got a warning"

View File

@@ -942,7 +942,7 @@
"message": "Ocultar para siempre" "message": "Ocultar para siempre"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Hemos notado que has estado cometiendo algunos errores comunes que no son maliciosos" "message": "Nos hemos dado cuenta de que cometías algunos errores comunes. Apreciamos mucho tu trabajo hasta ahora, pero aquí nos esforzamos por alcanzar la perfección, así que incluso los errores más pequeños importan :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Tienes una advertencia" "message": "Tienes una advertencia"
@@ -1197,7 +1197,7 @@
"description": "Example: You currently have 12 unsubmitted segments on 5 videos" "description": "Example: You currently have 12 unsubmitted segments on 5 videos"
}, },
"unsubmittedSegmentCountsZero": { "unsubmittedSegmentCountsZero": {
"message": "Actualmente no tiene segmentos sin enviar", "message": "Actualmente, no tiene segmentos sin enviar",
"description": "Replaces 'unsubmittedSegmentCounts' string when there are no unsubmitted segments" "description": "Replaces 'unsubmittedSegmentCounts' string when there are no unsubmitted segments"
}, },
"unsubmittedSegmentsSingular": { "unsubmittedSegmentsSingular": {

View File

@@ -36,7 +36,7 @@
"description": "Refers to drawing segments on the YouTube seek bar as split up chapters, similar to the existing chapter system" "description": "Refers to drawing segments on the YouTube seek bar as split up chapters, similar to the existing chapter system"
}, },
"showSegmentNameInChapterBar": { "showSegmentNameInChapterBar": {
"message": "Kuva praegune segment video kõrval" "message": "Kuva Praegune Segment Video Kestvuse Kõrval"
}, },
"upvoteButtonInfo": { "upvoteButtonInfo": {
"message": "Anna segmendile poolthääl" "message": "Anna segmendile poolthääl"
@@ -117,7 +117,7 @@
"message": "Ühendusega esines tõrge. Veakood: " "message": "Ühendusega esines tõrge. Veakood: "
}, },
"segmentsStillLoading": { "segmentsStillLoading": {
"message": "Segmendid on veel laadimas..." "message": "Segmente veel laetakse..."
}, },
"clearTimes": { "clearTimes": {
"message": "Tühjenda segmendid" "message": "Tühjenda segmendid"
@@ -255,6 +255,12 @@
"whatRefetchWhenNotFound": { "whatRefetchWhenNotFound": {
"message": "Kui video on uus ning segmente ei leitud, proovitakse vaatamise ajal iga paari minuti tagant uuesti segmente tuua." "message": "Kui video on uus ning segmente ei leitud, proovitakse vaatamise ajal iga paari minuti tagant uuesti segmente tuua."
}, },
"enableShowCategoryWithoutPermission": {
"message": "Näita kategooriaid segmentide menüüs isegi ilma segmendi esitamise loata"
},
"whatShowCategoryWithoutPermission": {
"message": "Osade kategooriate esitamiseks on minimaalse reputatsiooni nõuete tõttu vajalik luba"
},
"showNotice": { "showNotice": {
"message": "Kuva märkus uuesti" "message": "Kuva märkus uuesti"
}, },
@@ -319,6 +325,9 @@
"message": "Eelmine peatükk", "message": "Eelmine peatükk",
"description": "Keybind label" "description": "Keybind label"
}, },
"keybindDescription": {
"message": "Valige klahv vajutades sellele, seejärel valige need teisendklahvid, mida soovite kasutada."
},
"0": { "0": {
"message": "Ühenduse ajalõpp. Kontrolli oma võrguühendust. Kui internet töötab, on ilmselt server ülekoormatud või maas." "message": "Ühenduse ajalõpp. Kontrolli oma võrguühendust. Kui internet töötab, on ilmselt server ülekoormatud või maas."
}, },
@@ -414,9 +423,18 @@
"statusReminder": { "statusReminder": {
"message": "Serveri oleku saamiseks vaata status.sponsor.ajay.app" "message": "Serveri oleku saamiseks vaata status.sponsor.ajay.app"
}, },
"changeUserID": {
"message": "Impordi/ekspordi oma isiklik UserID"
},
"whatChangeUserID": { "whatChangeUserID": {
"message": "Seda tuleks privaatsena hoida. See on nagu parool ning seda ei tohiks kellegagi jagada. Kui kellelgi see on, saavad nad sinuna esineda.\nKui otsid avalikku UserID'd, klõpsa hüpikus olevat lõikelaua ikooni." "message": "Seda tuleks privaatsena hoida. See on nagu parool ning seda ei tohiks kellegagi jagada. Kui kellelgi see on, saavad nad sinuna esineda.\nKui otsid avalikku UserID'd, klõpsa hüpikus olevat lõikelaua ikooni."
}, },
"setUserID": {
"message": "Muutke oma isiklik UserID"
},
"userIDChangeWarning": {
"message": "Tähelepanu: Isikliku UserID muutmine on lõplik. Kas soovid kindlasti seda teha? Igaks juhuks soovitame eelmise UserID varundada."
},
"createdBy": { "createdBy": {
"message": "Autor" "message": "Autor"
}, },
@@ -460,6 +478,12 @@
"minDurationDescription": { "minDurationDescription": {
"message": "Segmendid, mis on lühemad kui määratud väärtus ei jäeta vahele või ei kuvata mängijal." "message": "Segmendid, mis on lühemad kui määratud väärtus ei jäeta vahele või ei kuvata mängijal."
}, },
"enableManualSkipOnFullVideo": {
"message": "Jäta käsitsi vahele, kui täis video pealkiri on saadaval"
},
"whatManualSkipOnFullVideo": {
"message": "Inimestele, kes soovivad vaadata (enese-)reklaami sisaldavat videot segamatult."
},
"skipNoticeDuration": { "skipNoticeDuration": {
"message": "Vahelejätmise teavituse kestus (sekundites):" "message": "Vahelejätmise teavituse kestus (sekundites):"
}, },
@@ -469,6 +493,9 @@
"shortCheck": { "shortCheck": {
"message": "See saadetav segment on lühem, kui sinu lühima pikkuse valik. See võib tähendada, et see on juba saadetud ning on selle valiku tõttu lihtsalt ignoreeritud. Kas soovid kindlasti seda saata?" "message": "See saadetav segment on lühem, kui sinu lühima pikkuse valik. See võib tähendada, et see on juba saadetud ning on selle valiku tõttu lihtsalt ignoreeritud. Kas soovid kindlasti seda saata?"
}, },
"liveOrPremiere": {
"message": "Käimasolevale otseülekandele segmentide esitamine pole lubatud. Palun oodake kuni otseülekanne lõpeb, värskendage lehte ja kontrollige siis, kas segmendid on jätkuvalt kehtivad."
},
"showUploadButton": { "showUploadButton": {
"message": "Kuva üleslaadimisnupp" "message": "Kuva üleslaadimisnupp"
}, },
@@ -505,6 +532,9 @@
"exportOptionsUpload": { "exportOptionsUpload": {
"message": "Laadi failist" "message": "Laadi failist"
}, },
"whatExportOptions": {
"message": "See on teie seadistusinformatsioon JSON-formaadis. See sisaldab teie isiklikku UserID-d, seega olge tähelepanelik, kellega seda jagate."
},
"setOptions": { "setOptions": {
"message": "Määra valikud" "message": "Määra valikud"
}, },
@@ -550,6 +580,9 @@
"copyDebugInformationComplete": { "copyDebugInformationComplete": {
"message": "Silumisinfo on lõikelauale kopeeritud. Võid sellelt vabalt eemaldada mistahes info, mida ei soovi jagada. Salvesta see tekstifaili või kleebi vearaportisse." "message": "Silumisinfo on lõikelauale kopeeritud. Võid sellelt vabalt eemaldada mistahes info, mida ei soovi jagada. Salvesta see tekstifaili või kleebi vearaportisse."
}, },
"keyAlreadyUsed": {
"message": "See kiirkombinatsioon on juba kasutuses teise toimingu poolt. Palun valige teine kiirkombinatsioon."
},
"to": { "to": {
"message": "kuni", "message": "kuni",
"description": "Used between segments. Example: 1:20 to 1:30" "description": "Used between segments. Example: 1:20 to 1:30"
@@ -664,6 +697,9 @@
"category_filler": { "category_filler": {
"message": "Täitesisu/naljad" "message": "Täitesisu/naljad"
}, },
"category_filler_description": {
"message": "Video täiteks või huumori eesmärgil lisatud sisu, mis ei ole vajalik video põhieesmärgi mõistmiseks. Selle alla ei kuulu segmendid, mis annavad kontekstiteavet või taustainfot. See on väga agressiivne kategooria, mis on mõeldud olukorraks, kui te pole \"parimas tujus\"."
},
"category_filler_short": { "category_filler_short": {
"message": "Täide" "message": "Täide"
}, },
@@ -709,9 +745,18 @@
"category_chapter": { "category_chapter": {
"message": "Peatükk" "message": "Peatükk"
}, },
"category_chapter_description": {
"message": "Isikupärastatava pealkirjaga peatükid, mis kirjeldavad tähtsamaid video osi."
},
"category_chapter_guideline1": { "category_chapter_guideline1": {
"message": "Ära maini sponsorite brändi nimesid" "message": "Ära maini sponsorite brändi nimesid"
}, },
"category_chapter_guideline2": {
"message": "Kasutage suuri peatükke peamiste osade jaoks"
},
"category_chapter_guideline3": {
"message": "Lühikesemaid peatükke saab paigutada suurtemate peatükkide sisse"
},
"category_livestream_messages": { "category_livestream_messages": {
"message": "Otseülekanne: annetuste ja sõnumite lugemine" "message": "Otseülekanne: annetuste ja sõnumite lugemine"
}, },
@@ -817,6 +862,9 @@
"description": "This error appears in an alert when they try to whitelist a channel and the extension is unable to determine what channel they are looking at.", "description": "This error appears in an alert when they try to whitelist a channel and the extension is unable to determine what channel they are looking at.",
"message": "Kanali ID pole veel laaditud. Kui vaatad manustatud videot, proovi seda vaadata YouTube'i kodulehelt. See võib olla põhjustatud ka YouTube'i paigutuse muudatustest - kui arvad et see nii on, kommenteeri siin:" "message": "Kanali ID pole veel laaditud. Kui vaatad manustatud videot, proovi seda vaadata YouTube'i kodulehelt. See võib olla põhjustatud ka YouTube'i paigutuse muudatustest - kui arvad et see nii on, kommenteeri siin:"
}, },
"invidiousPermissionRefresh": {
"message": "Teie veebilehitseja on eemaldanud loa, mis on vajalik korrektseks tööks Invidious platformil ja muudel kolmandate osapoolte lehtedel. Vajutage allolevat nuppu, et taaskäivitada see luba."
},
"acceptPermission": { "acceptPermission": {
"message": "Nõustu loaga" "message": "Nõustu loaga"
}, },
@@ -877,9 +925,15 @@
"message": "Sain aru", "message": "Sain aru",
"description": "Used as the button to dismiss a tooltip" "description": "Used as the button to dismiss a tooltip"
}, },
"fullVideoTooltipWarning": {
"message": "See segment on üsna suur. Kui kogu video vältel käsitletakse üht teemat, muutke seade \"jäta vahele\" seadeks \"Täis video\". Vaadake juhiseid rohkema info jaoks."
},
"categoryPillTitleText": { "categoryPillTitleText": {
"message": "See terve video on selle kategooriaga sildistatud ning on liiga tihedalt integreeritud, et eraldada saaks" "message": "See terve video on selle kategooriaga sildistatud ning on liiga tihedalt integreeritud, et eraldada saaks"
}, },
"chapterNameTooltipWarning": {
"message": "Üks teie peatükkidest sarnaneb kategooriaga. Kui võimalik, kasutage kategooriaid."
},
"experiementOptOut": { "experiementOptOut": {
"message": "Keeldu kõigist tulevikus tehtavatatest eksperimentidest", "message": "Keeldu kõigist tulevikus tehtavatatest eksperimentidest",
"description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private." "description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private."
@@ -888,7 +942,7 @@
"message": "Peida igaveseks" "message": "Peida igaveseks"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Panime tähele, et tegid teatud sagedasi vigu, mis ei olnud pahatahtlikud" "message": "Märkasime, et olete teinud paar üsna tavalist viga. Oleme teie töö eest väga tänulikud, aga me püüdleme täiuslikkuse poole, seega iga väike viga loeb :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Sa said hoiatuse" "message": "Sa said hoiatuse"
@@ -921,20 +975,38 @@
"message": "Palun vaata allolevad valikud üle" "message": "Palun vaata allolevad valikud üle"
}, },
"helpPageFeatureDisclaimer": { "helpPageFeatureDisclaimer": {
"message": "Paljud funktsioonid on vaikimisi välja lülitatud. Kui sa soovid jätta vahele vaheaejad, kasutada Invidioust jms, lülita need allpool sisse. Sa saad ka peita/kuvada kasutajaliidese elemente." "message": "Paljud funktsioonid on vaikimisi välja lülitatud. Kui te soovite jätta vahele sissejutatused, kokkuvõtted, kasutada Invidioust jms, lülitage need allpool sisse. Te saate ka peita/kuvada kasutajaliidese elemente."
}, },
"helpPageHowSkippingWorks": { "helpPageHowSkippingWorks": {
"message": "Kuidas vahelejätmine töötab" "message": "Kuidas vahelejätmine töötab"
}, },
"helpPageHowSkippingWorks1": {
"message": "Video segmendid jäetakse automaatselt vahele, kui nendele vastavad segmendid leitakse andmebaasist. Saate avada hüpikakna vajutades SponsorBlock-i logo peale - nii saate näha eelvaadet nendest segmentidest."
},
"helpPageHowSkippingWorks2": {
"message": "Iga kord, kui jätate segmendi vahele, saadetakse teie teavitus. Kui segmendi ajastus tundub vale või ebatäpsena - hääletage see valeks vajutades vastuhäälele. Hääletamine on võimalik ka hüpikakna abil."
},
"Submitting": { "Submitting": {
"message": "Saatmine" "message": "Saatmine"
}, },
"helpPageSubmitting1": {
"message": "Segmentide esitamine on võimalik kas käsitsi hüpikakna abil, vajutades \"Segment algab siit\" või läbi videopleieri kasutades pleieriribas olevaid kahte nuppu."
},
"helpPageSubmitting2": {
"message": "Vajutades play-nuppu märgite segmendi alguse ning stop-nuppu vajutades märgite segmendi lõpu. Võite märkida mitu sponsorit enne \"saada\" vajutamist. Vajutades üleslaadimisnupule laetakse teie loodud segmendid andmebaasi. Vajutades prügikastile kustutatakse teie märgitud segmendid."
},
"Editing": { "Editing": {
"message": "Redigeerimine" "message": "Redigeerimine"
}, },
"helpPageEditing1": {
"message": "Juhul, kui tegite vea, saate muuta või kustutada oma segmente vajutades üles näitavat noolt."
},
"helpPageTooSlow": { "helpPageTooSlow": {
"message": "See on liiga aeglane" "message": "See on liiga aeglane"
}, },
"helpPageTooSlow1": {
"message": "Kiirklahvid on saadaval, kui soovite neid kasutada. Vajutage semikooloniga klahvi, et märkida segmendi algus või lõpp ning vajutage ülakomaga klahvi, et oma segmendid esitada. Need kiirklahvid on muudetavad seadetest. Kui te ei kasuta QWERTY-klaviatuuri, tuleb teil tõenäoliselt muuta klahviseoseid."
},
"helpPageCopyOfDatabase": { "helpPageCopyOfDatabase": {
"message": "Kas ma saaksin andmebaasist koopia? Mis juhtub, kui sa kaod?" "message": "Kas ma saaksin andmebaasist koopia? Mis juhtub, kui sa kaod?"
}, },
@@ -1068,15 +1140,29 @@
"redeemFailed": { "redeemFailed": {
"message": "Litsentsivõti on sobimatu" "message": "Litsentsivõti on sobimatu"
}, },
"hideUpsells": {
"message": "Peida valikud, mis on saadaval vaid lisatasu eest"
},
"chooseACountry": { "chooseACountry": {
"message": "Vali riik" "message": "Vali riik"
}, },
"noDiscount": {
"message": "Te ei kvalifitseeru allahindluse saamiseks"
},
"discountLink": { "discountLink": {
"message": "Soodushinna link (vaata roosat hinda)" "message": "Soodushinna link (vaata roosat hinda)"
}, },
"selectYourCountry": { "selectYourCountry": {
"message": "Vali oma riik" "message": "Vali oma riik"
}, },
"alreadyDonated": {
"message": "Kui olete annetanud ükskõik millise summa, saate lunastada oma tasuta ligipääsu saates kirja e-postile:",
"description": "After the colon is an email address"
},
"cantAfford": {
"message": "Kui te ei saa lubada endale litsensi soetamist, vajutage {here}, et uurida, kas kvalifitseerute allahindluse saamisele",
"description": "Keep the curly braces. The word 'here' should be translated as well."
},
"patreonSignIn": { "patreonSignIn": {
"message": "Logi Patreoniga sisse" "message": "Logi Patreoniga sisse"
}, },
@@ -1095,6 +1181,17 @@
"chaptersPage1": { "chaptersPage1": {
"message": "SponsorBlocki kogukonna-loodud peatükkide funktsioon on saadaval vaid inimestele, kes ostavad litsentsi või neile, kes saavad tasuta ligipääsu varasemate panuste tõttu" "message": "SponsorBlocki kogukonna-loodud peatükkide funktsioon on saadaval vaid inimestele, kes ostavad litsentsi või neile, kes saavad tasuta ligipääsu varasemate panuste tõttu"
}, },
"chaptersPage2": {
"message": "NB: Teie luba peatükke esitada on jätkuvalt põhinev reputatsiooni arvutustel. Litsensi soetamine lubab vaid teiste inimeste peatükke näha",
"description": "On the chapters page for getting access to the paid chapters feature"
},
"chapterNewFeature": {
"message": "Uus Funktsioon: Rahva poolt rahastatavad isikupärased peatükid. Need on isikupärastatava pealikirjaga peatükid videos, mis on samuti ühildatavad, et näidata täpsemat segmendi pikkust. Soetage litsents, et näha selle video jaoks esitatud peatükke, nt: ",
"description": "After the comma, a list of chapters for this video will appear"
},
"chapterNewFeature2": {
"message": "Uus Funktsioon: Rahva poolt rahastatavad isikupärased peatükid. Need on isikupärastatava pealikirjaga peatükid videos, mis on samuti ühildatavad, et näidata täpsemat segmendi pikkust. Teil on tasuta ligipääs, lülitage see seadetest sisse."
},
"unsubmittedSegmentCounts": { "unsubmittedSegmentCounts": {
"message": "Sul on hetkel {0} {1}", "message": "Sul on hetkel {0} {1}",
"description": "Example: You currently have 12 unsubmitted segments on 5 videos" "description": "Example: You currently have 12 unsubmitted segments on 5 videos"
@@ -1151,7 +1248,7 @@
"message": "Jaga URLina" "message": "Jaga URLina"
}, },
"segmentFetchFailureWarning": { "segmentFetchFailureWarning": {
"message": "Hoiatus: Server ei ole veel segmentidega vastanud. Sellel videol võivad juba olla saadetud segmendid, aga sa ei ole veel neid serveri vea tõttu saanud." "message": "Tähelepanu: Server ei ole veel segmente edastanud. Sellel videol võivad olla esitatud segmendid, aga te pole neid serveri tõrke tõttu kätte saanud."
}, },
"allowScrollingToEdit": { "allowScrollingToEdit": {
"message": "Luba aegade muutmiseks kerimine" "message": "Luba aegade muutmiseks kerimine"

View File

@@ -942,7 +942,7 @@
"message": "Piilota ikuisesti" "message": "Piilota ikuisesti"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Huomasimme, että teit joitakin yleisiä virheitä, jotka eivät ole haitallisia" "message": "Huomasimme, että teit joitakin yleisiä virheitä. Arvostamme työtäsi erittäin paljon, mutta pyrimme täydellisyyteen, joten pienilläkin virheillä on väliä :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Sait varoituksen" "message": "Sait varoituksen"

View File

@@ -942,7 +942,7 @@
"message": "Cacher pour toujours" "message": "Cacher pour toujours"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Nous avons remarqué que vous faisiez des erreurs courantes qui ne sont pas intentionnelles" "message": "Nous avons remarqué que vous commettiez des erreurs courantes. Nous apprécions votre travail jusqu'à présent, mais nous recherchons la perfection, donc même de petites erreurs comptent :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Vous avez reçu un avertissement" "message": "Vous avez reçu un avertissement"
@@ -1249,5 +1249,8 @@
}, },
"segmentFetchFailureWarning": { "segmentFetchFailureWarning": {
"message": "Attention : Le serveur n'a pas encore répondu avec les segments. Il se peut qu'il y ait des segments sur cette vidéo déjà soumise, mais vous ne les avez tout simplement pas reçus en raison de problèmes avec le serveur." "message": "Attention : Le serveur n'a pas encore répondu avec les segments. Il se peut qu'il y ait des segments sur cette vidéo déjà soumise, mais vous ne les avez tout simplement pas reçus en raison de problèmes avec le serveur."
},
"allowScrollingToEdit": {
"message": "Autoriser l'utilisation de la molette pour modifier les temps"
} }
} }

View File

@@ -26,17 +26,17 @@
"message": "מקטעים" "message": "מקטעים"
}, },
"SegmentsCap": { "SegmentsCap": {
"message": "מקטעים" "message": "קטעים"
}, },
"Chapters": { "Chapters": {
"message": "פרקים" "message": "פרקים"
}, },
"renderAsChapters": { "renderAsChapters": {
"message": "עיבוד מקטעים כפרקים", "message": "עיבוד קטעים כפרקים",
"description": "Refers to drawing segments on the YouTube seek bar as split up chapters, similar to the existing chapter system" "description": "Refers to drawing segments on the YouTube seek bar as split up chapters, similar to the existing chapter system"
}, },
"showSegmentNameInChapterBar": { "showSegmentNameInChapterBar": {
"message": "הצגת מקטע נוכחי לצד זמן וידאו" "message": "הצגת קטע נוכחי לצד זמן וידאו"
}, },
"upvoteButtonInfo": { "upvoteButtonInfo": {
"message": "הצבע לדיווח הזה" "message": "הצבע לדיווח הזה"
@@ -96,13 +96,13 @@
"message": "מקטע נגמר עכשיו" "message": "מקטע נגמר עכשיו"
}, },
"sponsorCancel": { "sponsorCancel": {
"message": "ביטול יצירת מקטע" "message": "ביטול יצירת קטע"
}, },
"noVideoID": { "noVideoID": {
"message": "לא נמצא סרטון יוטיוב.\nאם זה לא נכון, רענן את הכרטיסייה." "message": "לא נמצא סרטון יוטיוב.\nאם זה לא נכון, רענן את הכרטיסייה."
}, },
"refreshSegments": { "refreshSegments": {
"message": "ריענון מקטעים" "message": "ריענון קטעים"
}, },
"success": { "success": {
"message": "הצלחה!" "message": "הצלחה!"
@@ -117,7 +117,7 @@
"message": "התקבלה שגיאת חיבור. קוד השגיאה: " "message": "התקבלה שגיאת חיבור. קוד השגיאה: "
}, },
"segmentsStillLoading": { "segmentsStillLoading": {
"message": "מקטע עדיין נטען..." "message": "קטעים עדיין נטענים..."
}, },
"clearTimes": { "clearTimes": {
"message": "נקה מקטעים" "message": "נקה מקטעים"
@@ -228,6 +228,30 @@
"whatViewTracking": { "whatViewTracking": {
"message": "תכונה זו עוקבת אחר אילו מקטעים דילגת כדי לאפשר למשתמשים לדעת עד כמה ההגשה שלהם עזרה לאחרים והשתמשה כמדד יחד עם הצבעות בעד כדי להבטיח שספאם לא ייכנס למסד הנתונים. התוסף שולח הודעה לשרת בכל פעם שאתה מדלג על קטע. אני מקווה שרוב האנשים לא ישנו את ההגדרה הזו כדי שמספרי התצוגה יהיו מדויקים. :)" "message": "תכונה זו עוקבת אחר אילו מקטעים דילגת כדי לאפשר למשתמשים לדעת עד כמה ההגשה שלהם עזרה לאחרים והשתמשה כמדד יחד עם הצבעות בעד כדי להבטיח שספאם לא ייכנס למסד הנתונים. התוסף שולח הודעה לשרת בכל פעם שאתה מדלג על קטע. אני מקווה שרוב האנשים לא ישנו את ההגדרה הזו כדי שמספרי התצוגה יהיו מדויקים. :)"
}, },
"enableTrackDownvotes": {
"message": "שמור דירוג קטעים"
},
"whatTrackDownvotes": {
"message": "כל קטע שידורג על ידך יישאר חבוי, גם לאחר רענון"
},
"trackDownvotesWarning": {
"message": "אזהרה: ביטול השמירה ימחק את הדירוגים שנשמרו"
},
"enableRefetchWhenNotFound": {
"message": "השג מקטעים של סרטונים חדשים"
},
"whatRefetchWhenNotFound": {
"message": "אם הסרטון חדש, ואין בו אף מקטע, הוא יחפש מחדש מקטעים כל כמה דקות בהן תצפה/י בסרטון."
},
"enableShowCategoryWithoutPermission": {
"message": "הצג קטגוריה בתפריט ההגשה גם ללא הרשאת הגשה"
},
"whatShowCategoryWithoutPermission": {
"message": "ישנן קטגוריות שדורשות הרשאת הגשה בגלל דרישות מינימום נקודות רפוטציה"
},
"showNotice": {
"message": "הראה הודעה שוב"
},
"website": { "website": {
"message": "אתר", "message": "אתר",
"description": "Used on Firefox Store Page" "description": "Used on Firefox Store Page"

View File

@@ -113,6 +113,9 @@
"connectionError": { "connectionError": {
"message": "Dogodija se greška u povezivanju. Kod pogreške: " "message": "Dogodija se greška u povezivanju. Kod pogreške: "
}, },
"segmentsStillLoading": {
"message": "Segmenti se učitavaju..."
},
"clearTimes": { "clearTimes": {
"message": "Očisti isječke" "message": "Očisti isječke"
}, },
@@ -383,6 +386,12 @@
"statusReminder": { "statusReminder": {
"message": "Provjerite status.sponsor.ajay.app za status poslužitelja." "message": "Provjerite status.sponsor.ajay.app za status poslužitelja."
}, },
"changeUserID": {
"message": "Uvezi/Izvezi svoj privatni UserID"
},
"setUserID": {
"message": "Postavi Privatni UserID"
},
"createdBy": { "createdBy": {
"message": "Izradio" "message": "Izradio"
}, },
@@ -482,6 +491,12 @@
"edit": { "edit": {
"message": "Uredi" "message": "Uredi"
}, },
"copyDebugInformation": {
"message": "Kopiraj Informaciju Za Otklanjanje Pogrešaka U Međuspremnik"
},
"copyDebugInformationFailed": {
"message": "Pisanje u međuspremnik nije uspjelo"
},
"copyDebugInformationOptions": { "copyDebugInformationOptions": {
"message": "Kopira informacije u međuspremnik koje treba dati razvojnom programeru kada otkrije grešku / kada to programer zatraži. Osjetljive informacije kao što su vaš korisnički ID, kanali s popisa dopuštenih i prilagođena adresa poslužitelja uklonjeni su. Međutim, sadrži informacije kao što su vaš korisnički agent, preglednik, operativni sustav i broj verzije proširenja. " "message": "Kopira informacije u međuspremnik koje treba dati razvojnom programeru kada otkrije grešku / kada to programer zatraži. Osjetljive informacije kao što su vaš korisnički ID, kanali s popisa dopuštenih i prilagođena adresa poslužitelja uklonjeni su. Međutim, sadrži informacije kao što su vaš korisnički agent, preglednik, operativni sustav i broj verzije proširenja. "
}, },
@@ -517,6 +532,9 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Slično kao „sponzor”, osim za neplaćenu promociju i samopromociju. To uključuje odlomke marketinga, donacija ili informacija o tome, s kim su surađivali." "message": "Slično kao „sponzor”, osim za neplaćenu promociju i samopromociju. To uključuje odlomke marketinga, donacija ili informacija o tome, s kim su surađivali."
}, },
"category_selfpromo_guideline1": {
"message": "Donacije, članarine i roba"
},
"category_exclusive_access": { "category_exclusive_access": {
"message": "Eksluzivni pristup" "message": "Eksluzivni pristup"
}, },
@@ -538,6 +556,9 @@
"category_intro_short": { "category_intro_short": {
"message": "Stanka" "message": "Stanka"
}, },
"category_intro_guideline1": {
"message": "Interval bez stvarnog sadržaja"
},
"category_outro": { "category_outro": {
"message": "Završni kadrovi/Zasluge" "message": "Završni kadrovi/Zasluge"
}, },
@@ -547,6 +568,9 @@
"category_preview_description": { "category_preview_description": {
"message": "Zbirka isječaka koji pokazuju što dolazi u ovom videu ili drugim videima u nizu gdje se sve informacije kasnije ponavljaju u videu." "message": "Zbirka isječaka koji pokazuju što dolazi u ovom videu ili drugim videima u nizu gdje se sve informacije kasnije ponavljaju u videu."
}, },
"category_filler": {
"message": "Nepotrebna Popuna/Šale"
},
"category_filler_short": { "category_filler_short": {
"message": "Popuna" "message": "Popuna"
}, },
@@ -647,6 +671,9 @@
"message": "Kraj", "message": "Kraj",
"description": "Button that skips to the end of a segment" "description": "Button that skips to the end of a segment"
}, },
"hiddenDueToDuration": {
"message": "sakriveno: redak je prekratak"
},
"manuallyHidden": { "manuallyHidden": {
"message": "ručno skriveno" "message": "ručno skriveno"
}, },
@@ -701,15 +728,33 @@
"hideForever": { "hideForever": {
"message": "Sakrij zauvijek" "message": "Sakrij zauvijek"
}, },
"warningChatInfo": {
"message": "Primjetili smo da ste radili neke uobičajene pogreške. Mi cijenimo vaš trud, ali ovdje težimo perfekciji, pa i jako male pogreške su važne :)"
},
"warningTitle": {
"message": "Dobili ste upozorenje"
},
"questionButton": {
"message": "Imam pitanje"
},
"warningConfirmButton": {
"message": "Razumijem razlog"
},
"Donate": { "Donate": {
"message": "Doniraj" "message": "Doniraj"
}, },
"darkModeOptionsPage": {
"message": "Tamni Način Na Stranici Opcija"
},
"helpPageThanksForInstalling": { "helpPageThanksForInstalling": {
"message": "Hvala na instaliranju SponsorBlocka." "message": "Hvala na instaliranju SponsorBlocka."
}, },
"helpPageFeatureDisclaimer": { "helpPageFeatureDisclaimer": {
"message": "Mnoge funkcije su standardno deaktivirane. Ako želiš preskočiti uvode, završne dijelove, koristiti Invidious, itd., aktiviraj ih niže dolje. Također možeš sakriti/prikazati elemente korisničkog sučelja." "message": "Mnoge funkcije su standardno deaktivirane. Ako želiš preskočiti uvode, završne dijelove, koristiti Invidious, itd., aktiviraj ih niže dolje. Također možeš sakriti/prikazati elemente korisničkog sučelja."
}, },
"helpPageHowSkippingWorks": {
"message": "Kako preskakanje radi"
},
"Submitting": { "Submitting": {
"message": "Slanje" "message": "Slanje"
}, },
@@ -746,6 +791,9 @@
"hideSegment": { "hideSegment": {
"message": "Sakri odsječak" "message": "Sakri odsječak"
}, },
"skipSegment": {
"message": "Preskoči odsječak"
},
"dayAbbreviation": { "dayAbbreviation": {
"message": "d", "message": "d",
"description": "100d" "description": "100d"
@@ -852,5 +900,24 @@
}, },
"enterLicenseKey": { "enterLicenseKey": {
"message": "Upiši licencni ključ" "message": "Upiši licencni ključ"
},
"videosSingular": {
"message": "video",
"description": "Example: You currently have 3 unsubmitted segments on 1 *video*"
},
"videosPlural": {
"message": "videa",
"description": "Example: You currently have 12 unsubmitted segments on 5 *videos*"
},
"videoID": {
"message": "ID videa",
"description": "Header of the unsubmitted segments list"
},
"actions": {
"message": "Radnje",
"description": "Header of the unsubmitted segments list"
},
"exportSegmentsAsURL": {
"message": "Dijeli kao URL"
} }
} }

View File

@@ -942,7 +942,7 @@
"message": "Eltüntetés örökre" "message": "Eltüntetés örökre"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Észrevettük, hogy néhány nem rossz szándékú, gyakran előforduló hibát ejtettél" "message": "Észrevettük, hogy néhány gyakran előforduló hibát vétettél. Nagyra értékeljük az eddigi munkádat, de a tökéletességre törekszünk, úgyhogy a legkisebb hibák is számítanak :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Figyelmeztetést kaptál" "message": "Figyelmeztetést kaptál"
@@ -1249,5 +1249,8 @@
}, },
"segmentFetchFailureWarning": { "segmentFetchFailureWarning": {
"message": "Figyelem: A szerver még nem válaszolt szegmensekkel. Elképzelhető, hogy már vannak beküldött szegmensek ezen a videón, csak még nem töltődtek be szerver-problémák miatt." "message": "Figyelem: A szerver még nem válaszolt szegmensekkel. Elképzelhető, hogy már vannak beküldött szegmensek ezen a videón, csak még nem töltődtek be szerver-problémák miatt."
},
"allowScrollingToEdit": {
"message": "Görgő használatának engedélyezése az idők szerkesztéséhez"
} }
} }

View File

@@ -208,7 +208,7 @@
"message": "Ini akan menyembunyikan tombol yang muncul di pemutar YouTube untuk mengirimkan segmen yang dilewati." "message": "Ini akan menyembunyikan tombol yang muncul di pemutar YouTube untuk mengirimkan segmen yang dilewati."
}, },
"showSkipButton": { "showSkipButton": {
"message": "Tetap lewati ke tombol Highlight di Pemutar" "message": "Biarkan Tombol Lewati ke Sorotan Tetap Tampil di Pemutar"
}, },
"showInfoButton": { "showInfoButton": {
"message": "Tampilkan Tombol Info Di Pemutar Video YouTube" "message": "Tampilkan Tombol Info Di Pemutar Video YouTube"
@@ -682,6 +682,9 @@
"category_preview": { "category_preview": {
"message": "Pratinjau/Rekap" "message": "Pratinjau/Rekap"
}, },
"category_preview_description": {
"message": "Koleksi klip yang menampilkan hal yang akan hadir di video ini atau video lain dalam suatu seri, di mana segala informasinya diulang dalam video tersebut."
},
"category_preview_guideline1": { "category_preview_guideline1": {
"message": "Klip yang ditampilkan nanti, atau di video di masa mendatang" "message": "Klip yang ditampilkan nanti, atau di video di masa mendatang"
}, },
@@ -694,6 +697,9 @@
"category_filler": { "category_filler": {
"message": "Pengisi Tak berkaitan/Lawakan" "message": "Pengisi Tak berkaitan/Lawakan"
}, },
"category_filler_description": {
"message": "Adegan tangensial ditambahkan hanya untuk pengisi atau humor yang tidak diperlukan untuk memahami isi utama video. Ini tidak boleh mencakup segmen yang memberikan detail konteks atau latar belakang. Ini adalah kategori sangat agresif yang ditujukan saat Anda tidak sedang ingin \"bersenang-senang\"."
},
"category_filler_short": { "category_filler_short": {
"message": "Isian" "message": "Isian"
}, },
@@ -936,7 +942,7 @@
"message": "Sembunyikan selamanya" "message": "Sembunyikan selamanya"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Kami melihat Anda membuat beberapa kesalahan umum yang tidak tersengaja" "message": "Kami perhatikan Anda membuat beberapa kesalahan umum. Kami hargai sekali yang Anda kerjakan sejauh ini, tapi di sini kami berjuang mencapai sempurna, sehingga kesalahan sangat kecil pun berarti :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Anda mendapatkan peringatan" "message": "Anda mendapatkan peringatan"
@@ -1172,10 +1178,20 @@
"enterLicenseKey": { "enterLicenseKey": {
"message": "Masukkan Kode Lisensi" "message": "Masukkan Kode Lisensi"
}, },
"chaptersPage1": {
"message": "Fitur bab yang diurun-dayakan SponsorBlock hanya tersedia bagi pembeli lisensi, atau penerima akses gratis berkat kontribusinya di masa lalu"
},
"chaptersPage2": { "chaptersPage2": {
"message": "Catatan: Izin untuk mengirim bab tetap bedasarkan reputasi yang diperhitungkan. Membeli lisensi hanya memperbolehkan Anda untuk melihat bab yang dikirim oleh orang lain", "message": "Catatan: Izin untuk mengirim bab tetap bedasarkan reputasi yang diperhitungkan. Membeli lisensi hanya memperbolehkan Anda untuk melihat bab yang dikirim oleh orang lain",
"description": "On the chapters page for getting access to the paid chapters feature" "description": "On the chapters page for getting access to the paid chapters feature"
}, },
"chapterNewFeature": {
"message": "Fitur Baru: Bab ubah suai yang diurun daya. Ini adalah beberapa bagian video yang secara khusus diberi nama dan dapat ditumpuk untuk mendapat ketepatan yang lebih. Beli lisensi untuk menampilkan bab yang dikirimkan untuk video ini seperti: ",
"description": "After the comma, a list of chapters for this video will appear"
},
"chapterNewFeature2": {
"message": "Fitur Baru: Bab ubah suai yang diurun daya. Ini adalah beberapa bagian video yang secara khusus diberi nama dan dapat ditumpuk untuk mendapat ketepatan yang lebih. Anda memiliki akses gratis yang aktif di opsi."
},
"unsubmittedSegmentCounts": { "unsubmittedSegmentCounts": {
"message": "Kamu mempunyai {0} dalam {1}", "message": "Kamu mempunyai {0} dalam {1}",
"description": "Example: You currently have 12 unsubmitted segments on 5 videos" "description": "Example: You currently have 12 unsubmitted segments on 5 videos"

View File

@@ -942,7 +942,7 @@
"message": "Nascondi per sempre" "message": "Nascondi per sempre"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Abbiamo notato che stavi facendo alcuni errori comuni che non sono intenzionali" "message": "Abbiamo notato che hai fatto qualche errore comune. Noi apprezziamo molto il tuo lavoro fin'ora, ma qui puntiamo alla perfezione, quindi ogni errore, anche molto piccolo, conta :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Hai ricevuto un avvertimento" "message": "Hai ricevuto un avvertimento"

View File

@@ -942,7 +942,7 @@
"message": "二度と表示しない" "message": "二度と表示しない"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "悪意によるものではないよくある間違いを犯してたようです" "message": "どうやらあなたはよくある間違いを犯してしまったようです。これまでのあなたの協力には大きく感謝しています。ですが、私たちは完璧さを追求しており、小さなミスでも重要であることをご理解ください :)"
}, },
"warningTitle": { "warningTitle": {
"message": "警告を付与されました" "message": "警告を付与されました"

View File

@@ -942,7 +942,7 @@
"message": "다시 보지 않음" "message": "다시 보지 않음"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "악의적인 의도는 없었던 듯 보이지만 제출하신 구간 중에 일부 문제가 있는 것 같아요." "message": "현재 일반적인 실수들을 저지르고 계셔서 알려드려요. 지금까지 작업해주신 것은 감사드리지만, 저희는 완벽을 추구하고 있기 때문에 아주 작은 실수라도 문제가 될 수 있어요 :)"
}, },
"warningTitle": { "warningTitle": {
"message": "경고를 받으셨어요" "message": "경고를 받으셨어요"
@@ -1248,9 +1248,9 @@
"message": "URL로 공유" "message": "URL로 공유"
}, },
"segmentFetchFailureWarning": { "segmentFetchFailureWarning": {
"message": "경고: 아직 서버로부터 구간 정보를 지 못했습니다. 동영상에 이미 제출된 구간이 있지만 서버의 문제로 수신못했을 수 있습니다." "message": "경고: 서버에서 구간 정보를 불러오지 못했어요. 동영상에 이미 제출된 구간이 있을 수 있지만 서버의 문제로 인해 수신않았을 수어요."
}, },
"allowScrollingToEdit": { "allowScrollingToEdit": {
"message": "스크롤하여 수정" "message": "스크롤하여 편집 허용"
} }
} }

View File

@@ -941,9 +941,6 @@
"hideForever": { "hideForever": {
"message": "Sembunyikan selamanya" "message": "Sembunyikan selamanya"
}, },
"warningChatInfo": {
"message": "Kami perasaan yang anda telah membuat beberapa kesilapan biasa yang tidak berniat jahat"
},
"warningTitle": { "warningTitle": {
"message": "Anda mendapat amaran" "message": "Anda mendapat amaran"
}, },

View File

@@ -942,7 +942,7 @@
"message": "Voor altijd verbergen" "message": "Voor altijd verbergen"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "We hebben gemerkt dat u een aantal veelvoorkomende fouten maakte zonder slechte bedoelingen" "message": "Het viel ons op dat u enkele veel voorkomende fouten maakte. We waarderen uw werk tot nu toe zeer, maar we streven hier naar perfectie, dus zelfs heel kleine foutjes zijn belangrijk :)"
}, },
"warningTitle": { "warningTitle": {
"message": "U heeft een waarschuwing gekregen" "message": "U heeft een waarschuwing gekregen"

View File

@@ -941,9 +941,6 @@
"hideForever": { "hideForever": {
"message": "Schowaj na zawsze" "message": "Schowaj na zawsze"
}, },
"warningChatInfo": {
"message": "Zauważyliśmy, że popełniałeś pewne powszechne błędy, które nie są złośliwe"
},
"warningTitle": { "warningTitle": {
"message": "Otrzymałeś ostrzeżenie" "message": "Otrzymałeś ostrzeżenie"
}, },

View File

@@ -116,6 +116,9 @@
"connectionError": { "connectionError": {
"message": "Ocorreu um erro de conexão. Código de erro: " "message": "Ocorreu um erro de conexão. Código de erro: "
}, },
"segmentsStillLoading": {
"message": "Segmentos ainda carregando..."
},
"clearTimes": { "clearTimes": {
"message": "Limpar Segmentos" "message": "Limpar Segmentos"
}, },
@@ -912,9 +915,6 @@
"hideForever": { "hideForever": {
"message": "Ocultar para sempre" "message": "Ocultar para sempre"
}, },
"warningChatInfo": {
"message": "Percebemos que você estava cometendo alguns erros comuns que não são prejudiciais"
},
"Donate": { "Donate": {
"message": "Doar" "message": "Doar"
}, },

View File

@@ -116,6 +116,9 @@
"connectionError": { "connectionError": {
"message": "A apărut o eroare de conexiune. Cod de eroare: " "message": "A apărut o eroare de conexiune. Cod de eroare: "
}, },
"segmentsStillLoading": {
"message": "Încă se încarcă segmentele..."
},
"clearTimes": { "clearTimes": {
"message": "Curăță segmentele" "message": "Curăță segmentele"
}, },
@@ -939,7 +942,7 @@
"message": "Ascunde pentru totdeauna" "message": "Ascunde pentru totdeauna"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Am observat că făceați niște greșeli comune care nu sunt răuvoitoare" "message": "Am observat că faci niște greșeli comune. Apreciem foarte mult munca ta de până acum, însă noi tindem spre perfecțiune aici, așa că fiecare mică greșeală contează :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Ai primit o avertizare" "message": "Ai primit o avertizare"
@@ -1243,5 +1246,11 @@
}, },
"exportSegmentsAsURL": { "exportSegmentsAsURL": {
"message": "Distribuie ca URL" "message": "Distribuie ca URL"
},
"segmentFetchFailureWarning": {
"message": "Atenție: Serverul nu a răspuns încă cu segmente. Ar putea fi segmente în acest videoclip deja trimise, dar pur și simplu nu le-ai primit din cauza problemelor cu serverul."
},
"allowScrollingToEdit": {
"message": "Permite derularea la Editarea Timpilor"
} }
} }

View File

@@ -942,7 +942,7 @@
"message": "Скрыть навсегда" "message": "Скрыть навсегда"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Мы заметили, что вы совершаете распространённые и не злонамеренные ошибки" "message": "Мы заметили, что вы совершаете распространенные ошибки. Мы очень ценим вашу работу, но мы стремимся делать всё идеально, так что даже очень маленькие ошибки имеют большое значение :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Вы получили предупреждение" "message": "Вы получили предупреждение"

View File

@@ -942,7 +942,7 @@
"message": "Navždy skryť" "message": "Navždy skryť"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Zaznamenali sme, že robíte omylom chyby" "message": "Zaznamenali sme, že ste urobili obvyklú chybu. Vážime si vašej práce, ale ide nám o presnosť, takže aj na malých chybách záleží."
}, },
"warningTitle": { "warningTitle": {
"message": "Dostali ste varovanie" "message": "Dostali ste varovanie"
@@ -954,7 +954,7 @@
"message": "Rozumiem dôvodu" "message": "Rozumiem dôvodu"
}, },
"warningError": { "warningError": {
"message": "Chyb a pri pokuse o súhlas s upozornením:" "message": "Chyba pri pokuse o súhlas s upozornením:"
}, },
"Donate": { "Donate": {
"message": "Prispieť" "message": "Prispieť"

View File

@@ -942,7 +942,7 @@
"message": "Dölj för alltid" "message": "Dölj för alltid"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Vi märkte att du gjorde några vanliga misstag som inte var uppsåtligt" "message": "Vi märkte att du gjorde några vanliga misstag. Vi uppskattar verkligen ditt arbete hittills, men vi strävar mot perfektion här, så även mycket små misstag spelar roll :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Du har fått en varning" "message": "Du har fått en varning"

View File

@@ -25,6 +25,9 @@
"Segments": { "Segments": {
"message": "ส่วน" "message": "ส่วน"
}, },
"SegmentsCap": {
"message": "ส่วน"
},
"upvoteButtonInfo": { "upvoteButtonInfo": {
"message": "เห็นด้วยกับรายงานนี้" "message": "เห็นด้วยกับรายงานนี้"
}, },

View File

@@ -479,7 +479,7 @@
"message": "Ayarlanan değerden daha kısa kısımlar atlanmayacak veya oynatıcıda gösterilmeyecektir." "message": "Ayarlanan değerden daha kısa kısımlar atlanmayacak veya oynatıcıda gösterilmeyecektir."
}, },
"enableManualSkipOnFullVideo": { "enableManualSkipOnFullVideo": {
"message": "Tam bir video etiketi mevcut olduğunda manuel atlama özelliğini kullanın" "message": "Bütün video etiketi mevcut olduğunda manuel atlama özelliğini kullanın"
}, },
"whatManualSkipOnFullVideo": { "whatManualSkipOnFullVideo": {
"message": "Video boyunca sponsorlu veya kendi reklamını yapıyorsa videoyu kesintisiz izlemek isteyenler için." "message": "Video boyunca sponsorlu veya kendi reklamını yapıyorsa videoyu kesintisiz izlemek isteyenler için."
@@ -942,7 +942,7 @@
"message": "Asla gösterme" "message": "Asla gösterme"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Kötü amaçlı olmayan bazı yaygın hatalar yaptığınızı fark ettik" "message": "Yaygın hataları sıkça yaptığınız anlaşılıyor. Çabalarınız için sağolun fakat burada mükemmel bir sonuç istiyoruz, küçük hatalar bile fark ediyor :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Bir uyarı aldın" "message": "Bir uyarı aldın"

View File

@@ -942,7 +942,7 @@
"message": "Сховати назавжди" "message": "Сховати назавжди"
}, },
"warningChatInfo": { "warningChatInfo": {
"message": "Ми помітили, що ви не зловмисно робили деякі поширені помилки" "message": "Ми помітили, що ви робите деякі типові помилки. Ми дуже цінуємо вашу роботу, але ми прагнемо до досконалості, тому навіть дуже маленькі помилки мають значення :)"
}, },
"warningTitle": { "warningTitle": {
"message": "Ви отримали попередження" "message": "Ви отримали попередження"
@@ -1249,5 +1249,8 @@
}, },
"segmentFetchFailureWarning": { "segmentFetchFailureWarning": {
"message": "Попередження: сервер ще не завантажив сегменти. Можливо, у цьому відео вже є сегменти, але ви просто не отримали їх через проблеми з сервером." "message": "Попередження: сервер ще не завантажив сегменти. Можливо, у цьому відео вже є сегменти, але ви просто не отримали їх через проблеми з сервером."
},
"allowScrollingToEdit": {
"message": "Дозволити прокручування для редагування часу"
} }
} }

View File

@@ -25,6 +25,16 @@
"Segments": { "Segments": {
"message": "phân đoạn" "message": "phân đoạn"
}, },
"SegmentsCap": {
"message": "Phân đoạn"
},
"Chapters": {
"message": "Chương"
},
"renderAsChapters": {
"message": "Coi phân đoạn như chương",
"description": "Refers to drawing segments on the YouTube seek bar as split up chapters, similar to the existing chapter system"
},
"upvoteButtonInfo": { "upvoteButtonInfo": {
"message": "Tán thành phân đoạn này" "message": "Tán thành phân đoạn này"
}, },
@@ -103,6 +113,9 @@
"connectionError": { "connectionError": {
"message": "Đã xảy ra lỗi kết nối. Mã lỗi: " "message": "Đã xảy ra lỗi kết nối. Mã lỗi: "
}, },
"segmentsStillLoading": {
"message": "Phân đoạn vẫn đang tải..."
},
"clearTimes": { "clearTimes": {
"message": "Xóa các phân đoạn" "message": "Xóa các phân đoạn"
}, },
@@ -115,6 +128,9 @@
"SubmitTimes": { "SubmitTimes": {
"message": "Gửi phân đoạn" "message": "Gửi phân đoạn"
}, },
"sortSegments": {
"message": "Sắp xếp phân đoạn"
},
"submitCheck": { "submitCheck": {
"message": "Bạn có chắc chắn muốn gửi không?" "message": "Bạn có chắc chắn muốn gửi không?"
}, },
@@ -289,6 +305,14 @@
"message": "Gửi phân đoạn", "message": "Gửi phân đoạn",
"description": "Keybind label" "description": "Keybind label"
}, },
"nextChapterKeybind": {
"message": "Chương tiếp",
"description": "Keybind label"
},
"previousChapterKeybind": {
"message": "Chương trước",
"description": "Keybind label"
},
"keybindDescription": { "keybindDescription": {
"message": "Chọn một phím bằng cách nhập nó và chọn bất kỳ phím bổ trợ nào bạn muốn sử dụng." "message": "Chọn một phím bằng cách nhập nó và chọn bất kỳ phím bổ trợ nào bạn muốn sử dụng."
}, },
@@ -533,6 +557,10 @@
"message": "đến", "message": "đến",
"description": "Used between segments. Example: 1:20 to 1:30" "description": "Used between segments. Example: 1:20 to 1:30"
}, },
"CopiedExclamation": {
"message": "Đã sao chép!",
"description": "Used after something has been copied to the clipboard. Example: 'Copied!'"
},
"generic_guideline2": { "generic_guideline2": {
"message": "Chơi như thể không có gì bị bỏ qua" "message": "Chơi như thể không có gì bị bỏ qua"
}, },
@@ -669,6 +697,9 @@
"category_poi_highlight_guideline3": { "category_poi_highlight_guideline3": {
"message": "Có thể bỏ qua để đến tiêu đề hoặc hình thu nhỏ" "message": "Có thể bỏ qua để đến tiêu đề hoặc hình thu nhỏ"
}, },
"category_chapter": {
"message": "Chương"
},
"category_livestream_messages": { "category_livestream_messages": {
"message": "Luồng phát trực tiếp: Đọc Quyên góp/Tin nhắn" "message": "Luồng phát trực tiếp: Đọc Quyên góp/Tin nhắn"
}, },
@@ -754,6 +785,10 @@
"bracketEnd": { "bracketEnd": {
"message": "(Kết thúc)" "message": "(Kết thúc)"
}, },
"End": {
"message": "Kết thúc",
"description": "Button that skips to the end of a segment"
},
"hiddenDueToDownvote": { "hiddenDueToDownvote": {
"message": "đã ẩn: hạ bình chọn" "message": "đã ẩn: hạ bình chọn"
}, },
@@ -791,6 +826,9 @@
"downvoteDescription": { "downvoteDescription": {
"message": "Chỉnh thời gian sai/không đúng" "message": "Chỉnh thời gian sai/không đúng"
}, },
"incorrectVote": {
"message": "Sai"
},
"incorrectCategory": { "incorrectCategory": {
"message": "Đổi danh mục" "message": "Đổi danh mục"
}, },
@@ -941,6 +979,9 @@
"hideSegment": { "hideSegment": {
"message": "Ẩn phân đoạn" "message": "Ẩn phân đoạn"
}, },
"skipSegment": {
"message": "Bỏ qua phân đoạn"
},
"SponsorTimeEditScrollNewFeature": { "SponsorTimeEditScrollNewFeature": {
"message": "Sử dụng con lăn chuột của bạn khi di chuột qua hộp chỉnh sửa để nhanh chóng điều chỉnh thời gian. Kết hợp phím ctrl hoặc shift có thể được sử dụng để tinh chỉnh các thay đổi." "message": "Sử dụng con lăn chuột của bạn khi di chuột qua hộp chỉnh sửa để nhanh chóng điều chỉnh thời gian. Kết hợp phím ctrl hoặc shift có thể được sử dụng để tinh chỉnh các thay đổi."
}, },
@@ -1003,5 +1044,24 @@
}, },
"confirmResetToDefault": { "confirmResetToDefault": {
"message": "Bạn chắc chắn muốn đặt lại cài đặt về mặc định? Điều này sẽ không thể hoàn tác được." "message": "Bạn chắc chắn muốn đặt lại cài đặt về mặc định? Điều này sẽ không thể hoàn tác được."
},
"chooseACountry": {
"message": "Chọn một quốc gia"
},
"selectYourCountry": {
"message": "Chọn quốc gia"
},
"patreonSignIn": {
"message": "Đăng nhập bằng Patreon"
},
"joinOnPatreon": {
"message": "Đăng ký trên Patreon"
},
"enterLicenseKey": {
"message": "Nhập mã bản quyền"
},
"hideUnsubmittedSegments": {
"message": "Ẩn các phân đoạn",
"description": "Show/hide button for the unsubmitted segments list"
} }
} }

View File

@@ -911,9 +911,6 @@
"hideForever": { "hideForever": {
"message": "永久隱藏" "message": "永久隱藏"
}, },
"warningChatInfo": {
"message": "我們注意到你犯下了一些不帶有惡意的常見錯誤"
},
"warningTitle": { "warningTitle": {
"message": "你收到了一支警告" "message": "你收到了一支警告"
}, },

View File

@@ -585,6 +585,7 @@ input::-webkit-inner-spin-button {
font-size: 14px; font-size: 14px;
-moz-appearance: textfield; -moz-appearance: textfield;
appearance: textfield;
} }
.sponsorTimeEditInput { .sponsorTimeEditInput {

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

@@ -3,7 +3,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
} }
/* cyrillic */ /* cyrillic */
@@ -11,7 +11,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
} }
/* greek-ext */ /* greek-ext */
@@ -19,7 +19,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2) format('woff2');
unicode-range: U+1F00-1FFF; unicode-range: U+1F00-1FFF;
} }
/* greek */ /* greek */
@@ -27,7 +27,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2) format('woff2');
unicode-range: U+0370-03FF; unicode-range: U+0370-03FF;
} }
/* vietnamese */ /* vietnamese */
@@ -35,7 +35,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} }
/* latin-ext */ /* latin-ext */
@@ -43,7 +43,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
} }
/* latin */ /* latin */
@@ -51,7 +51,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2'); src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
} }
/* cyrillic-ext */ /* cyrillic-ext */
@@ -59,7 +59,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
} }
/* cyrillic */ /* cyrillic */
@@ -67,7 +67,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
} }
/* greek-ext */ /* greek-ext */
@@ -75,7 +75,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2');
unicode-range: U+1F00-1FFF; unicode-range: U+1F00-1FFF;
} }
/* greek */ /* greek */
@@ -83,7 +83,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2');
unicode-range: U+0370-03FF; unicode-range: U+0370-03FF;
} }
/* vietnamese */ /* vietnamese */
@@ -91,7 +91,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
} }
/* latin-ext */ /* latin-ext */
@@ -99,7 +99,7 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
} }
/* latin */ /* latin */
@@ -107,6 +107,6 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2'); src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
} }

View File

@@ -453,7 +453,7 @@
</label> </label>
</div> </div>
<div class="small-description">(__MSG_supportedSites__ Invidious, CloudTube)</div> <div class="small-description">(__MSG_supportedSites__ Invidious, CloudTube, Piped)</div>
<div class="small-description">__MSG_supportOtherSitesDescription__ </div> <div class="small-description">__MSG_supportOtherSitesDescription__ </div>
</div> </div>

View File

@@ -1,3 +1,174 @@
@ajayyy/maze-utils
1.0.3 <https://github.com/ajayyy/SponsorBlock>
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
******************************
js-tokens js-tokens
4.0.0 <https://github.com/lydell/js-tokens> 4.0.0 <https://github.com/lydell/js-tokens>
The MIT License (MIT) The MIT License (MIT)

View File

@@ -6,10 +6,9 @@ import Utils from "../utils";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent"; import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
import { RectangleTooltip } from "../render/RectangleTooltip"; import { RectangleTooltip } from "../render/RectangleTooltip";
import SelectorComponent, { SelectorOption } from "./SelectorComponent"; import SelectorComponent, { SelectorOption } from "./SelectorComponent";
import { GenericUtils } from "../utils/genericUtils";
import { noRefreshFetchingChaptersAllowed } from "../utils/licenseKey"; import { noRefreshFetchingChaptersAllowed } from "../utils/licenseKey";
import { DEFAULT_CATEGORY } from "../utils/categoryUtils"; import { DEFAULT_CATEGORY } from "../utils/categoryUtils";
import { getFormattedTime, getFormattedTimeToSeconds } from "@ajayyy/maze-utils/lib/formating";
const utils = new Utils(); const utils = new Utils();
@@ -181,9 +180,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
style={timeDisplayStyle} style={timeDisplayStyle}
className="sponsorTimeDisplay" className="sponsorTimeDisplay"
onClick={this.toggleEditTime.bind(this)}> onClick={this.toggleEditTime.bind(this)}>
{GenericUtils.getFormattedTime(segment[0], true) + {getFormattedTime(segment[0], true) +
((!isNaN(segment[1]) && sponsorTime.actionType !== ActionType.Poi) ((!isNaN(segment[1]) && sponsorTime.actionType !== ActionType.Poi)
? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(segment[1], true) : "")} ? " " + chrome.i18n.getMessage("to") + " " + getFormattedTime(segment[1], true) : "")}
</div> </div>
); );
} }
@@ -308,8 +307,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
const sponsorTimeEdits = this.state.sponsorTimeEdits; const sponsorTimeEdits = this.state.sponsorTimeEdits;
// check if change is small engough to show tooltip // check if change is small engough to show tooltip
const before = GenericUtils.getFormattedTimeToSeconds(sponsorTimeEdits[index]); const before = getFormattedTimeToSeconds(sponsorTimeEdits[index]);
const after = GenericUtils.getFormattedTimeToSeconds(targetValue); const after = getFormattedTimeToSeconds(targetValue);
const difference = Math.abs(before - after); const difference = Math.abs(before - after);
if (0 < difference && difference < 0.5) this.showScrollToEditToolTip(); if (0 < difference && difference < 0.5) this.showScrollToEditToolTip();
@@ -333,7 +332,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
const sponsorTimeEdits = this.state.sponsorTimeEdits; const sponsorTimeEdits = this.state.sponsorTimeEdits;
let timeAsNumber = GenericUtils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[index]); let timeAsNumber = getFormattedTimeToSeconds(this.state.sponsorTimeEdits[index]);
if (timeAsNumber !== null && e.deltaY != 0) { if (timeAsNumber !== null && e.deltaY != 0) {
if (e.deltaY < 0) { if (e.deltaY < 0) {
timeAsNumber += step; timeAsNumber += step;
@@ -343,7 +342,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
timeAsNumber = 0; timeAsNumber = 0;
} }
sponsorTimeEdits[index] = GenericUtils.getFormattedTime(timeAsNumber, true); sponsorTimeEdits[index] = getFormattedTime(timeAsNumber, true);
if (sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = sponsorTimeEdits[0]; if (sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = sponsorTimeEdits[0];
this.setState({sponsorTimeEdits}); this.setState({sponsorTimeEdits});
@@ -575,8 +574,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
/** Returns an array in the sponsorTimeEdits form (formatted time string) from a normal seconds sponsor time */ /** Returns an array in the sponsorTimeEdits form (formatted time string) from a normal seconds sponsor time */
getFormattedSponsorTimesEdits(sponsorTime: SponsorTime): [string, string] { getFormattedSponsorTimesEdits(sponsorTime: SponsorTime): [string, string] {
return [GenericUtils.getFormattedTime(sponsorTime.segment[0], true), return [getFormattedTime(sponsorTime.segment[0], true),
GenericUtils.getFormattedTime(sponsorTime.segment[1], true)]; getFormattedTime(sponsorTime.segment[1], true)];
} }
saveEditTimes(): void { saveEditTimes(): void {
@@ -584,8 +583,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
const category = this.categoryOptionRef.current.value as Category const category = this.categoryOptionRef.current.value as Category
if (this.state.editing) { if (this.state.editing) {
const startTime = GenericUtils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]); const startTime = getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]);
const endTime = GenericUtils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1]); const endTime = getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1]);
// Change segment time only if the format was correct // Change segment time only if the format was correct
if (startTime !== null && endTime !== null) { if (startTime !== null && endTime !== null) {

View File

@@ -124,7 +124,7 @@ class KeybindDialogComponent extends React.Component<KeybindDialogProps, Keybind
let youtubeShortcuts: Keybind[]; let youtubeShortcuts: Keybind[];
if (/[a-zA-Z0-9,.+\-\][:]/.test(this.state.key.key)) { if (/[a-zA-Z0-9,.+\-\][:]/.test(this.state.key.key)) {
youtubeShortcuts = [{key: "k"}, {key: "j"}, {key: "l"}, {key: "p", shift: true}, {key: "n", shift: true}, {key: ","}, {key: "."}, {key: ",", shift: true}, {key: ".", shift: true}, youtubeShortcuts = [{key: "k"}, {key: "j"}, {key: "l"}, {key: "p", shift: true}, {key: "n", shift: true}, {key: ","}, {key: "."}, {key: ",", shift: true}, {key: ".", shift: true},
{key: "ArrowRight"}, {key: "ArrowLeft"}, {key: "ArrowUp"}, {key: "ArrowDown"}, {key: "ArrowRight", ctrl: true}, {key: "ArrowLeft", ctrl: true}, {key: "c"}, {key: "o"}, {key: "ArrowRight"}, {key: "ArrowLeft"}, {key: "ArrowUp"}, {key: "ArrowDown"}, {key: "c"}, {key: "o"},
{key: "w"}, {key: "+"}, {key: "-"}, {key: "f"}, {key: "t"}, {key: "i"}, {key: "m"}, {key: "a"}, {key: "s"}, {key: "d"}, {key: "Home"}, {key: "End"}, {key: "w"}, {key: "+"}, {key: "-"}, {key: "f"}, {key: "t"}, {key: "i"}, {key: "m"}, {key: "a"}, {key: "s"}, {key: "d"}, {key: "Home"}, {key: "End"},
{key: "0"}, {key: "1"}, {key: "2"}, {key: "3"}, {key: "4"}, {key: "5"}, {key: "6"}, {key: "7"}, {key: "8"}, {key: "9"}, {key: "]"}, {key: "["}]; {key: "0"}, {key: "1"}, {key: "2"}, {key: "3"}, {key: "4"}, {key: "5"}, {key: "6"}, {key: "7"}, {key: "8"}, {key: "9"}, {key: "]"}, {key: "["}];
} else { } else {

View File

@@ -1,7 +1,7 @@
import * as CompileConfig from "../config.json"; import * as CompileConfig from "../config.json";
import * as invidiousList from "../ci/invidiouslist.json"; import * as invidiousList from "../ci/invidiouslist.json";
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, Keybind, HashedValue, VideoID, SponsorHideType } from "./types"; import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, Keybind, HashedValue, VideoID, SponsorHideType } from "./types";
import { keybindEquals } from "./utils/configUtils"; import { isSafari, keybindEquals } from "./utils/configUtils";
export interface Permission { export interface Permission {
canSubmit: boolean; canSubmit: boolean;
@@ -180,7 +180,7 @@ const Config: SBObject = {
hideDiscordLaunches: 0, hideDiscordLaunches: 0,
hideDiscordLink: false, hideDiscordLink: false,
invidiousInstances: ["invidious.snopyta.org"], // leave as default invidiousInstances: ["invidious.snopyta.org"], // leave as default
supportInvidious: false, supportInvidious: isSafari(),
serverAddress: CompileConfig.serverAddress, serverAddress: CompileConfig.serverAddress,
minDuration: 0, minDuration: 0,
skipNoticeDuration: 4, skipNoticeDuration: 4,
@@ -219,8 +219,8 @@ const Config: SBObject = {
skipKeybind: {key: "Enter"}, skipKeybind: {key: "Enter"},
startSponsorKeybind: {key: ";"}, startSponsorKeybind: {key: ";"},
submitKeybind: {key: "'"}, submitKeybind: {key: "'"},
nextChapterKeybind: {key: "]", ctrl: true}, nextChapterKeybind: {key: "ArrowRight", ctrl: true},
previousChapterKeybind: {key: "[", ctrl: true}, previousChapterKeybind: {key: "ArrowLeft", ctrl: true},
categorySelections: [{ categorySelections: [{
name: "sponsor" as Category, name: "sponsor" as Category,
@@ -550,7 +550,8 @@ function migrateOldSyncFormats(config: SBConfig) {
} }
// populate invidiousInstances with new instances if 3p support is **DISABLED** // populate invidiousInstances with new instances if 3p support is **DISABLED**
if (!config["supportInvidious"] && config["invidiousInstances"].length !== invidiousList.length) { // for safari, update it immediately
if ((isSafari() || !config["supportInvidious"]) && config["invidiousInstances"].length !== invidiousList.length) {
config["invidiousInstances"] = invidiousList; config["invidiousInstances"] = invidiousList;
} }

View File

@@ -39,6 +39,8 @@ import { ChapterVote } from "./render/ChapterVote";
import { openWarningDialog } from "./utils/warnings"; import { openWarningDialog } from "./utils/warnings";
import { Tooltip } from "./render/Tooltip"; import { Tooltip } from "./render/Tooltip";
import { noRefreshFetchingChaptersAllowed } from "./utils/licenseKey"; import { noRefreshFetchingChaptersAllowed } from "./utils/licenseKey";
import { waitFor } from "@ajayyy/maze-utils";
import { getFormattedTime } from "@ajayyy/maze-utils/lib/formating";
const utils = new Utils(); const utils = new Utils();
@@ -311,7 +313,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
break; break;
} }
case "keydown": case "keydown":
document.dispatchEvent(new KeyboardEvent('keydown', { (document.body || document).dispatchEvent(new KeyboardEvent('keydown', {
key: request.key, key: request.key,
keyCode: request.keyCode, keyCode: request.keyCode,
code: request.code, code: request.code,
@@ -524,6 +526,9 @@ function createPreviewBar(): void {
// For Youtube Music // For Youtube Music
// there are two sliders, one for volume and one for progress - both called #progressContainer // there are two sliders, one for volume and one for progress - both called #progressContainer
selector: "#progress-bar>#sliderContainer>div>#sliderBar>#progressContainer", selector: "#progress-bar>#sliderContainer>div>#sliderBar>#progressContainer",
}, {
// For piped
selector: ".shaka-ad-markers",
isVisibleCheck: false isVisibleCheck: false
} }
]; ];
@@ -964,25 +969,23 @@ function updateVirtualTime() {
if (utils.isFirefox()) { if (utils.isFirefox()) {
let count = 0; let count = 0;
let lastTime = lastKnownVideoTime.videoTime; let lastTime = lastKnownVideoTime.videoTime;
if (lastKnownVideoTime.fromPause) { currentVirtualTimeInterval = setInterval(() => {
currentVirtualTimeInterval = setInterval(() => { if (lastTime !== video.currentTime) {
if (lastTime !== video.currentTime) { count++;
count++; lastTime = video.currentTime;
lastTime = video.currentTime; }
}
if (count > 1) { if (count > 1) {
const delay = lastKnownVideoTime.fromPause && lastKnownVideoTime.approximateDelay ? const delay = lastKnownVideoTime.fromPause && lastKnownVideoTime.approximateDelay ?
lastKnownVideoTime.approximateDelay : 0; lastKnownVideoTime.approximateDelay : 0;
lastKnownVideoTime.videoTime = video.currentTime + delay; lastKnownVideoTime.videoTime = video.currentTime + delay;
lastKnownVideoTime.preciseTime = performance.now(); lastKnownVideoTime.preciseTime = performance.now();
clearInterval(currentVirtualTimeInterval); clearInterval(currentVirtualTimeInterval);
currentVirtualTimeInterval = null; currentVirtualTimeInterval = null;
} }
}, 1); }, 1);
}
} }
} }
@@ -1089,98 +1092,88 @@ async function sponsorsLookup(keepOldSubmissions = true) {
source: SponsorSourceType.Server source: SponsorSourceType.Server
})) }))
?.sort((a, b) => a.segment[0] - b.segment[0]); ?.sort((a, b) => a.segment[0] - b.segment[0]);
if (!recievedSegments || !recievedSegments.length) { if (recievedSegments && recievedSegments.length) {
// return if no video found if (showChapterMessage) {
chrome.runtime.sendMessage({ const chapterSegments = recievedSegments.filter((s) => s.actionType === ActionType.Chapter);
message: "infoUpdated", if (chapterSegments.length > 3) {
found: false, const prependElement = document.querySelector(".ytp-chrome-bottom") as HTMLElement;
status: lastResponseStatus, if (prependElement) {
sponsorTimes: sponsorTimes, Config.config.showChapterInfoMessage = false;
time: video.currentTime, new Tooltip({
onMobileYouTube text: `🟨${chrome.i18n.getMessage("chapterNewFeature")}${chapterSegments.slice(0, 3).map((s) => s.description).join(", ")}`,
}); linkOnClick: () => void chrome.runtime.sendMessage({ "message": "openUpsell" }),
referenceNode: prependElement.parentElement,
prependElement,
timeout: 1500,
leftOffset: "20px",
positionRealtive: false
});
}
}
recievedSegments = recievedSegments.filter((s) => s.actionType !== ActionType.Chapter);
}
sponsorDataFound = true;
// Check if any old submissions should be kept
if (sponsorTimes !== null && keepOldSubmissions) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].source === SponsorSourceType.Local) {
// This is a user submission, keep it
recievedSegments.push(sponsorTimes[i]);
}
}
}
const oldSegments = sponsorTimes || [];
sponsorTimes = recievedSegments;
existingChaptersImported = false;
// Hide all submissions smaller than the minimum duration
if (Config.config.minDuration !== 0) {
for (const segment of sponsorTimes) {
const duration = segment.segment[1] - segment.segment[0];
if (duration > 0 && duration < Config.config.minDuration) {
segment.hidden = SponsorHideType.MinimumDuration;
}
}
}
if (keepOldSubmissions) {
for (const segment of oldSegments) {
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
if (otherSegment) {
// If they downvoted it, or changed the category, keep it
otherSegment.hidden = segment.hidden;
otherSegment.category = segment.category;
}
}
}
// See if some segments should be hidden
const downvotedData = Config.local.downvotedSegments[hashPrefix];
if (downvotedData) {
for (const segment of sponsorTimes) {
const hashedUUID = await utils.getHash(segment.UUID, 1);
const segmentDownvoteData = downvotedData.segments.find((downvote) => downvote.uuid === hashedUUID);
if (segmentDownvoteData) {
segment.hidden = segmentDownvoteData.hidden;
}
}
}
startSkipScheduleCheckingForStartSponsors();
//update the preview bar
//leave the type blank for now until categories are added
if (lastPreviewBarUpdate == sponsorVideoID || (lastPreviewBarUpdate == null && !isNaN(video.duration))) {
//set it now
//otherwise the listener can handle it
updatePreviewBar();
}
} else {
retryFetch(404); retryFetch(404);
return;
}
if (showChapterMessage) {
const chapterSegments = recievedSegments.filter((s) => s.actionType === ActionType.Chapter);
if (chapterSegments.length > 3) {
const prependElement = document.querySelector(".ytp-chrome-bottom") as HTMLElement;
if (prependElement) {
Config.config.showChapterInfoMessage = false;
new Tooltip({
text: `🟨${chrome.i18n.getMessage("chapterNewFeature")}${chapterSegments.slice(0, 3).map((s) => s.description).join(", ")}`,
linkOnClick: () => void chrome.runtime.sendMessage({ "message": "openUpsell" }),
referenceNode: prependElement.parentElement,
prependElement,
timeout: 1500,
leftOffset: "20px",
positionRealtive: false
});
}
}
recievedSegments = recievedSegments.filter((s) => s.actionType !== ActionType.Chapter);
}
sponsorDataFound = true;
// Check if any old submissions should be kept
if (sponsorTimes !== null && keepOldSubmissions) {
for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].source === SponsorSourceType.Local) {
// This is a user submission, keep it
recievedSegments.push(sponsorTimes[i]);
}
}
}
const oldSegments = sponsorTimes || [];
sponsorTimes = recievedSegments;
existingChaptersImported = false;
// Hide all submissions smaller than the minimum duration
if (Config.config.minDuration !== 0) {
for (const segment of sponsorTimes) {
const duration = segment.segment[1] - segment.segment[0];
if (duration > 0 && duration < Config.config.minDuration) {
segment.hidden = SponsorHideType.MinimumDuration;
}
}
}
if (keepOldSubmissions) {
for (const segment of oldSegments) {
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
if (otherSegment) {
// If they downvoted it, or changed the category, keep it
otherSegment.hidden = segment.hidden;
otherSegment.category = segment.category;
}
}
}
// See if some segments should be hidden
const downvotedData = Config.local.downvotedSegments[hashPrefix];
if (downvotedData) {
for (const segment of sponsorTimes) {
const hashedUUID = await utils.getHash(segment.UUID, 1);
const segmentDownvoteData = downvotedData.segments.find((downvote) => downvote.uuid === hashedUUID);
if (segmentDownvoteData) {
segment.hidden = segmentDownvoteData.hidden;
}
}
}
startSkipScheduleCheckingForStartSponsors();
//update the preview bar
//leave the type blank for now until categories are added
if (lastPreviewBarUpdate == sponsorVideoID || (lastPreviewBarUpdate == null && !isNaN(video.duration))) {
//set it now
//otherwise the listener can handle it
updatePreviewBar();
} }
} else { } else {
retryFetch(lastResponseStatus); retryFetch(lastResponseStatus);
@@ -1205,8 +1198,8 @@ async function sponsorsLookup(keepOldSubmissions = true) {
function importExistingChapters(wait: boolean) { function importExistingChapters(wait: boolean) {
if (!existingChaptersImported) { if (!existingChaptersImported) {
GenericUtils.wait(() => video?.duration && getExistingChapters(sponsorVideoID, video.duration), waitFor(() => video?.duration && getExistingChapters(sponsorVideoID, video.duration),
wait ? 5000 : 0, 100, (c) => c?.length > 0).then((chapters) => { wait ? 15000 : 0, 400, (c) => c?.length > 0).then((chapters) => {
if (!existingChaptersImported && chapters?.length > 0) { if (!existingChaptersImported && chapters?.length > 0) {
sponsorTimes = (sponsorTimes ?? []).concat(...chapters).sort((a, b) => a.segment[0] - b.segment[0]); sponsorTimes = (sponsorTimes ?? []).concat(...chapters).sort((a, b) => a.segment[0] - b.segment[0]);
existingChaptersImported = true; existingChaptersImported = true;
@@ -1421,6 +1414,7 @@ function updatePreviewBar(): void {
if (video === null) return; if (video === null) return;
const hashParams = getHashParams(); const hashParams = getHashParams();
const requiredSegment = hashParams?.requiredSegment as SegmentUUID || undefined;
const previewBarSegments: PreviewBarSegment[] = []; const previewBarSegments: PreviewBarSegment[] = [];
if (sponsorTimes) { if (sponsorTimes) {
sponsorTimes.forEach((segment) => { sponsorTimes.forEach((segment) => {
@@ -1434,7 +1428,7 @@ function updatePreviewBar(): void {
showLarger: segment.actionType === ActionType.Poi, showLarger: segment.actionType === ActionType.Poi,
description: segment.description, description: segment.description,
source: segment.source, source: segment.source,
requiredSegment: hashParams.requiredSegment && segment.UUID === hashParams.requiredSegment requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID.startsWith(requiredSegment))
}); });
}); });
} }
@@ -2095,19 +2089,33 @@ function openInfoMenu() {
frame.src = chrome.extension.getURL("popup.html"); frame.src = chrome.extension.getURL("popup.html");
popup.appendChild(frame); popup.appendChild(frame);
const parentNodes = document.querySelectorAll("#secondary-inner"); const elemHasChild = (elements: NodeListOf<HTMLElement>): Element => {
let parentNode = null; let parentNode: Element;
for (let i = 0; i < parentNodes.length; i++) { for (const node of elements) {
if (parentNodes[i].firstElementChild !== null) { if (node.firstElementChild !== null) {
parentNode = parentNodes[i]; parentNode = node;
}
} }
} return parentNode
if (parentNode == null) {
//old youtube theme
parentNode = document.getElementById("watch7-sidebar-contents");
} }
parentNode.insertBefore(popup, parentNode.firstChild); const parentNodeOptions = [{
// YouTube
selector: "#secondary-inner",
hasChildCheck: true
}, {
// old youtube theme
selector: "#watch7-sidebar-contents",
}];
for (const option of parentNodeOptions) {
const allElements = document.querySelectorAll(option.selector) as NodeListOf<HTMLElement>;
const el = option.hasChildCheck ? elemHasChild(allElements) : allElements[0];
if (el) {
if (option.hasChildCheck) el.insertBefore(popup, el.firstChild);
break;
}
}
} }
function closeInfoMenu() { function closeInfoMenu() {
@@ -2366,7 +2374,7 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
for (let i = 0; i < sponsorTimes.length; i++) { for (let i = 0; i < sponsorTimes.length; i++) {
for (let s = 0; s < sponsorTimes[i].segment.length; s++) { for (let s = 0; s < sponsorTimes[i].segment.length; s++) {
let timeMessage = GenericUtils.getFormattedTime(sponsorTimes[i].segment[s]); let timeMessage = getFormattedTime(sponsorTimes[i].segment[s]);
//if this is an end time //if this is an end time
if (s == 1) { if (s == 1) {
timeMessage = " " + chrome.i18n.getMessage("to") + " " + timeMessage; timeMessage = " " + chrome.i18n.getMessage("to") + " " + timeMessage;
@@ -2483,11 +2491,23 @@ function addPageListeners(): void {
document.addEventListener("yt-navigate-start", resetValues); document.addEventListener("yt-navigate-start", resetValues);
document.addEventListener("yt-navigate-finish", refreshListners); document.addEventListener("yt-navigate-finish", refreshListners);
// piped player init
window.addEventListener("playerInit", () => {
if (!document.querySelector('meta[property="og:title"][content="Piped"]')) return
previewBar = null; // remove old previewbar
createPreviewBar()
});
window.addEventListener("message", windowListenerHandler); window.addEventListener("message", windowListenerHandler);
} }
function addHotkeyListener(): void { function addHotkeyListener(): void {
document.addEventListener("keydown", hotkeyListener); document.addEventListener("keydown", hotkeyListener);
document.addEventListener("DOMContentLoaded", () => {
// Allow us to stop propagation to YouTube by being deeper
document.removeEventListener("keydown", hotkeyListener);
document.body.addEventListener("keydown", hotkeyListener);
});
} }
function hotkeyListener(e: KeyboardEvent): void { function hotkeyListener(e: KeyboardEvent): void {
@@ -2519,9 +2539,11 @@ function hotkeyListener(e: KeyboardEvent): void {
submitSponsorTimes(); submitSponsorTimes();
return; return;
} else if (keybindEquals(key, nextChapterKey)) { } else if (keybindEquals(key, nextChapterKey)) {
if (sponsorTimes.length > 0) e.stopPropagation();
nextChapter(); nextChapter();
return; return;
} else if (keybindEquals(key, previousChapterKey)) { } else if (keybindEquals(key, previousChapterKey)) {
if (sponsorTimes.length > 0) e.stopPropagation();
previousChapter(); previousChapter();
return; return;
} }
@@ -2594,7 +2616,7 @@ function showTimeWithoutSkips(skippedDuration: number): void {
display.appendChild(duration); display.appendChild(duration);
} }
const durationAfterSkips = GenericUtils.getFormattedTime(video?.duration - skippedDuration); const durationAfterSkips = getFormattedTime(video?.duration - skippedDuration);
duration.innerText = (durationAfterSkips == null || skippedDuration <= 0) ? "" : " (" + durationAfterSkips + ")"; duration.innerText = (durationAfterSkips == null || skippedDuration <= 0) ? "" : " (" + durationAfterSkips + ")";
} }

View File

@@ -2,14 +2,14 @@ import Config from "./config";
import { showDonationLink } from "./utils/configUtils"; import { showDonationLink } from "./utils/configUtils";
import { localizeHtmlPage } from "./utils/pageUtils"; import { localizeHtmlPage } from "./utils/pageUtils";
import { GenericUtils } from "./utils/genericUtils"; import { waitFor } from "@ajayyy/maze-utils";
window.addEventListener('DOMContentLoaded', init); window.addEventListener('DOMContentLoaded', init);
async function init() { async function init() {
localizeHtmlPage(); localizeHtmlPage();
await GenericUtils.wait(() => Config.config !== null); await waitFor(() => Config.config !== null);
if (!Config.config.darkMode) { if (!Config.config.darkMode) {
document.documentElement.setAttribute("data-theme", "light"); document.documentElement.setAttribute("data-theme", "light");

View File

@@ -11,8 +11,8 @@ import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceT
import { partition } from "../utils/arrayUtils"; import { partition } from "../utils/arrayUtils";
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils"; import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
import { normalizeChapterName } from "../utils/exporter"; import { normalizeChapterName } from "../utils/exporter";
import { GenericUtils } from "../utils/genericUtils";
import { findValidElement } from "../utils/pageUtils"; import { findValidElement } from "../utils/pageUtils";
import { getFormattedTimeToSeconds } from "@ajayyy/maze-utils/lib/formating";
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible'; const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
const MIN_CHAPTER_SIZE = 0.003; const MIN_CHAPTER_SIZE = 0.003;
@@ -140,7 +140,7 @@ class PreviewBar {
const tooltipText = tooltipTextElement.textContent; const tooltipText = tooltipTextElement.textContent;
if (tooltipText === null || tooltipText.length === 0) continue; if (tooltipText === null || tooltipText.length === 0) continue;
timeInSeconds = GenericUtils.getFormattedTimeToSeconds(tooltipText); timeInSeconds = getFormattedTimeToSeconds(tooltipText);
if (timeInSeconds !== null) break; if (timeInSeconds !== null) break;
} }
@@ -314,7 +314,7 @@ class PreviewBar {
if (chapterChevron) { if (chapterChevron) {
if (this.segments.some((segment) => segment.source === SponsorSourceType.YouTube)) { if (this.segments.some((segment) => segment.source === SponsorSourceType.YouTube)) {
chapterChevron.style.removeProperty("display"); chapterChevron.style.removeProperty("display");
} else { } else if (this.segments) {
chapterChevron.style.display = "none"; chapterChevron.style.display = "none";
} }
} }
@@ -814,7 +814,16 @@ class PreviewBar {
chapterButton.disabled = false; chapterButton.disabled = false;
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement; const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
chapterTitle.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category); chapterTitle.innerText = "";
const chapterCustomText = (chapterTitle.querySelector(".sponsorChapterText") || (() => {
const elem = document.createElement("div");
chapterTitle.appendChild(elem);
elem.classList.add("sponsorChapterText");
return elem;
})()) as HTMLDivElement;
chapterCustomText.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
if (chosenSegment.actionType !== ActionType.Chapter) { if (chosenSegment.actionType !== ActionType.Chapter) {
chapterTitle.classList.add("sponsorBlock-segment-title"); chapterTitle.classList.add("sponsorBlock-segment-title");
} else { } else {
@@ -838,7 +847,13 @@ class PreviewBar {
this.chapterVote.setVisibility(false); this.chapterVote.setVisibility(false);
} }
} else { } else {
chaptersContainer.style.display = "none"; chaptersContainer.querySelector(".sponsorChapterText")?.remove();
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
if (chapterTitle.innerText === "") {
chaptersContainer.style.display = "none";
} else {
chaptersContainer.style.removeProperty("display");
}
this.chapterVote.setVisibility(false); this.chapterVote.setVisibility(false);
} }
} }

View File

@@ -27,6 +27,7 @@ import { localizeHtmlPage } from "./utils/pageUtils";
import { exportTimes } from "./utils/exporter"; import { exportTimes } from "./utils/exporter";
import GenericNotice from "./render/GenericNotice"; import GenericNotice from "./render/GenericNotice";
import { noRefreshFetchingChaptersAllowed } from "./utils/licenseKey"; import { noRefreshFetchingChaptersAllowed } from "./utils/licenseKey";
import { getFormattedTime } from "@ajayyy/maze-utils/lib/formating";
const utils = new Utils(); const utils = new Utils();
@@ -593,9 +594,9 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
if (downloadedTimes[i].actionType === ActionType.Full) { if (downloadedTimes[i].actionType === ActionType.Full) {
segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full"); segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full");
} else { } else {
segmentTimeFromToNode.innerText = GenericUtils.getFormattedTime(downloadedTimes[i].segment[0], true) + segmentTimeFromToNode.innerText = getFormattedTime(downloadedTimes[i].segment[0], true) +
(actionType !== ActionType.Poi (actionType !== ActionType.Poi
? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(downloadedTimes[i].segment[1], true) ? " " + chrome.i18n.getMessage("to") + " " + getFormattedTime(downloadedTimes[i].segment[1], true)
: ""); : "");
} }

View File

@@ -4,8 +4,9 @@ import CategoryPillComponent, { CategoryPillState } from "../components/Category
import Config from "../config"; import Config from "../config";
import { VoteResponse } from "../messageTypes"; import { VoteResponse } from "../messageTypes";
import { Category, SegmentUUID, SponsorTime } from "../types"; import { Category, SegmentUUID, SponsorTime } from "../types";
import { GenericUtils } from "../utils/genericUtils";
import { Tooltip } from "./Tooltip"; import { Tooltip } from "./Tooltip";
import { waitFor } from "@ajayyy/maze-utils";
import { getYouTubeTitleNode } from "@ajayyy/maze-utils/lib/elements";
export class CategoryPill { export class CategoryPill {
container: HTMLElement; container: HTMLElement;
@@ -23,44 +24,43 @@ export class CategoryPill {
async attachToPage(onMobileYouTube: boolean, onInvidious: boolean, async attachToPage(onMobileYouTube: boolean, onInvidious: boolean,
vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>): Promise<void> { vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise<VoteResponse>): Promise<void> {
const referenceNode = const referenceNode =
await GenericUtils.wait(() => await waitFor(() => getYouTubeTitleNode());
// New YouTube Title, YouTube, Mobile YouTube, Invidious
document.querySelector("#title h1, .ytd-video-primary-info-renderer.title, .slim-video-information-title, #player-container + .h-box > h1") as HTMLElement);
if (referenceNode && !referenceNode.contains(this.container)) { if (referenceNode && !referenceNode.contains(this.container)) {
this.container = document.createElement('span'); if (!this.container) {
this.container.id = "categoryPill"; this.container = document.createElement('span');
this.container.style.display = "relative"; this.container.id = "categoryPill";
this.container.style.display = "relative";
referenceNode.prepend(this.container); this.root = createRoot(this.container);
referenceNode.style.display = "flex"; this.root.render(<CategoryPillComponent ref={this.ref} vote={vote} />);
if (this.ref.current) { if (onMobileYouTube) {
this.unsavedState = this.ref.current.state; if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
this.mutationObserver = new MutationObserver((changes) => {
if (changes.some((change) => change.removedNodes.length > 0)) {
this.attachToPage(onMobileYouTube, onInvidious, vote);
}
});
this.mutationObserver.observe(referenceNode, {
childList: true,
subtree: true
});
}
} }
this.root = createRoot(this.container);
this.root.render(<CategoryPillComponent ref={this.ref} vote={vote} />);
if (this.unsavedState) { if (this.unsavedState) {
GenericUtils.wait(() => this.ref.current).then(() => { waitFor(() => this.ref.current).then(() => {
this.ref.current?.setState(this.unsavedState); this.ref.current?.setState(this.unsavedState);
this.unsavedState = null;
});
}
if (onMobileYouTube) {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
this.mutationObserver = new MutationObserver(() => this.attachToPage(onMobileYouTube, onInvidious, vote));
this.mutationObserver.observe(referenceNode, {
childList: true,
subtree: true
}); });
} }
referenceNode.prepend(this.container);
referenceNode.style.display = "flex";
} }
} }
@@ -99,7 +99,7 @@ export class CategoryPill {
if (!Config.config.categoryPillUpdate) { if (!Config.config.categoryPillUpdate) {
Config.config.categoryPillUpdate = true; Config.config.categoryPillUpdate = true;
const watchDiv = await GenericUtils.wait(() => document.querySelector("#info.ytd-watch-flexy") as HTMLElement); const watchDiv = await waitFor(() => document.querySelector("#info.ytd-watch-flexy") as HTMLElement);
if (watchDiv) { if (watchDiv) {
new Tooltip({ new Tooltip({
text: chrome.i18n.getMessage("categoryPillNewFeature"), text: chrome.i18n.getMessage("categoryPillNewFeature"),

View File

@@ -3,7 +3,8 @@ import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContaine
import * as CompileConfig from "../config.json"; import * as CompileConfig from "../config.json";
import { findValidElement, findValidElementFromSelector } from "./utils/pageUtils"; import { findValidElement, findValidElementFromSelector } from "./utils/pageUtils";
import { GenericUtils } from "./utils/genericUtils"; import { waitFor } from "@ajayyy/maze-utils";
import { isSafari } from "./utils/configUtils";
export default class Utils { export default class Utils {
@@ -18,7 +19,8 @@ export default class Utils {
css = [ css = [
"content.css", "content.css",
"./libs/Source+Sans+Pro.css", "./libs/Source+Sans+Pro.css",
"popup.css" "popup.css",
"shared.css"
]; ];
/* Used for waitForElement */ /* Used for waitForElement */
@@ -31,7 +33,7 @@ export default class Utils {
} }
async wait<T>(condition: () => T, timeout = 5000, check = 100): Promise<T> { async wait<T>(condition: () => T, timeout = 5000, check = 100): Promise<T> {
return GenericUtils.wait(condition, timeout, check); return waitFor(condition, timeout, check);
} }
/* Uses a mutation observer to wait asynchronously */ /* Uses a mutation observer to wait asynchronously */
@@ -235,6 +237,8 @@ export default class Utils {
} }
containsInvidiousPermission(): Promise<boolean> { containsInvidiousPermission(): Promise<boolean> {
if (isSafari()) return Promise.resolve(true);
return new Promise((resolve) => { return new Promise((resolve) => {
let permissions = ["declarativeContent"]; let permissions = ["declarativeContent"];
if (this.isFirefox()) permissions = []; if (this.isFirefox()) permissions = [];
@@ -401,14 +405,15 @@ export default class Utils {
"#player-container", // Preview on hover "#player-container", // Preview on hover
"#main-panel.ytmusic-player-page", // YouTube music "#main-panel.ytmusic-player-page", // YouTube music
"#player-container .video-js", // Invidious "#player-container .video-js", // Invidious
".main-video-section > .video-container" // Cloudtube ".main-video-section > .video-container", // Cloudtube
".shaka-video-container" // Piped
]; ];
let referenceNode = findValidElementFromSelector(selectors) let referenceNode = findValidElementFromSelector(selectors)
if (referenceNode == null) { if (referenceNode == null) {
//for embeds //for embeds
const player = document.getElementById("player"); const player = document.getElementById("player");
referenceNode = player.firstChild as HTMLElement; referenceNode = player?.firstChild as HTMLElement;
if (referenceNode) { if (referenceNode) {
let index = 1; let index = 1;

View File

@@ -6,7 +6,7 @@ export function showDonationLink(): boolean {
} }
export function isSafari(): boolean { export function isSafari(): boolean {
return navigator.vendor === "Apple Computer, Inc."; return typeof(navigator) !== "undefined" && navigator.vendor === "Apple Computer, Inc.";
} }
export function keybindEquals(first: Keybind, second: Keybind): boolean { export function keybindEquals(first: Keybind, second: Keybind): boolean {

View File

@@ -2,6 +2,7 @@ import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } fro
import { shortCategoryName } from "./categoryUtils"; import { shortCategoryName } from "./categoryUtils";
import { GenericUtils } from "./genericUtils"; import { GenericUtils } from "./genericUtils";
import * as CompileConfig from "../../config.json"; import * as CompileConfig from "../../config.json";
import { getFormattedTime, getFormattedTimeToSeconds } from "@ajayyy/maze-utils/lib/formating";
const inTest = typeof chrome === "undefined"; const inTest = typeof chrome === "undefined";
@@ -26,9 +27,9 @@ export function exportTimes(segments: SponsorTime[]): string {
function exportTime(segment: SponsorTime): string { function exportTime(segment: SponsorTime): string {
const name = segment.description || shortCategoryName(segment.category); const name = segment.description || shortCategoryName(segment.category);
return `${GenericUtils.getFormattedTime(segment.segment[0], true)}${ return `${getFormattedTime(segment.segment[0], true)}${
segment.segment[1] && segment.segment[0] !== segment.segment[1] segment.segment[1] && segment.segment[0] !== segment.segment[1]
? ` - ${GenericUtils.getFormattedTime(segment.segment[1], true)}` : ""} ${name}`; ? ` - ${getFormattedTime(segment.segment[1], true)}` : ""} ${name}`;
} }
export function importTimes(data: string, videoDuration: number): SponsorTime[] { export function importTimes(data: string, videoDuration: number): SponsorTime[] {
@@ -37,20 +38,26 @@ export function importTimes(data: string, videoDuration: number): SponsorTime[]
for (const line of lines) { for (const line of lines) {
const match = line.match(/(?:((?:\d+:)?\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g); const match = line.match(/(?:((?:\d+:)?\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g);
if (match) { if (match) {
const startTime = GenericUtils.getFormattedTimeToSeconds(match[0]); const startTime = getFormattedTimeToSeconds(match[0]);
if (startTime !== null) { if (startTime !== null) {
const specialCharsMatcher = /^(?:\s+seconds?)?[-:()\s]*|(?:\s+at)?[-:()\s]+$/g // Remove "seconds", "at", special characters, and ")" if there was a "("
const titleLeft = line.split(match[0])[0].replace(specialCharsMatcher, ""); const specialCharMatchers = [{
matcher: /^(?:\s+seconds?)?[-:()\s]*|(?:\s+at)?[-:(\s]+$/g
}, {
matcher: /[-:()\s]*$/g,
condition: (value) => !!value.match(/^\s*\(/)
}];
const titleLeft = removeIf(line.split(match[0])[0], specialCharMatchers);
let titleRight = null; let titleRight = null;
const split2 = line.split(match[1] || match[0]); const split2 = line.split(match[1] || match[0]);
titleRight = split2[split2.length - 1].replace(specialCharsMatcher, ""); titleRight = removeIf(split2[split2.length - 1], specialCharMatchers)
const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight; const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight;
if (title) { if (title) {
const determinedCategory = chapterNames.find(c => c.names.includes(title))?.code as Category; const determinedCategory = chapterNames.find(c => c.names.includes(title))?.code as Category;
const segment: SponsorTime = { const segment: SponsorTime = {
segment: [startTime, GenericUtils.getFormattedTimeToSeconds(match[1])], segment: [startTime, getFormattedTimeToSeconds(match[1])],
category: determinedCategory ?? ("chapter" as Category), category: determinedCategory ?? ("chapter" as Category),
actionType: determinedCategory ? ActionType.Skip : ActionType.Chapter, actionType: determinedCategory ? ActionType.Skip : ActionType.Chapter,
description: title, description: title,
@@ -75,6 +82,17 @@ export function importTimes(data: string, videoDuration: number): SponsorTime[]
return result; return result;
} }
function removeIf(value: string, matchers: Array<{ matcher: RegExp; condition?: (value: string) => boolean }>): string {
let result = value;
for (const matcher of matchers) {
if (!matcher.condition || matcher.condition(value)) {
result = result.replace(matcher.matcher, "");
}
}
return result;
}
export function exportTimesAsHashParam(segments: SponsorTime[]): string { export function exportTimesAsHashParam(segments: SponsorTime[]): string {
const hashparamSegments = segments.map(segment => ({ const hashparamSegments = segments.map(segment => ({
actionType: segment.actionType, actionType: segment.actionType,

View File

@@ -1,70 +1,3 @@
/** Function that can be used to wait for a condition before returning. */
async function wait<T>(condition: () => T, timeout = 5000, check = 100, predicate?: (obj: T) => boolean): Promise<T> {
return await new Promise((resolve, reject) => {
setTimeout(() => {
clearInterval(interval);
reject("TIMEOUT");
}, timeout);
const intervalCheck = () => {
const result = condition();
if (predicate ? predicate(result) : result) {
resolve(result);
clearInterval(interval);
}
};
const interval = setInterval(intervalCheck, check);
//run the check once first, this speeds it up a lot
intervalCheck();
});
}
function getFormattedTimeToSeconds(formatted: string): number | null {
const fragments = /^(?:(?:(\d+):)?(\d+):)?(\d*(?:[.,]\d+)?)$/.exec(formatted);
if (fragments === null) {
return null;
}
const hours = fragments[1] ? parseInt(fragments[1]) : 0;
const minutes = fragments[2] ? parseInt(fragments[2] || '0') : 0;
const seconds = fragments[3] ? parseFloat(fragments[3].replace(',', '.')) : 0;
return hours * 3600 + minutes * 60 + seconds;
}
function getFormattedTime(seconds: number, precise?: boolean): string {
seconds = Math.max(seconds, 0);
const hours = Math.floor(seconds / 60 / 60);
const minutes = Math.floor(seconds / 60) % 60;
let minutesDisplay = String(minutes);
let secondsNum = seconds % 60;
if (!precise) {
secondsNum = Math.floor(secondsNum);
}
let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
if (secondsNum < 10) {
//add a zero
secondsDisplay = "0" + secondsDisplay;
}
if (hours && minutes < 10) {
//add a zero
minutesDisplay = "0" + minutesDisplay;
}
if (isNaN(hours) || isNaN(minutes)) {
return null;
}
const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
return formatted;
}
/** /**
* Gets the error message in a nice string * Gets the error message in a nice string
* *
@@ -148,9 +81,6 @@ function generateUserID(length = 36): string {
} }
export const GenericUtils = { export const GenericUtils = {
wait,
getFormattedTime,
getFormattedTimeToSeconds,
getErrorMessage, getErrorMessage,
getLuminance, getLuminance,
generateUserID, generateUserID,

View File

@@ -1,5 +1,5 @@
import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types"; import { ActionType, Category, SponsorSourceType, SponsorTime, VideoID } from "../types";
import { GenericUtils } from "./genericUtils"; import { getFormattedTimeToSeconds } from "@ajayyy/maze-utils/lib/formating";
export function getControls(): HTMLElement { export function getControls(): HTMLElement {
const controlsSelectors = [ const controlsSelectors = [
@@ -9,6 +9,8 @@ export function getControls(): HTMLElement {
".player-controls-top", ".player-controls-top",
// Invidious/videojs video element's controls element // Invidious/videojs video element's controls element
".vjs-control-bar", ".vjs-control-bar",
// Piped shaka player
".shaka-bottom-controls"
]; ];
for (const controlsSelector of controlsSelectors) { for (const controlsSelector of controlsSelectors) {
@@ -80,7 +82,7 @@ export function getExistingChapters(currentVideoID: VideoID, duration: number):
const timeElement = link.querySelector("#time") as HTMLElement; const timeElement = link.querySelector("#time") as HTMLElement;
const description = link.querySelector("#details h4") as HTMLElement; const description = link.querySelector("#details h4") as HTMLElement;
if (timeElement && description?.innerText?.length > 0 && link.getAttribute("href")?.includes(currentVideoID)) { if (timeElement && description?.innerText?.length > 0 && link.getAttribute("href")?.includes(currentVideoID)) {
const time = GenericUtils.getFormattedTimeToSeconds(timeElement.innerText); const time = getFormattedTimeToSeconds(timeElement.innerText);
if (time === null) return []; if (time === null) return [];
if (lastSegment) { if (lastSegment) {

View File

@@ -275,4 +275,36 @@ describe("Import segments", () => {
category: "chapter" as Category category: "chapter" as Category
}]); }]);
}); });
it ("0:00 G¹ (Tangent Continuity)", () => {
const input = ` 0:00 G¹ (Tangent Continuity)
0:01 G² (Tangent Continuity)`;
const result = importTimes(input, 8000);
expect(result).toMatchObject([{
segment: [0, 1],
description: "G¹ (Tangent Continuity)",
category: "chapter" as Category
}, {
segment: [1, 8000],
description: "G² (Tangent Continuity)",
category: "chapter" as Category
}]);
});
it ("((Some name) 1:20)", () => {
const input = ` ((Some name) 1:20)
((Some other name) 1:25)`;
const result = importTimes(input, 8000);
expect(result).toMatchObject([{
segment: [80, 85],
description: "Some name",
category: "chapter" as Category
}, {
segment: [85, 8000],
description: "Some other name",
category: "chapter" as Category
}]);
});
}); });

View File

@@ -3,6 +3,7 @@
const webpack = require("webpack"); const webpack = require("webpack");
const path = require('path'); const path = require('path');
const { validate } = require('schema-utils'); const { validate } = require('schema-utils');
const invidiousList = require("../ci/invidiouslist.json");
const fs = require('fs'); const fs = require('fs');
@@ -49,6 +50,11 @@ class BuildManifest {
mergeObjects(manifest, chromeManifestExtra); mergeObjects(manifest, chromeManifestExtra);
} else if (this.options.browser.toLowerCase() === "safari") { } else if (this.options.browser.toLowerCase() === "safari") {
mergeObjects(manifest, safariManifestExtra); mergeObjects(manifest, safariManifestExtra);
// Add all invidious instances as options for the content script
for (const instance of invidiousList) {
manifest.content_scripts[0].matches.push(`*://*.${instance}/*`);
}
} }
if (this.options.stream === "beta") { if (this.options.stream === "beta") {