mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 11:37:02 +03:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf55b0f233 | ||
|
|
df1d3b401c | ||
|
|
4d55a71619 | ||
|
|
29e6ebab29 | ||
|
|
97e80c6f4e | ||
|
|
3ac730c053 | ||
|
|
d25951f313 | ||
|
|
886e134010 | ||
|
|
cde50b0cb5 | ||
|
|
19ac01a17c | ||
|
|
e9e53d1d43 | ||
|
|
07ab99a8d0 | ||
|
|
896120d311 | ||
|
|
3dc6563048 | ||
|
|
2d74ce2093 | ||
|
|
eac4ca4d71 | ||
|
|
399a5af990 | ||
|
|
48861439b7 | ||
|
|
bd60875c66 | ||
|
|
fe8f25fe23 | ||
|
|
87f3cf3881 | ||
|
|
0b9df8a45c | ||
|
|
59c5b7eefe | ||
|
|
dea9dac20a | ||
|
|
684cd676e5 | ||
|
|
929e3396cd | ||
|
|
fad3284c4f | ||
|
|
c69047c7f9 | ||
|
|
4fc7a69dd5 | ||
|
|
dd4c903c6a | ||
|
|
c9a2edaf3d | ||
|
|
8d41af073d | ||
|
|
bfafcd07cc | ||
|
|
77abc1d031 | ||
|
|
809de0e0fd | ||
|
|
e55d1f5115 | ||
|
|
037bd511b0 | ||
|
|
1265eeb941 | ||
|
|
b3a94142c3 | ||
|
|
367657e44e | ||
|
|
955ee32b46 | ||
|
|
cd0b1f4a31 | ||
|
|
5c4f0c960c | ||
|
|
5ac6dd1a7f | ||
|
|
8d53e776b8 | ||
|
|
ec2950786f | ||
|
|
f33fa2f621 | ||
|
|
fd77748b15 | ||
|
|
3b59389cab | ||
|
|
e6f53a3ef9 | ||
|
|
b2f1a737f5 | ||
|
|
e2c7f4d16f | ||
|
|
4a89dfaac5 | ||
|
|
e47330a79c | ||
|
|
72fc3620bc | ||
|
|
0ebd7f4f8d | ||
|
|
beea8181a1 | ||
|
|
f0716e8bbb | ||
|
|
783ea5cf5b | ||
|
|
42a813d325 | ||
|
|
e6dfb5041e | ||
|
|
c857308038 | ||
|
|
f73abdbabc | ||
|
|
43184e466f | ||
|
|
627c7769b0 | ||
|
|
759dba9cd3 | ||
|
|
7cac2c4045 | ||
|
|
27bb44189f |
14
README.md
14
README.md
@@ -11,28 +11,32 @@
|
||||
<b>Download:</b>
|
||||
<a href="https://chrome.google.com/webstore/detail/mnjggcdmjocbbbhaepdhchncahnbgone">Chrome/Chromium</a> |
|
||||
<a href="https://addons.mozilla.org/addon/sponsorblock/?src=external-github">Firefox</a> |
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Android">Android</a> |
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Edge">Edge</a> |
|
||||
<a href="https://sponsor.ajay.app">Website</a> |
|
||||
<a href="https://sponsor.ajay.app/stats">Stats</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>Unofficial Ports:</b>
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Unofficial-Ports#mpv-media-player">MPV</a>
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Unofficial-Ports#mpv-media-player">MPV</a> |
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Unofficial-Ports#kodi">Kodi</a> |
|
||||
<a href="https://github.com/ajayyy/SponsorBlock/wiki/Unofficial-Ports#ios">iOS</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://addons.mozilla.org/addon/sponsorblock/?src=external-github"><img src="https://img.shields.io/amo/users/sponsorblock?label=Firefox%20Users" alt="Badge"></img></a>
|
||||
<a href="https://chrome.google.com/webstore/detail/mnjggcdmjocbbbhaepdhchncahnbgone"><img src="https://img.shields.io/chrome-web-store/users/mnjggcdmjocbbbhaepdhchncahnbgone?label=Chome%20Users" alt="Badge"></img></a>
|
||||
<a href="https://sponsor.ajay.app/stats"><img src="https://img.shields.io/badge/dynamic/json?label=Sponsors%20Submitted&query=totalSubmissions&suffix=%20sponsors&url=http%3A%2F%2Fsponsor.ajay.app%2Fapi%2FgetTotalStats&color=darkred" alt="Badge"></img></a>
|
||||
<a href="https://chrome.google.com/webstore/detail/mnjggcdmjocbbbhaepdhchncahnbgone"><img src="https://img.shields.io/chrome-web-store/users/mnjggcdmjocbbbhaepdhchncahnbgone?label=Chrome%20Users" alt="Badge"></img></a>
|
||||
<a href="https://sponsor.ajay.app/stats"><img src="https://img.shields.io/badge/dynamic/json?label=Submissions&query=totalSubmissions&suffix=%20segments&url=http%3A%2F%2Fsponsor.ajay.app%2Fapi%2FgetTotalStats&color=darkred" alt="Badge"></img></a>
|
||||
<a href="https://sponsor.ajay.app/stats"><img src="https://img.shields.io/badge/dynamic/json?label=Contributing%20Users&query=userCount&url=http%3A%2F%2Fsponsor.ajay.app%2Fapi%2FgetTotalStats&color=darkblue" alt="Badge"></img></a>
|
||||
<a href="https://sponsor.ajay.app/stats"><img src="https://img.shields.io/badge/dynamic/json?label=Time%20Saved%20From%20Skips&query=daysSaved&url=http%3A%2F%2Fsponsor.ajay.app%2Fapi%2FgetDaysSavedFormatted&color=darkgreen&suffix=%20days" alt="Badge"></img></a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that lets anyone submit the start and end times of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.
|
||||
SponsorBlock is an open-source crowdsourced browser extension to skip sponsor segments in YouTube videos. Users submit when a sponsor happens from the extension, and the extension automatically skips sponsors it knows about. It also supports skipping other categories, such as intros, outros and reminders to subscribe.
|
||||
|
||||
Also support Invidio.us.
|
||||
It also supports Invidio.us.
|
||||
|
||||
**Translate:** [](https://crowdin.com/project/sponsorblock)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "__MSG_Name__",
|
||||
"version": "2.0.1",
|
||||
"short_name": "SponsorBlock",
|
||||
"version": "2.0.4.2",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"content_scripts": [{
|
||||
@@ -50,7 +50,7 @@
|
||||
"*://*/*"
|
||||
],
|
||||
"browser_action": {
|
||||
"default_title": "__MSG_Name__",
|
||||
"default_title": "SponsorBlock",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"background": {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock за YouTube - пропускай спонсорства",
|
||||
"description": "Name of the extension."
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock für YouTube - Überspringe Sponsor Anzeigen",
|
||||
"description": "Name of the extension."
|
||||
@@ -12,10 +8,10 @@
|
||||
"description": "Description of the extension."
|
||||
},
|
||||
"400": {
|
||||
"message": "Ungültige Anforderung"
|
||||
"message": "Der Server meldet, dass diese Anfrage ungültig war"
|
||||
},
|
||||
"429": {
|
||||
"message": "Du hast zu viele Segmente in diesem Video eingereicht. Bist du dir sicher?"
|
||||
"message": "Du hast zu viele Segmente in diesem Video eingereicht. Sind es wirklich so viele?"
|
||||
},
|
||||
"409": {
|
||||
"message": "Dieser Inhalt wurde bereits eingereicht."
|
||||
@@ -123,7 +119,7 @@
|
||||
"message": "Öffne SponsorBlock-Popup"
|
||||
},
|
||||
"SubmitTimes": {
|
||||
"message": "Sende Auswahl"
|
||||
"message": "Diese Zeiten einreichen"
|
||||
},
|
||||
"submitCheck": {
|
||||
"message": "Bist du sicher, dass die Auswahl abgeschickt werden soll?"
|
||||
@@ -132,7 +128,7 @@
|
||||
"message": "Kanal auf Whitelist setzen "
|
||||
},
|
||||
"removeFromWhitelist": {
|
||||
"message": "Kanal von Whitelist entfernen"
|
||||
"message": "Kanal von der Whitelist entfernen"
|
||||
},
|
||||
"voteOnTime": {
|
||||
"message": "Stimme für Zeiten ab"
|
||||
@@ -222,7 +218,7 @@
|
||||
"message": "Benachrichtigung wieder zeigen"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock ist eine Erweiterung, die gesponsorte Segmente in YouTube-Videos überspringt. SponsorBlock ist ein Benutzernetzwerk, bei dem jeder Anfang und Ende eines Werbeblocks einreichen kann. Sobald die Information von einem Nutzer eingereicht wurde, überspringen die Erweiterungen der anderen dieses Segment automatisch.",
|
||||
"message": "SponsorBlock ist eine Erweiterung, die gesponserte Segmente von YouTube Videos überspringt. SponsorBlock ist eine durch Crowdsourcing betriebene Browsererweiterung, die es jedem erlaubt, die Beginn- und Endzeitpunkte von gesponserten Segmenten in YouTube Videos einzureichen. Nachdem diese Information eingereicht wurde, wird das Segment für alle anderen Nutzer mit dieser Erweiterung direkt übersprungen.\n\nDu kannst auch den Vorspann, Nachspann, Erinnerungen zu abonnieren und andere Kategorien überspringen.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
@@ -282,7 +278,7 @@
|
||||
"message": "Auto-Überspringen deaktivieren"
|
||||
},
|
||||
"enableAutoSkip": {
|
||||
"message": "Auto-Überspringen aktivieren"
|
||||
"message": "Automatisches Überspringen aktivieren"
|
||||
},
|
||||
"autoSkipDescription": {
|
||||
"message": "Auto-Überspringen überspringt gesponsorte Inhalte für dich. Wenn deaktiviert, fragt die Benachrichtigung, ob übersprungen werden soll."
|
||||
@@ -293,6 +289,12 @@
|
||||
"audioNotificationDescription": {
|
||||
"message": "Audio-Benachrichtigung beim Überspringen wird einen Ton abspielen, wenn ein Sponsor übersprungen wird. Wenn deaktiviert (oder wenn Automatisches-Überspringen deaktiviert ist), wird kein Ton abgespielt."
|
||||
},
|
||||
"showTimeWithSkips": {
|
||||
"message": "Zeit mit entfernten Überspringungen anzeigen"
|
||||
},
|
||||
"showTimeWithSkipsDescription": {
|
||||
"message": "Diese Zeit wird in Klammern neben der aktuellen Zeit in der Suchleiste angezeigt. Diese Zeit die gesamte Dauer des Videos ohne jeglicher Segmente. Dies inkludiert auch Segmente, die als \"In Suchleiste anzeigen\" markiert sind."
|
||||
},
|
||||
"youHaveSkipped": {
|
||||
"message": "Du übersprangst "
|
||||
},
|
||||
@@ -470,9 +472,6 @@
|
||||
"theKey": {
|
||||
"message": "Die Taste"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "wird bereits von Youtube verwendet. Bitte wählen Sie eine andere Taste."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "an eine andere Aktion gebunden. Bitte wählen Sie eine andere Taste."
|
||||
},
|
||||
@@ -487,13 +486,13 @@
|
||||
"message": "Bezahlte Promotion, bezahlte Empfehlungen und direkte Werbung. Nicht für Selbstpromotion oder kostenlose Shoutouts an Ursachen/Webseiten/Produkte, die sie mögen."
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Intro Animation"
|
||||
"message": "Unterbrechung/Vorspann"
|
||||
},
|
||||
"category_intro_description": {
|
||||
"message": "Intro Animationen, die in der Serie wiederholt werden oder keinen direkten Wert haben. Dies sollte nicht für Musikvideos verwendet werden."
|
||||
"message": "Ein Intervall ohne relevanten Inhalt. Beispiele dafür wären eine Pause, ein statisches Bild oder sich wiederholende Animationen. Dies sollte nicht für Übergänge benutzt werden, die Informationen erhalten oder für Musikvideos."
|
||||
},
|
||||
"category_intro_short": {
|
||||
"message": "Intro"
|
||||
"message": "Unterbrechung"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Endkarten/Credits"
|
||||
@@ -558,7 +557,7 @@
|
||||
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
|
||||
},
|
||||
"enableTestingServer": {
|
||||
"message": "Beta-Testing Server aktivieren"
|
||||
"message": "Beta Testing Server aktivieren"
|
||||
},
|
||||
"whatEnableTestingServer": {
|
||||
"message": "Deine Einreichungen und Stimmen werden NICHT für den Hauptserver geltend. Benutze dies nur für Tests."
|
||||
@@ -572,6 +571,12 @@
|
||||
"moreCategories": {
|
||||
"message": "Weitere Kategorien"
|
||||
},
|
||||
"chooseACategory": {
|
||||
"message": "Wähle eine Kategorie"
|
||||
},
|
||||
"youMustSelectACategory": {
|
||||
"message": "Sie müssen eine Kategorie für alle Segmente auswählen, die Sie abschicken!"
|
||||
},
|
||||
"bracketEnd": {
|
||||
"message": "(Ende)"
|
||||
},
|
||||
@@ -585,7 +590,7 @@
|
||||
"message": "Kanal-ID wurde noch nicht geladen."
|
||||
},
|
||||
"adblockerIssue": {
|
||||
"message": "Irgendetwas hält SponsorBlock davon ab, die Videodaten abzurufen. Möglicherweise ist es dein Werbeblocker. Mehr Infos: https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
|
||||
"message": "Irgendwas hält SponsorBlock davon ab, die Videodaten abzurufen. Möglicherweise ist das dein Werbeblocker. Mehr Infos: https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
|
||||
},
|
||||
"itCouldBeAdblockerIssue": {
|
||||
"message": "Falls dies weiterhin geschieht, könnte dies durch Ihren Werbeblocker verursacht werden. Bitte überprüfen Sie https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests"
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock for YouTube - Skip Sponsorships",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"Description": {
|
||||
"message": "Skip over sponsorship on YouTube videos. Report sponsors on videos you watch to save the time of others.",
|
||||
"message": "Skip sponsorships, subscription begging and more on YouTube videos. Report sponsors on videos you watch to save others' time.",
|
||||
"description": "Description of the extension."
|
||||
},
|
||||
"400": {
|
||||
@@ -222,7 +218,7 @@
|
||||
"message": "Show Notice Again"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that lets anyone submit the start and end times of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.",
|
||||
"message": "SponsorBlock lets you skip over sponsors, intros, outros subscription reminders, and other annoying parts of YouTube videos. SponsorBlock is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments and other segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment. You can also skip over non music sections of music videos.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
@@ -293,6 +289,12 @@
|
||||
"audioNotificationDescription": {
|
||||
"message": "Audio notification on skip will play a sound whenever a sponsor is skipped. If disabled (or auto skip is disabled), no sound will be played."
|
||||
},
|
||||
"showTimeWithSkips": {
|
||||
"message": "Show Time With Skips Removed"
|
||||
},
|
||||
"showTimeWithSkipsDescription": {
|
||||
"message": "This time appears in brackets next to the current time on below the seekbar. This shows the total video duration minus any segments. This includes segments marked as only \"Show In Seekbar\"."
|
||||
},
|
||||
"youHaveSkipped": {
|
||||
"message": "You have skipped "
|
||||
},
|
||||
@@ -470,9 +472,6 @@
|
||||
"theKey": {
|
||||
"message": "The key"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "is already used by youtube. Please select another key."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "is bound to another action. Please select another key."
|
||||
},
|
||||
@@ -487,13 +486,13 @@
|
||||
"message": "Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like."
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Intro Animation"
|
||||
"message": "Intermission/Intro Animation"
|
||||
},
|
||||
"category_intro_description": {
|
||||
"message": "Intro animations that are recurring in the series or provide no direct value. This should not be used on music videos."
|
||||
"message": "An interval without actual content. Could be a pause, static frame, repeating animation. This should not be used for transitions containing information or be used on music videos."
|
||||
},
|
||||
"category_intro_short": {
|
||||
"message": "Intro"
|
||||
"message": "Intermission"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Endcards/Credits"
|
||||
@@ -572,6 +571,12 @@
|
||||
"moreCategories": {
|
||||
"message": "More Categories"
|
||||
},
|
||||
"chooseACategory": {
|
||||
"message": "Choose a Category"
|
||||
},
|
||||
"youMustSelectACategory": {
|
||||
"message": "You must select a category for all segments you are submitting!"
|
||||
},
|
||||
"bracketEnd": {
|
||||
"message": "(End)"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock para YouTube - Omitir Sponsors",
|
||||
"description": "Name of the extension."
|
||||
@@ -221,10 +217,6 @@
|
||||
"showNotice": {
|
||||
"message": "Mostrar aviso de nuevo"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock es una extensión que se omitira segmentos de sponsor de vídeos de YouTube. SponsorBlock es una extensión de navegador que permite a cualquiera enviar los tiempos de comienzo y fin de segmentos de sponsor de vídeos de YouTube. Una vez que una persona envíe esta información, todos los demás con esta extensión pueden omitir directamente el segmento de sponsor.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Sitio Web",
|
||||
"description": "Used on Firefox Store Page"
|
||||
@@ -440,18 +432,12 @@
|
||||
"theKey": {
|
||||
"message": "El botón"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "ya está en uso por youtube. Por favor, seleccione otro botón."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "está enlazado a otra acción. Por favor, seleccione otro botón."
|
||||
},
|
||||
"category_sponsor": {
|
||||
"message": "Sponsor"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Animación de introducción"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Tarjetas/Créditos"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock YouTubelle - Ohita sponsoroinnit",
|
||||
"description": "Name of the extension."
|
||||
@@ -314,9 +310,6 @@
|
||||
"theKey": {
|
||||
"message": "Näppäin"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "on jo YouTuben käytössä. Valitse toinen näppäin."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "on jo liitetty toiseen toimintoon. Valitse toinen näppäin."
|
||||
},
|
||||
@@ -327,9 +320,6 @@
|
||||
"category_sponsor": {
|
||||
"message": "Sponsori"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Intro-animaatio"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Loppukortit/-tekstit"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock pour YouTube - Supprime les messages commerciaux et publicités intégrées",
|
||||
"description": "Name of the extension."
|
||||
@@ -81,7 +77,7 @@
|
||||
"message": "Êtes-vous certain(e) de vouloir supprimer vos soumissions ?\n\n"
|
||||
},
|
||||
"Unknown": {
|
||||
"message": "Une erreur s'est produite lors de la soumission, veuillez ré-essayer plus tard."
|
||||
"message": "Une erreur s'est produite lors de la soumission, veuillez réessayer plus tard."
|
||||
},
|
||||
"sponsorFound": {
|
||||
"message": "Les messages commerciaux pour cette vidéo sont déjà dans notre base de données !"
|
||||
@@ -168,10 +164,10 @@
|
||||
"message": "Soumettre les temps"
|
||||
},
|
||||
"publicStats": {
|
||||
"message": "Affiché sur le classement public pour montrer vos contributions. Voir sur"
|
||||
"message": "Votre pseudo est inscrit dans le classement public pour afficher vos contributions. Le consulter"
|
||||
},
|
||||
"setUsername": {
|
||||
"message": "Choisir pseudo"
|
||||
"message": "Choisir un pseudonyme"
|
||||
},
|
||||
"discordAdvert": {
|
||||
"message": "Rejoignez le serveur Discord officiel pour toutes suggestions ou remarques!"
|
||||
@@ -222,7 +218,7 @@
|
||||
"message": "Afficher la notification"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock est une extension qui permet de passer les messages commerciaux des vidéos YouTube. SponsorBlock est une extension pour navigateur basée sur le crowdsourcing permettant à n'importe qui de soumettre le début et la fin des segments commerciaux sur les vidéos YouTube. Dès qu'une personne a soumis ces informations, les autres utilisateurs de l'extension en bénéficieront et verront les messages commerciaux automatiquement sautés.",
|
||||
"message": "SponsorBlock est une extension qui permet de sauter les segments sponsorisés des vidéos de YouTube. SponsorBlock est une extension de navigateur participative qui permet à quiconque de soumettre les heures de début et de fin des segments sponsorisés des vidéos de YouTube. Une fois qu'une personne a soumis ces informations, toutes les autres personnes possédant cette extension sauteront automatiquement les segments sponsorisés. \n\n Vous pouvez également sauter les intros, les outros, les rappels d'abonnement et d'autres catégories.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
@@ -293,11 +289,17 @@
|
||||
"audioNotificationDescription": {
|
||||
"message": "La notification audio lors du passage jouera un son à chaque fois qu'un sponsor est ignoré. Si désactivé (ou si le passage automatique est désactivé), aucun son ne sera joué."
|
||||
},
|
||||
"showTimeWithSkips": {
|
||||
"message": "Afficher le temps avec les passages supprimés"
|
||||
},
|
||||
"showTimeWithSkipsDescription": {
|
||||
"message": "Ce temps apparaît entre crochets à côté du temps actuel sous la barre de défilement. Cela indique la durée totale de la vidéo après déduction de tout les segments. Ceci comprend les segments marqués comme étant uniquement à \"Afficher dans la barre de recherche\"."
|
||||
},
|
||||
"youHaveSkipped": {
|
||||
"message": "Vous avez passé "
|
||||
},
|
||||
"youHaveSaved": {
|
||||
"message": "Vous avez économisé "
|
||||
"message": "Vous vous êtes économisé "
|
||||
},
|
||||
"minLower": {
|
||||
"message": "minute"
|
||||
@@ -342,7 +344,7 @@
|
||||
"message": "Passage automatique"
|
||||
},
|
||||
"showSkipNotice": {
|
||||
"message": "Afficher l'avis après le passage d'un sponsor"
|
||||
"message": "Notifier après qu'un sponsor ait été sauté"
|
||||
},
|
||||
"keybindCurrentlySet": {
|
||||
"message": ". Il est actuellement réglé sur :"
|
||||
@@ -470,9 +472,6 @@
|
||||
"theKey": {
|
||||
"message": "La clé"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "est déjà utilisé par YouTube. Veuillez sélectionner une autre clé."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "est lié à une autre action. Veuillez sélectionner une autre clé."
|
||||
},
|
||||
@@ -483,8 +482,17 @@
|
||||
"category_sponsor": {
|
||||
"message": "Message commercial"
|
||||
},
|
||||
"category_sponsor_description": {
|
||||
"message": "Promotion rémunérée, parrainage rémunéré et publicité directe. Pas pour l'autopromotion ou les présentations gratuites de causes, de créateurs, de sites web ou de produits qu'ils aiment."
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Générique d'introduction"
|
||||
"message": "Entracte/Animation d'intro"
|
||||
},
|
||||
"category_intro_description": {
|
||||
"message": "Un intervalle sans contenu réel. Peut être une pause, une image statique, une animation répétitive. Ceci ne doit pas être utilisé pour les transitions contenant des informations ou être utilisé sur les vidéos musicales."
|
||||
},
|
||||
"category_intro_short": {
|
||||
"message": "Entracte"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Générique de fin"
|
||||
@@ -495,12 +503,33 @@
|
||||
"category_interaction": {
|
||||
"message": "Rappel d'interaction (abonnement)"
|
||||
},
|
||||
"category_interaction_description": {
|
||||
"message": "Lorsqu'il y a un bref rappel pour liker, s'abonner ou les follow parmi le contenu. Si le message est long ou porte sur quelque chose de spécifique, cela devrait plutôt être classé comme une autopromotion."
|
||||
},
|
||||
"category_interaction_short": {
|
||||
"message": "Rappel d'interaction"
|
||||
},
|
||||
"category_selfpromo": {
|
||||
"message": "Non rémunéré/autopromotion"
|
||||
},
|
||||
"category_selfpromo_description": {
|
||||
"message": "Semblable au \"sponsor\", excepté pour la promotion non rémunérée ou l'auto-promotion. Cela inclut les marchandises, les dons et les informations sur leurs collaborateurs."
|
||||
},
|
||||
"category_music_offtopic": {
|
||||
"message": "Musique : Segment non-musicale"
|
||||
},
|
||||
"category_music_offtopic_description": {
|
||||
"message": "A utiliser uniquement dans les vidéos musicales. Cela inclut les introductions ou les fins dans les vidéos."
|
||||
},
|
||||
"category_music_offtopic_short": {
|
||||
"message": "Hors musique"
|
||||
},
|
||||
"category_livestream_messages": {
|
||||
"message": "Stream : lecture de dons et messages"
|
||||
},
|
||||
"category_livestream_messages_short": {
|
||||
"message": "Lecture de messages"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Désactiver"
|
||||
},
|
||||
@@ -510,6 +539,23 @@
|
||||
"showOverlay": {
|
||||
"message": "Afficher dans la barre de progression"
|
||||
},
|
||||
"colorFormatIncorrect": {
|
||||
"message": "Votre couleur est mal formatée. Il devrait s'agir d'un code hexadécimal à 3 ou 6 chiffres avec un signe numérique au début."
|
||||
},
|
||||
"previewColor": {
|
||||
"message": "Couleur en mode aperçu",
|
||||
"description": "Referring to submissions that have not been sent to the server yet."
|
||||
},
|
||||
"seekBarColor": {
|
||||
"message": "Couleur dans la barre de progression"
|
||||
},
|
||||
"category": {
|
||||
"message": "Catégorie"
|
||||
},
|
||||
"skipOption": {
|
||||
"message": "Option de saut",
|
||||
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
|
||||
},
|
||||
"enableTestingServer": {
|
||||
"message": "Activer le serveur de test bêta"
|
||||
},
|
||||
@@ -525,6 +571,12 @@
|
||||
"moreCategories": {
|
||||
"message": "Autres catégories"
|
||||
},
|
||||
"chooseACategory": {
|
||||
"message": "Choisissez une catégorie"
|
||||
},
|
||||
"youMustSelectACategory": {
|
||||
"message": "Vous devez sélectionner une catégorie pour tous les segments que vous soumettez !"
|
||||
},
|
||||
"bracketEnd": {
|
||||
"message": "(Fin)"
|
||||
},
|
||||
@@ -563,5 +615,24 @@
|
||||
},
|
||||
"multipleSegments": {
|
||||
"message": "Plusieurs segments"
|
||||
},
|
||||
"guidelines": {
|
||||
"message": "Instructions"
|
||||
},
|
||||
"readTheGuidelines": {
|
||||
"message": "Lisez les instructions !!",
|
||||
"description": "Show the first time they submit or if they are \"high risk\""
|
||||
},
|
||||
"categoryUpdate1": {
|
||||
"message": "Les catégories sont là !"
|
||||
},
|
||||
"categoryUpdate2": {
|
||||
"message": "Ouvrir les options pour sauter les intros, outros, marchandises, etc."
|
||||
},
|
||||
"unsubmittedWarning": {
|
||||
"message": "Notification de segments non soumis"
|
||||
},
|
||||
"unsubmittedWarningDescription": {
|
||||
"message": "Envoyer une notification lorsque vous quittez une vidéo avec des segments qui ne sont pas téléversés"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "સ્પોન્સરબ્લોક",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "યુટ્યુબ માટે સ્પોન્સરબ્લોક - સ્પોન્સરશિપ છોડી દો",
|
||||
"description": "Name of the extension."
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"Loading": {
|
||||
"message": "Betöltés..."
|
||||
},
|
||||
|
||||
@@ -205,10 +205,6 @@
|
||||
"showNotice": {
|
||||
"message": "Mostra di Nuovo l'Avviso"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock è un'estensione che salta gli spezzoni con contenuti sponsorizzati nei video di YouTube. SponsorBlock è un'estensione crowdsourced per i browser che permette a chiunque di inviare i minutaggi degli spezzoni sponsorizzati nei video di YouTube. Quando una persona avrà inviato questa informazione, tutti gli utenti che utilizzano questa estensione potranno saltare lo spezzone sponsorizzato.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Sito Web",
|
||||
"description": "Used on Firefox Store Page"
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
{}
|
||||
{
|
||||
"fullName": {
|
||||
"message": "SponserBlock for YouTube - 動画のスポンサーセクションを自動でスキップする",
|
||||
"description": "Name of the extension."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock voor YouTube - Sla sponsorberichten over",
|
||||
"description": "Name of the extension."
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock na YouTube - Omiń reklamy sponsorów",
|
||||
"description": "Name of the extension."
|
||||
@@ -209,10 +205,6 @@
|
||||
"showNotice": {
|
||||
"message": "Pokaż informacje ponownie"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock jest rozszerzeniem które przewinie segmenty sponsorów w filmach na YouTube. SponsorBlock jest opartym na crowdsourcing rozszerzeniem które pozwala każdemu zgłaszać początek i koniec segmentu reklamowego w filmach na YouTube. Kiedy ktoś zgłosi taki fragment zostanie on pominięty przez innych użytkowników rozszerzenia.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Strona",
|
||||
"description": "Used on Firefox Store Page"
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock para YouTube - Pule patrocínios",
|
||||
"description": "Name of the extension."
|
||||
@@ -35,9 +31,15 @@
|
||||
"Segments": {
|
||||
"message": "segmentos de patrocinadores"
|
||||
},
|
||||
"upvoteButtonInfo": {
|
||||
"message": "Votar nesse segmento positivamente"
|
||||
},
|
||||
"reportButtonTitle": {
|
||||
"message": "Reportar"
|
||||
},
|
||||
"reportButtonInfo": {
|
||||
"message": "Reportar esse segmento como inválido."
|
||||
},
|
||||
"Dismiss": {
|
||||
"message": "Ignorar"
|
||||
},
|
||||
@@ -54,7 +56,7 @@
|
||||
"message": "Nunca mostrar"
|
||||
},
|
||||
"hitGoBack": {
|
||||
"message": "Aperta reverter pulo para voltar onde estava"
|
||||
"message": "Aperta \"reverter pulo\" para voltar onde estava."
|
||||
},
|
||||
"unskip": {
|
||||
"message": "Reverter pulo"
|
||||
@@ -65,6 +67,9 @@
|
||||
"paused": {
|
||||
"message": "Pausado"
|
||||
},
|
||||
"manualPaused": {
|
||||
"message": "Tempo parado"
|
||||
},
|
||||
"confirmMSG": {
|
||||
"message": "\n\nPara editar ou remover linhas individuais, clique com o botão direito ou abra o popup da extensão pelo icone no canto superior direito."
|
||||
},
|
||||
@@ -95,6 +100,9 @@
|
||||
"voted": {
|
||||
"message": "Votado!"
|
||||
},
|
||||
"serverDown": {
|
||||
"message": "Parece que o servidor caiu. Contate o desenvolvedor o quanto antes."
|
||||
},
|
||||
"connectionError": {
|
||||
"message": "Ocorreu um erro de conexão. Código de erro: "
|
||||
},
|
||||
@@ -210,7 +218,7 @@
|
||||
"message": "Mostrar notificação outra vez"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock é uma extensão que salta segmentos patrocinados em vídeos do YouTube. SponsorBlock é uma extenção crowdfunded que permite a qualquer um submeter o início e o fim de segmentos patrocinados. Assim que uma pessoa submete essa informação todos com a extenção poderam saltar automaticamete o patrocínio.",
|
||||
"message": "SponsorBlock é uma extensão que pula patrocínios em vídeos no YouTube. SponsorBlock é uma extensão de navegador que deixa qualquer pessoa enviar o começo e fim de segmentos patrocinados em vídeos no YouTube. Quando uma pessoa envia essa informação, todo mundo com a extensão vai pular essa mesma parte patrocinada. \n\n Você também pode pular introduções, finais do vídeo, lembretes para inscrever e outras categorias.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
@@ -263,6 +271,9 @@
|
||||
"skip": {
|
||||
"message": "Pular"
|
||||
},
|
||||
"skipped": {
|
||||
"message": "Pulado"
|
||||
},
|
||||
"disableAutoSkip": {
|
||||
"message": "Desativar Salto Automático"
|
||||
},
|
||||
@@ -278,6 +289,9 @@
|
||||
"audioNotificationDescription": {
|
||||
"message": "A notificação de áudio ao pular irá tocar um som sempre que um patrocínio for ignorado. Se desativado (ou o pulo automático estiver desativado), nenhum som será reproduzido."
|
||||
},
|
||||
"showTimeWithSkips": {
|
||||
"message": "Mostrar tempo com pulos removidos"
|
||||
},
|
||||
"youHaveSkipped": {
|
||||
"message": "Você pulou "
|
||||
},
|
||||
@@ -455,9 +469,6 @@
|
||||
"theKey": {
|
||||
"message": "A tecla"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "já está sendo usado pelo youtube. Por favor, selecione outra tecla."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "está vinculado a outra ação. Por favor, selecione outra tecla."
|
||||
},
|
||||
@@ -468,21 +479,24 @@
|
||||
"category_sponsor": {
|
||||
"message": "Patrocinador"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Animação de Introdução"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Finalização/Créditos"
|
||||
},
|
||||
"category_interaction": {
|
||||
"message": "Lembrete de interação (inscrever-se)"
|
||||
},
|
||||
"category_interaction_short": {
|
||||
"message": "Lembrete de interação"
|
||||
},
|
||||
"category_music_offtopic": {
|
||||
"message": "Música: Seção sem música"
|
||||
},
|
||||
"category_livestream_messages": {
|
||||
"message": "Livestream: Leituras de Doação/Mensagem"
|
||||
},
|
||||
"category_livestream_messages_short": {
|
||||
"message": "Leitura de mensagens"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Desativar"
|
||||
},
|
||||
@@ -492,6 +506,20 @@
|
||||
"showOverlay": {
|
||||
"message": "Mostrar barra de progresso"
|
||||
},
|
||||
"previewColor": {
|
||||
"message": "Cor de pré-visualização",
|
||||
"description": "Referring to submissions that have not been sent to the server yet."
|
||||
},
|
||||
"seekBarColor": {
|
||||
"message": "Cor da barra"
|
||||
},
|
||||
"category": {
|
||||
"message": "Categoria"
|
||||
},
|
||||
"skipOption": {
|
||||
"message": "Opção de pulo",
|
||||
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
|
||||
},
|
||||
"enableTestingServer": {
|
||||
"message": "Habilitar Servidor em teste Beta"
|
||||
},
|
||||
@@ -534,10 +562,25 @@
|
||||
"forceChannelCheckPopup": {
|
||||
"message": "Considere habilitar a verificação de canal forçada antes de pular os patrocinadores"
|
||||
},
|
||||
"downvoteDescription": {
|
||||
"message": "Incorreto/tempo errado"
|
||||
},
|
||||
"incorrectCategory": {
|
||||
"message": "Categoria errada"
|
||||
},
|
||||
"nonMusicCategoryOnMusic": {
|
||||
"message": "Este vídeo é classificado como música. Você tem certeza que deseja enviar segmentos com categorias que não são músicas? A menos que esse vídeo não seja de fato música, você não deve enviar esse segmento. Por favor leia as orientações se estiver em dúvidas."
|
||||
},
|
||||
"multipleSegments": {
|
||||
"message": "Multiplos segmentos"
|
||||
},
|
||||
"categoryUpdate1": {
|
||||
"message": "As categorias estão aqui!"
|
||||
},
|
||||
"unsubmittedWarning": {
|
||||
"message": "Notificação de segmentos não enviados"
|
||||
},
|
||||
"unsubmittedWarningDescription": {
|
||||
"message": "Enviar uma notificação quando você sair de um vídeo com segmentos que não foram enviados"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,10 +205,6 @@
|
||||
"showNotice": {
|
||||
"message": "Mostrar notificação outra vez"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock é uma extensão que salta segmentos patrocinados em vídeos do YouTube. SponsorBlock é uma extenção crowdfunded que permite a qualquer um submeter o início e o fim de segmentos patrocinados. Assim que uma pessoa submete essa informação todos com a extenção poderam saltar automaticamete o patrocínio.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Site",
|
||||
"description": "Used on Firefox Store Page"
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock pentru YouTube - Sari peste sponsorizări",
|
||||
"description": "Name of the extension."
|
||||
@@ -222,7 +218,7 @@
|
||||
"message": "Arată Notificarea Din Nou"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock este o extensie care va sări peste segmentele sponsorizate din videoclipurile de pe YouTube. SponsorBlock este o extensie crowdsourced care lasă pe oricine să trimită începutul și finalul segmentelor sponsorizate din videoclipurile de pe YouTube. Odată ce o persoană trimite aceste informații, toată lumea cu această extensie va sări peste acel segment sponsorizat.",
|
||||
"message": "SponsorBlock este o extensie care va sări peste segmentele sponsorizate din videoclipurile de pe YouTube. SponsorBlock este o extensie crowdsourced care lasă pe oricine să trimită începutul și finalul segmentelor sponsorizate din videoclipurile de pe YouTube. Odată ce o persoană trimite aceste informații, toată lumea cu această extensie va sări peste acel segment sponsorizat. \n\n Poți de asemenea să sari peste intro-uri, outro-uri, amintiri de abonare și alte categorii.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
@@ -293,6 +289,12 @@
|
||||
"audioNotificationDescription": {
|
||||
"message": "Notificarea audio va reda un sunet atunci când sari peste un segement sponsorizat. Daca este dezactivat (sau autoskip este dezactivat), niciun sunet nu va fi redat."
|
||||
},
|
||||
"showTimeWithSkips": {
|
||||
"message": "Arată timpul cu săriturile eliminate"
|
||||
},
|
||||
"showTimeWithSkipsDescription": {
|
||||
"message": "Acest timp apare în paranteze lângă ora curentă sub bara de progres. Aceasta arată durata totală a videoclipului minus orice segment. Aceasta include segmente marcate doar ca \"Afișare în Seekbar\"."
|
||||
},
|
||||
"youHaveSkipped": {
|
||||
"message": "Ai sărit peste "
|
||||
},
|
||||
@@ -470,9 +472,6 @@
|
||||
"theKey": {
|
||||
"message": "Tasta"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "este deja utilizat de YouTube. Vă rugăm selectați o altă tastă."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "este deja setată la o altă acțiune. Vă rugăm să selectați o altă tastă."
|
||||
},
|
||||
@@ -487,13 +486,13 @@
|
||||
"message": "Promovare plătită, refferali plătiți și reclame directe. Nu pentru autopromovări sau promovări gratis ale cauzelor/creatorilor/website-urilor/produselor."
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Animație de Început"
|
||||
"message": "Pauză/Animație Intro"
|
||||
},
|
||||
"category_intro_description": {
|
||||
"message": "Animațiile de introducere care se repetă în serie sau care nu oferă nici o valoare directă. Această opțiune nu ar trebui folosită în videoclipurile muzicale."
|
||||
"message": "Un interval fără conținut real. Poate fi o pauză, un cadru static, o animație care se repetă. Acestea nu ar trebui utilizate pentru tranziții care conțin informații sau nu ar trebui utilizate pe videoclipuri muzicale."
|
||||
},
|
||||
"category_intro_short": {
|
||||
"message": "Intro"
|
||||
"message": "Pauză"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Ecran De Final/Credite"
|
||||
@@ -572,6 +571,12 @@
|
||||
"moreCategories": {
|
||||
"message": "Mai multe categorii"
|
||||
},
|
||||
"chooseACategory": {
|
||||
"message": "Alege o categorie"
|
||||
},
|
||||
"youMustSelectACategory": {
|
||||
"message": "Trebuie să selectaţi o categorie pentru toate segmentele pe care le trimiteţi!"
|
||||
},
|
||||
"bracketEnd": {
|
||||
"message": "(Sfârșit)"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock для YouTube - Пропускайте спонсорские вставки",
|
||||
"description": "Name of the extension."
|
||||
@@ -65,6 +61,9 @@
|
||||
"paused": {
|
||||
"message": "Пауза"
|
||||
},
|
||||
"manualPaused": {
|
||||
"message": "Таймер остановлен"
|
||||
},
|
||||
"confirmMSG": {
|
||||
"message": "\n\nЧтобы изменить или удалить отдельные значения, нажмите кнопку «Информация» или откройте всплывающее окно расширения, щелкнув значок расширения в правом верхнем углу."
|
||||
},
|
||||
@@ -107,6 +106,9 @@
|
||||
"clearTimes": {
|
||||
"message": "Удалить отмеченные сегменты"
|
||||
},
|
||||
"openPopup": {
|
||||
"message": "Открыть всплывающее окно SponsorBlock"
|
||||
},
|
||||
"SubmitTimes": {
|
||||
"message": "Отправить отмеченные сегменты"
|
||||
},
|
||||
@@ -206,10 +208,6 @@
|
||||
"showNotice": {
|
||||
"message": "Показывать уведомление снова"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock — это расширение, которое пропускает спонсорские вставки в видео на YouTube. SponsorBlock — это краудсорсинговое расширение, которое позволяет каждому отправить время начала и конца спонсорских сегментов в видео на YouTube. После того, как кто-нибудь отправляет эту информацию, все остальные пользователи расширения будут автоматически пропускать спонсорские сегменты.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Сайт",
|
||||
"description": "Used on Firefox Store Page"
|
||||
@@ -413,6 +411,24 @@
|
||||
"whatExportOptions": {
|
||||
"message": "Это вся конфигурация в формате JSON. Этот файл содержит Ваш идентификатор пользователя, поэтому не забудьте общаться с этим разумно."
|
||||
},
|
||||
"submit": {
|
||||
"message": "Отправить"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Отменить"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Удалить"
|
||||
},
|
||||
"preview": {
|
||||
"message": "Превью"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Редактировать"
|
||||
},
|
||||
"copyDebugInformation": {
|
||||
"message": "Скопировать отладочную информацию в буфер обмена"
|
||||
},
|
||||
"bracketNow": {
|
||||
"message": "(Сейчас)"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"Sponsor": {
|
||||
"message": "sponzor"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock för YouTube - Hoppa över sponsring",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"Description": {
|
||||
"message": "Hoppa över sponsormeddelanden på YouTube-videor. Rapportera sponsring på videor du ser på för att spara tid för andra.",
|
||||
"message": "Hoppa över sponsormeddelanden på YouTube-videor. Rapportera sponsring på videor du ser på för att spara tid åt andra.",
|
||||
"description": "Description of the extension."
|
||||
},
|
||||
"400": {
|
||||
@@ -18,7 +14,7 @@
|
||||
"message": "Du har rapporterat för många sponsormeddelanden för den här videon, är du säker att det finns så många?"
|
||||
},
|
||||
"409": {
|
||||
"message": "Den här har redan blivit rapporterad."
|
||||
"message": "Den här har redan blivit rapporterad"
|
||||
},
|
||||
"channelWhitelisted": {
|
||||
"message": "Kanal vitlistad!"
|
||||
@@ -54,7 +50,7 @@
|
||||
"message": "Sekunder"
|
||||
},
|
||||
"Hide": {
|
||||
"message": "\"Visa aldrig\"-knappen. "
|
||||
"message": "Visa Aldrig"
|
||||
},
|
||||
"hitGoBack": {
|
||||
"message": "Tryck på Tillbaka för att ångra åtgärden."
|
||||
@@ -63,7 +59,7 @@
|
||||
"message": "Tillbaka"
|
||||
},
|
||||
"reskip": {
|
||||
"message": "Frammåt"
|
||||
"message": "Framåt"
|
||||
},
|
||||
"paused": {
|
||||
"message": "Pausad"
|
||||
@@ -218,10 +214,6 @@
|
||||
"showNotice": {
|
||||
"message": "Visa Notisen Igen"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock är ett webbläsartillägg som hoppar över sponsormeddelanden på YouTube-videor. SponsorBlock är ett crowdsourcat webbläsartillägg som låter vem som hellst att rapportera start och sluttider för sponsorsegment på YouTube-videor. När informationen väl har rapporterats kommer alla andra med detta tillägg att hoppa över sponsorsegmentet.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Hemsida",
|
||||
"description": "Used on Firefox Store Page"
|
||||
@@ -467,9 +459,6 @@
|
||||
"theKey": {
|
||||
"message": "Nyckeln"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "används redan av YouTube. Välj en annan nyckel."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "är kopplad till en annan funktion. Välj en annan knapp."
|
||||
},
|
||||
@@ -480,15 +469,6 @@
|
||||
"category_sponsor": {
|
||||
"message": "Sponsormeddelande"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Intro-animation"
|
||||
},
|
||||
"category_intro_description": {
|
||||
"message": "Intro-animationer som är återkommande i serien eller inte ger direkt värde. Detta bör inte användas på musikvideor."
|
||||
},
|
||||
"category_intro_short": {
|
||||
"message": "Intro"
|
||||
},
|
||||
"category_music_offtopic_description": {
|
||||
"message": "Endast för användning i musikvideor. Detta inkluderar intros och outros i musikvideor."
|
||||
},
|
||||
@@ -522,6 +502,12 @@
|
||||
"moreCategories": {
|
||||
"message": "Fler Kategorier"
|
||||
},
|
||||
"chooseACategory": {
|
||||
"message": "Välj en kategori"
|
||||
},
|
||||
"youMustSelectACategory": {
|
||||
"message": "Du måste välja en kategori för alla segment du skickar in!"
|
||||
},
|
||||
"bracketEnd": {
|
||||
"message": "(Slut)"
|
||||
},
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "Youtube için SponsorBlock - Sponsorlukları Atla",
|
||||
"description": "Name of the extension."
|
||||
@@ -221,10 +217,6 @@
|
||||
"showNotice": {
|
||||
"message": "Uyarıyı Tekrar Göster"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock, YouTube'da sponsorlu bölümleri atlamanıza yardımcı olan bir eklentidir. SponsorBlock, herkesin YouTube videolarında bulunan sponsorlu bölümlerin başını ve sonunu göndermesine izin veren kitlekaynaklı bir tarayıcı eklentisidir. Bu bilgiyi biri yolladığında, bu sponsorlu bölüm herkeste atlanır.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Website",
|
||||
"description": "Used on Firefox Store Page"
|
||||
@@ -470,9 +462,6 @@
|
||||
"theKey": {
|
||||
"message": "Anahtar"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "YouTube tarafından zaten kullanımda. Lütfen başka bir anahtar seçin."
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "başka bir eyleme bağlı. Lütfen başka bir anahtar seçin."
|
||||
},
|
||||
@@ -483,9 +472,6 @@
|
||||
"category_sponsor": {
|
||||
"message": "Sponsor"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "Giriş Animasyonu"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "Bitiş Ekranı/Jenerik"
|
||||
},
|
||||
@@ -510,6 +496,20 @@
|
||||
"showOverlay": {
|
||||
"message": "Arama Çubuğunda Göster"
|
||||
},
|
||||
"previewColor": {
|
||||
"message": "Önizleme Rengi",
|
||||
"description": "Referring to submissions that have not been sent to the server yet."
|
||||
},
|
||||
"seekBarColor": {
|
||||
"message": "Oynatma Çubuğu Rengi"
|
||||
},
|
||||
"category": {
|
||||
"message": "Kategori"
|
||||
},
|
||||
"skipOption": {
|
||||
"message": "Atlama Seçeneği",
|
||||
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
|
||||
},
|
||||
"enableTestingServer": {
|
||||
"message": "Beta Deneme Sunucusunu Devreye Sok"
|
||||
},
|
||||
@@ -563,5 +563,15 @@
|
||||
},
|
||||
"multipleSegments": {
|
||||
"message": "Birden Çok Bölüm"
|
||||
},
|
||||
"guidelines": {
|
||||
"message": "Kılavuz"
|
||||
},
|
||||
"readTheGuidelines": {
|
||||
"message": "Kılavuzu Oku!!",
|
||||
"description": "Show the first time they submit or if they are \"high risk\""
|
||||
},
|
||||
"categoryUpdate1": {
|
||||
"message": "Kategoriler burada!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock для YouTube - Пропускайте спонсорські вставки",
|
||||
"description": "Name of the extension."
|
||||
@@ -209,10 +205,6 @@
|
||||
"showNotice": {
|
||||
"message": "Показувати сповіщення знову"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock - це розширення, яке пропускає спонсорські вставки в відео на YouTube. SponsorBlock - це краудсорсінгове розширення, яке дозволяє кожному надіслати час початку і кінця спонсорських сегментів в відео на YouTube. Після того, як хто-небудь надсилає цю інформацію, всі інші користувачі розширення будуть автоматично пропускати спонсорські сегменти.",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "Сайт",
|
||||
"description": "Used on Firefox Store Page"
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"Name": {
|
||||
"message": "SponsorBlock",
|
||||
"description": "Name of the extension."
|
||||
},
|
||||
"fullName": {
|
||||
"message": "SponsorBlock for YouTube - 跳过赞助商广告",
|
||||
"description": "Name of the extension."
|
||||
@@ -221,10 +217,6 @@
|
||||
"showNotice": {
|
||||
"message": "重新显示通知"
|
||||
},
|
||||
"longDescription": {
|
||||
"message": "SponsorBlock 是一个用于跳过 YouTube 视频的赞助商广告片段的扩展。SponsorBlock 是一个众包的浏览器扩展,可以让任何人提交 Youtube 视频的赞助商广告片段的开始和结束时间。一旦有人提交了信息,其他所有使用此扩展的人都能直接跳过赞助商广告片段。",
|
||||
"description": "Full description of the extension on the store pages."
|
||||
},
|
||||
"website": {
|
||||
"message": "网站",
|
||||
"description": "Used on Firefox Store Page"
|
||||
@@ -470,9 +462,6 @@
|
||||
"theKey": {
|
||||
"message": "按键"
|
||||
},
|
||||
"keyAlreadyUsedByYouTube": {
|
||||
"message": "已被 Youtube 使用。请选择其他按键。"
|
||||
},
|
||||
"keyAlreadyUsed": {
|
||||
"message": "已绑定其他操作。请选择其他按键。"
|
||||
},
|
||||
@@ -483,24 +472,30 @@
|
||||
"category_sponsor": {
|
||||
"message": "赞助商广告"
|
||||
},
|
||||
"category_intro": {
|
||||
"message": "开头动画"
|
||||
},
|
||||
"category_outro": {
|
||||
"message": "结束画面/结尾职员表"
|
||||
},
|
||||
"category_interaction": {
|
||||
"message": "互动提醒(订阅)"
|
||||
},
|
||||
"category_interaction_short": {
|
||||
"message": "互动提醒"
|
||||
},
|
||||
"category_selfpromo": {
|
||||
"message": "未收钱的/自我推销"
|
||||
},
|
||||
"category_music_offtopic": {
|
||||
"message": "音乐:非音乐部分"
|
||||
},
|
||||
"category_music_offtopic_short": {
|
||||
"message": "无音乐"
|
||||
},
|
||||
"category_livestream_messages": {
|
||||
"message": "直播:捐赠/消息阅读"
|
||||
},
|
||||
"category_livestream_messages_short": {
|
||||
"message": "阅读消息"
|
||||
},
|
||||
"disable": {
|
||||
"message": "禁用"
|
||||
},
|
||||
@@ -510,6 +505,20 @@
|
||||
"showOverlay": {
|
||||
"message": "在搜索栏中显示"
|
||||
},
|
||||
"previewColor": {
|
||||
"message": "预览颜色",
|
||||
"description": "Referring to submissions that have not been sent to the server yet."
|
||||
},
|
||||
"seekBarColor": {
|
||||
"message": "拖动条颜色"
|
||||
},
|
||||
"category": {
|
||||
"message": "类别"
|
||||
},
|
||||
"skipOption": {
|
||||
"message": "跳过选项",
|
||||
"description": "Used on the options page to describe the ways to skip the segment (auto skip, manual, etc.)"
|
||||
},
|
||||
"enableTestingServer": {
|
||||
"message": "启用 Beta 测试服务器"
|
||||
},
|
||||
@@ -563,5 +572,18 @@
|
||||
},
|
||||
"multipleSegments": {
|
||||
"message": "多个片段"
|
||||
},
|
||||
"guidelines": {
|
||||
"message": "指南"
|
||||
},
|
||||
"readTheGuidelines": {
|
||||
"message": "阅读指南!!",
|
||||
"description": "Show the first time they submit or if they are \"high risk\""
|
||||
},
|
||||
"categoryUpdate1": {
|
||||
"message": "类别在这里!"
|
||||
},
|
||||
"categoryUpdate2": {
|
||||
"message": "打开选项,跳过开头,结尾,商业等。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
color: rgb(235, 235, 235);
|
||||
border: none;
|
||||
display: inline-block;
|
||||
font-size: 13.3333px !important;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
@@ -179,6 +180,7 @@
|
||||
.sponsorSkipNoticeCloseButton {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
box-sizing: unset;
|
||||
|
||||
padding: 2px 5px;
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
|
||||
<body class="sponsorBlockPageBody">
|
||||
|
||||
<div id="title">
|
||||
<div id="title" class="titleBar">
|
||||
<img src="../icons/LogoSponsorBlocker256px.png" height="80" class="profilepic"/>
|
||||
SponsorBlock
|
||||
</div>
|
||||
|
||||
<div class="center">
|
||||
<p class="createdBy">__MSG_createdBy__ <a href="https://ajay.app">Ajay Ramachandran</a> <img src="../icons/newprofilepic.jpg" height="30" class="profilepiccircle"/></p>
|
||||
<p class="createdBy titleBar">__MSG_createdBy__ <a href="https://ajay.app">Ajay Ramachandran</a> <img src="../icons/newprofilepic.jpg" height="30" class="profilepiccircle"/></p>
|
||||
|
||||
<h1>__MSG_Options__</h1>
|
||||
|
||||
@@ -268,6 +268,23 @@
|
||||
<div class="small-description">__MSG_audioNotificationDescription__</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div option-type="toggle" sync-option="showTimeWithSkips">
|
||||
<label class="switch-container" label-name="__MSG_showTimeWithSkips__">
|
||||
<label class="switch">
|
||||
<input type="checkbox" checked>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div class="small-description">__MSG_showTimeWithSkipsDescription__</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div id="app" class="popupBody sponsorBlockPageBody">
|
||||
<h1 class="popupElement logoText">
|
||||
<img src="icons/IconSponsorBlocker256px.png" height="32px" id="sponsorBlockPopupLogo"/>
|
||||
__MSG_Name__
|
||||
SponsorBlock
|
||||
</h1>
|
||||
|
||||
<!-- Loading text -->
|
||||
|
||||
@@ -40,21 +40,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
case "addSponsorTime":
|
||||
addSponsorTime(request.time, request.videoID, callback);
|
||||
|
||||
//this allows the callback to be called later
|
||||
return true;
|
||||
|
||||
case "getSponsorTimes":
|
||||
getSponsorTimes(request.videoID, function(sponsorTimes) {
|
||||
callback({
|
||||
sponsorTimes
|
||||
});
|
||||
});
|
||||
|
||||
//this allows the callback to be called later
|
||||
return true;
|
||||
case "submitVote":
|
||||
submitVote(request.type, request.UUID, request.category).then(callback);
|
||||
@@ -127,38 +112,6 @@ function unregisterFirefoxContentScript(id: string) {
|
||||
delete contentScriptRegistrations[id];
|
||||
}
|
||||
|
||||
//gets the sponsor times from memory
|
||||
function getSponsorTimes(videoID, callback) {
|
||||
let sponsorTimes = [];
|
||||
let sponsorTimesStorage = Config.config.sponsorTimes.get(videoID);
|
||||
|
||||
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
||||
sponsorTimes = sponsorTimesStorage;
|
||||
}
|
||||
|
||||
callback(sponsorTimes);
|
||||
}
|
||||
|
||||
function addSponsorTime(time, videoID, callback) {
|
||||
getSponsorTimes(videoID, function(sponsorTimes) {
|
||||
//add to sponsorTimes
|
||||
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) {
|
||||
//it is an end time
|
||||
sponsorTimes[sponsorTimes.length - 1][1] = time;
|
||||
} else {
|
||||
//it is a start time
|
||||
let sponsorTimesIndex = sponsorTimes.length;
|
||||
sponsorTimes[sponsorTimesIndex] = [];
|
||||
|
||||
sponsorTimes[sponsorTimesIndex][0] = time;
|
||||
}
|
||||
|
||||
//save this info
|
||||
Config.config.sponsorTimes.set(videoID, sponsorTimes);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
async function submitVote(type: number, UUID: string, category: string) {
|
||||
let userID = Config.config.userID;
|
||||
|
||||
|
||||
@@ -134,13 +134,6 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
||||
this.audio.volume = this.contentContainer().v.volume * 0.1;
|
||||
this.audio.play();
|
||||
}
|
||||
|
||||
if (Config.config.categoryUpdateShowCount < 3 && Config.config.categorySelections.length <= 1) {
|
||||
this.setNoticeInfoMessageWithOnClick(() => chrome.runtime.sendMessage({"message": "openConfig"})
|
||||
, chrome.i18n.getMessage("categoryUpdate1"), chrome.i18n.getMessage("categoryUpdate2"));
|
||||
|
||||
Config.config.categoryUpdateShowCount = Config.config.categoryUpdateShowCount + 1
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -230,7 +230,12 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
}
|
||||
|
||||
getCategoryOptions() {
|
||||
let elements = [];
|
||||
let elements = [(
|
||||
<option value={"chooseACategory"}
|
||||
key={"chooseACategory"}>
|
||||
{chrome.i18n.getMessage("chooseACategory")}
|
||||
</option>
|
||||
)];
|
||||
|
||||
for (const category of Config.config.categorySelections) {
|
||||
elements.push(
|
||||
@@ -322,7 +327,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
|
||||
sponsorTimesSubmitting[this.props.index].category = this.categoryOptionRef.current.value;
|
||||
|
||||
Config.config.sponsorTimes.set(this.props.contentContainer().sponsorVideoID, utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting));
|
||||
Config.config.segmentTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimesSubmitting);
|
||||
|
||||
this.props.contentContainer().updatePreviewBar();
|
||||
}
|
||||
@@ -354,7 +359,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
sponsorTimes.splice(index, 1);
|
||||
|
||||
//save this
|
||||
Config.config.sponsorTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimes);
|
||||
Config.config.segmentTimes.set(this.props.contentContainer().sponsorVideoID, sponsorTimes);
|
||||
|
||||
this.props.contentContainer().updatePreviewBar();
|
||||
|
||||
|
||||
@@ -167,9 +167,16 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
ref.current.saveEditTimes();
|
||||
}
|
||||
|
||||
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
for (const sponsorTime of sponsorTimesSubmitting) {
|
||||
if (sponsorTime.category === "chooseACategory") {
|
||||
alert(chrome.i18n.getMessage("youMustSelectACategory"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any non music categories are being used on a music video
|
||||
if (this.contentContainer().videoInfo?.microformat?.playerMicroformatRenderer?.category === "Music") {
|
||||
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
|
||||
for (const sponsorTime of sponsorTimesSubmitting) {
|
||||
if (!sponsorTime.category.startsWith("music_")) {
|
||||
if (!confirm(chrome.i18n.getMessage("nonMusicCategoryOnMusic"))) return;
|
||||
|
||||
191
src/config.ts
191
src/config.ts
@@ -1,12 +1,14 @@
|
||||
import * as CompileConfig from "../config.json";
|
||||
import { CategorySelection, CategorySkipOption, PreviewBarOption } from "./types";
|
||||
import { CategorySelection, CategorySkipOption, PreviewBarOption, SponsorTime } from "./types";
|
||||
|
||||
import Utils from "./utils";
|
||||
const utils = new Utils();
|
||||
|
||||
interface SBConfig {
|
||||
userID: string,
|
||||
sponsorTimes: SBMap<string, any>,
|
||||
// sponsorTimes: SBMap<string, SponsorTime[]>,
|
||||
segmentTimes: SBMap<string, SponsorTime[]>,
|
||||
defaultCategory: string,
|
||||
whitelistedChannels: string[],
|
||||
forceChannelCheck: boolean,
|
||||
startSponsorKeybind: string,
|
||||
@@ -15,6 +17,7 @@ interface SBConfig {
|
||||
skipCount: number,
|
||||
sponsorTimesContributed: number,
|
||||
submissionCountSinceCategories: number, // New count used to show the "Read The Guidelines!!" message
|
||||
showTimeWithSkips: boolean,
|
||||
unsubmittedWarning: boolean,
|
||||
disableSkipping: boolean,
|
||||
trackViewCount: boolean,
|
||||
@@ -33,13 +36,12 @@ interface SBConfig {
|
||||
checkForUnlistedVideos: boolean,
|
||||
testingServer: boolean,
|
||||
|
||||
categoryUpdateShowCount: number,
|
||||
|
||||
// What categories should be skipped
|
||||
categorySelections: CategorySelection[],
|
||||
|
||||
// Preview bar
|
||||
barTypes: {
|
||||
"preview-chooseACategory": PreviewBarOption,
|
||||
"sponsor": PreviewBarOption,
|
||||
"preview-sponsor": PreviewBarOption,
|
||||
"intro": PreviewBarOption,
|
||||
@@ -51,7 +53,7 @@ interface SBConfig {
|
||||
"selfpromo": PreviewBarOption,
|
||||
"preview-selfpromo": PreviewBarOption,
|
||||
"music_offtopic": PreviewBarOption,
|
||||
"preview-music_offtopic": PreviewBarOption
|
||||
"preview-music_offtopic": PreviewBarOption,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,24 +86,39 @@ class SBMap<T, U> extends Map {
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
const result = super.set(key, value);
|
||||
get(key): U {
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
rawSet(key, value) {
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
update() {
|
||||
// Store updated SBMap locally
|
||||
chrome.storage.sync.set({
|
||||
[this.id]: encodeStoredItem(this)
|
||||
});
|
||||
}
|
||||
|
||||
set(key: T, value: U) {
|
||||
const result = super.set(key, value);
|
||||
|
||||
this.update();
|
||||
return result;
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const result = super.delete(key);
|
||||
|
||||
// Store updated SBMap locally
|
||||
chrome.storage.sync.set({
|
||||
[this.id]: encodeStoredItem(this)
|
||||
});
|
||||
// Make sure there are no empty elements
|
||||
for (const entry of this.entries()) {
|
||||
if (entry[1].length === 0) {
|
||||
super.delete(entry[0]);
|
||||
}
|
||||
}
|
||||
|
||||
this.update();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -109,10 +126,7 @@ class SBMap<T, U> extends Map {
|
||||
clear() {
|
||||
const result = super.clear();
|
||||
|
||||
chrome.storage.sync.set({
|
||||
[this.id]: encodeStoredItem(this)
|
||||
});
|
||||
|
||||
this.update();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +138,8 @@ var Config: SBObject = {
|
||||
configListeners: [],
|
||||
defaults: {
|
||||
userID: null,
|
||||
sponsorTimes: new SBMap("sponsorTimes"),
|
||||
segmentTimes: new SBMap("segmentTimes"),
|
||||
defaultCategory: "chooseACategory",
|
||||
whitelistedChannels: [],
|
||||
forceChannelCheck: false,
|
||||
startSponsorKeybind: ";",
|
||||
@@ -133,6 +148,7 @@ var Config: SBObject = {
|
||||
skipCount: 0,
|
||||
sponsorTimesContributed: 0,
|
||||
submissionCountSinceCategories: 0,
|
||||
showTimeWithSkips: true,
|
||||
unsubmittedWarning: true,
|
||||
disableSkipping: false,
|
||||
trackViewCount: true,
|
||||
@@ -151,8 +167,6 @@ var Config: SBObject = {
|
||||
checkForUnlistedVideos: false,
|
||||
testingServer: false,
|
||||
|
||||
categoryUpdateShowCount: 0,
|
||||
|
||||
categorySelections: [{
|
||||
name: "sponsor",
|
||||
option: CategorySkipOption.AutoSkip
|
||||
@@ -160,6 +174,10 @@ var Config: SBObject = {
|
||||
|
||||
// Preview bar
|
||||
barTypes: {
|
||||
"preview-chooseACategory": {
|
||||
color: "#ffffff",
|
||||
opacity: "0.7"
|
||||
},
|
||||
"sponsor": {
|
||||
color: "#00d400",
|
||||
opacity: "0.7"
|
||||
@@ -238,24 +256,13 @@ function encodeStoredItem<T>(data: T): T | Array<any> {
|
||||
*
|
||||
* @param {*} data
|
||||
*/
|
||||
function decodeStoredItem<T>(id: string, data: T): T | SBMap<string, any> {
|
||||
function decodeStoredItem<T>(id: string, data: T): T | SBMap<string, SponsorTime[]> {
|
||||
if (!Config.defaults[id]) return data;
|
||||
|
||||
if (Config.defaults[id] instanceof SBMap) {
|
||||
try {
|
||||
let jsonData: any = data;
|
||||
|
||||
// Check if data is stored in the old format for SBMap (a JSON string)
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
jsonData = JSON.parse(data);
|
||||
} catch(e) {
|
||||
// Continue normally (out of this if statement)
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(jsonData)) return data;
|
||||
return new SBMap(id, jsonData);
|
||||
if (!Array.isArray(data)) return data;
|
||||
return new SBMap(id, data);
|
||||
} catch(e) {
|
||||
console.error("Failed to parse SBMap: " + id);
|
||||
}
|
||||
@@ -313,9 +320,9 @@ function fetchConfig() {
|
||||
});
|
||||
}
|
||||
|
||||
async function migrateOldFormats() {
|
||||
if (Config.config["disableAutoSkip"]) {
|
||||
for (const selection of Config.config.categorySelections) {
|
||||
function migrateOldFormats(config: SBConfig) {
|
||||
if (config["disableAutoSkip"]) {
|
||||
for (const selection of config.categorySelections) {
|
||||
if (selection.name === "sponsor") {
|
||||
selection.option = CategorySkipOption.ManualSkip;
|
||||
|
||||
@@ -325,62 +332,108 @@ async function migrateOldFormats() {
|
||||
}
|
||||
|
||||
// Auto vote removal
|
||||
if (Config.config["autoUpvote"]) {
|
||||
if (config["autoUpvote"]) {
|
||||
chrome.storage.sync.remove("autoUpvote");
|
||||
}
|
||||
|
||||
// mobileUpdateShowCount removal
|
||||
if (Config.config["mobileUpdateShowCount"] !== undefined) {
|
||||
if (config["mobileUpdateShowCount"] !== undefined) {
|
||||
chrome.storage.sync.remove("mobileUpdateShowCount");
|
||||
}
|
||||
// categoryUpdateShowCount removal
|
||||
if (config["categoryUpdateShowCount"] !== undefined) {
|
||||
chrome.storage.sync.remove("categoryUpdateShowCount");
|
||||
}
|
||||
|
||||
// Channel URLS
|
||||
if (Config.config.whitelistedChannels.length > 0 &&
|
||||
(Config.config.whitelistedChannels[0] == null || Config.config.whitelistedChannels[0].includes("/"))) {
|
||||
let newChannelList: string[] = [];
|
||||
for (const item of Config.config.whitelistedChannels) {
|
||||
if (item != null) {
|
||||
if (item.includes("/channel/")) {
|
||||
newChannelList.push(item.split("/")[2]);
|
||||
} else if (item.includes("/user/") && utils.isContentScript()) {
|
||||
// Replace channel URL with channelID
|
||||
let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
|
||||
|
||||
if (response.ok) {
|
||||
newChannelList.push((JSON.parse(response.responseText)).authorId);
|
||||
} else {
|
||||
// Add it at the beginning so it gets converted later
|
||||
if (config.whitelistedChannels.length > 0 &&
|
||||
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
|
||||
const channelURLFixer = async() => {
|
||||
let newChannelList: string[] = [];
|
||||
for (const item of config.whitelistedChannels) {
|
||||
if (item != null) {
|
||||
if (item.includes("/channel/")) {
|
||||
newChannelList.push(item.split("/")[2]);
|
||||
} else if (item.includes("/user/") && utils.isContentScript()) {
|
||||
|
||||
|
||||
// Replace channel URL with channelID
|
||||
let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
|
||||
|
||||
if (response.ok) {
|
||||
newChannelList.push((JSON.parse(response.responseText)).authorId);
|
||||
} else {
|
||||
// Add it at the beginning so it gets converted later
|
||||
newChannelList.unshift(item);
|
||||
}
|
||||
} else if (item.includes("/user/")) {
|
||||
// Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues)
|
||||
newChannelList.unshift(item);
|
||||
} else {
|
||||
newChannelList.push(item);
|
||||
}
|
||||
} else if (item.includes("/user/")) {
|
||||
// Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues)
|
||||
newChannelList.unshift(item);
|
||||
} else {
|
||||
newChannelList.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
config.whitelistedChannels = newChannelList;
|
||||
}
|
||||
|
||||
Config.config.whitelistedChannels = newChannelList;
|
||||
channelURLFixer();
|
||||
}
|
||||
|
||||
// Check if off-topic category needs to be removed
|
||||
for (let i = 0; i < Config.config.categorySelections.length; i++) {
|
||||
if (Config.config.categorySelections[i].name === "offtopic") {
|
||||
Config.config.categorySelections.splice(i, 1);
|
||||
for (let i = 0; i < config.categorySelections.length; i++) {
|
||||
if (config.categorySelections[i].name === "offtopic") {
|
||||
config.categorySelections.splice(i, 1);
|
||||
// Call set listener
|
||||
Config.config.categorySelections = Config.config.categorySelections;
|
||||
config.categorySelections = config.categorySelections;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate old "sponsorTimes"
|
||||
if (config["sponsorTimes"]) {
|
||||
let jsonData: any = config["sponsorTimes"];
|
||||
|
||||
// Check if data is stored in the old format for SBMap (a JSON string)
|
||||
if (typeof jsonData === "string") {
|
||||
try {
|
||||
jsonData = JSON.parse(jsonData);
|
||||
} catch(e) {
|
||||
// Continue normally (out of this if statement)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise junk data
|
||||
if (Array.isArray(jsonData)) {
|
||||
let oldMap = new Map(jsonData);
|
||||
oldMap.forEach((sponsorTimes: number[][], key) => {
|
||||
let segmentTimes: SponsorTime[] = [];
|
||||
for (const segment of sponsorTimes) {
|
||||
segmentTimes.push({
|
||||
segment: segment,
|
||||
category: "sponsor",
|
||||
UUID: null
|
||||
});
|
||||
}
|
||||
|
||||
config.segmentTimes.rawSet(key, segmentTimes);
|
||||
});
|
||||
|
||||
config.segmentTimes.update();
|
||||
}
|
||||
|
||||
chrome.storage.sync.remove("sponsorTimes");
|
||||
}
|
||||
}
|
||||
|
||||
async function setupConfig() {
|
||||
await fetchConfig();
|
||||
addDefaults();
|
||||
convertJSON();
|
||||
Config.config = configProxy();
|
||||
migrateOldFormats();
|
||||
const config = configProxy();
|
||||
migrateOldFormats(config);
|
||||
|
||||
Config.config = config;
|
||||
}
|
||||
|
||||
// Reset config
|
||||
@@ -399,6 +452,12 @@ function addDefaults() {
|
||||
for (const key in Config.defaults) {
|
||||
if(!Config.localConfig.hasOwnProperty(key)) {
|
||||
Config.localConfig[key] = Config.defaults[key];
|
||||
} else if (key === "barTypes") {
|
||||
for (const key2 in Config.defaults[key]) {
|
||||
if(!Config.localConfig[key].hasOwnProperty(key2)) {
|
||||
Config.localConfig[key][key2] = Config.defaults[key][key2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -406,4 +465,4 @@ function addDefaults() {
|
||||
// Sync config
|
||||
setupConfig();
|
||||
|
||||
export default Config;
|
||||
export default Config;
|
||||
|
||||
147
src/content.ts
147
src/content.ts
@@ -323,13 +323,13 @@ async function videoIDChange(id) {
|
||||
//warn them if they had unsubmitted times
|
||||
if (previousVideoID != null) {
|
||||
//get the sponsor times from storage
|
||||
let sponsorTimes = Config.config.sponsorTimes.get(previousVideoID);
|
||||
let sponsorTimes = Config.config.segmentTimes.get(previousVideoID);
|
||||
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
|
||||
//warn them that they have unsubmitted sponsor times
|
||||
chrome.runtime.sendMessage({
|
||||
message: "alertPrevious",
|
||||
previousVideoID: previousVideoID
|
||||
})
|
||||
chrome.runtime.sendMessage({
|
||||
message: "alertPrevious",
|
||||
previousVideoID: previousVideoID
|
||||
});
|
||||
}
|
||||
|
||||
//set the previous video id to the currentID
|
||||
@@ -347,10 +347,10 @@ async function videoIDChange(id) {
|
||||
//make sure everything is properly added
|
||||
updateVisibilityOfPlayerControlsButton().then(() => {
|
||||
//see if the onvideo control image needs to be changed
|
||||
let segments = Config.config.sponsorTimes.get(sponsorVideoID);
|
||||
if (segments != null && segments.length > 0 && segments[segments.length - 1].length >= 2) {
|
||||
let segments = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
|
||||
changeStartSponsorButton(true, true);
|
||||
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].length < 2) {
|
||||
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) {
|
||||
changeStartSponsorButton(false, true);
|
||||
} else {
|
||||
changeStartSponsorButton(true, false);
|
||||
@@ -418,7 +418,7 @@ function createPreviewBar(): void {
|
||||
const el = document.querySelectorAll(selector);
|
||||
|
||||
if (el && el.length && el[0]) {
|
||||
previewBar = new PreviewBar(el[0], onMobileYouTube);
|
||||
previewBar = new PreviewBar(el[0], onMobileYouTube, onInvidious);
|
||||
|
||||
updatePreviewBar();
|
||||
|
||||
@@ -493,7 +493,8 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
|
||||
}
|
||||
|
||||
// Don't skip if this category should not be skipped
|
||||
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ShowOverlay) return;
|
||||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ShowOverlay
|
||||
&& skipInfo.array !== sponsorTimesSubmitting) return;
|
||||
|
||||
let skippingFunction = () => {
|
||||
let forcedSkipTime: number = null;
|
||||
@@ -504,8 +505,7 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
|
||||
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
|
||||
skipToTime(video, skipTime, skippingSegments, skipInfo.openNotice);
|
||||
|
||||
// TODO: Know the autoSkip settings for ALL items being skipped
|
||||
if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ManualSkip) {
|
||||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ManualSkip) {
|
||||
forcedSkipTime = skipTime[0] + 0.001;
|
||||
} else {
|
||||
forcedSkipTime = skipTime[1];
|
||||
@@ -618,7 +618,7 @@ function sponsorsLookup(id: string) {
|
||||
videoID: id,
|
||||
categories
|
||||
}).then(async (response: FetchResponse) => {
|
||||
if (response.ok) {
|
||||
if (response?.ok) {
|
||||
let recievedSegments: SponsorTime[] = JSON.parse(response.responseText);
|
||||
if (!recievedSegments.length) {
|
||||
console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments));
|
||||
@@ -662,7 +662,7 @@ function sponsorsLookup(id: string) {
|
||||
}
|
||||
|
||||
sponsorLookupRetries = 0;
|
||||
} else if (response.status === 404) {
|
||||
} else if (response?.status === 404) {
|
||||
sponsorDataFound = false;
|
||||
|
||||
//check if this video was uploaded recently
|
||||
@@ -801,7 +801,7 @@ function updatePreviewBar() {
|
||||
if (localSponsorTimes == null) localSponsorTimes = [];
|
||||
|
||||
let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
|
||||
|
||||
|
||||
//create an array of the sponsor types
|
||||
let types = [];
|
||||
for (let i = 0; i < localSponsorTimes.length; i++) {
|
||||
@@ -818,6 +818,10 @@ function updatePreviewBar() {
|
||||
|
||||
previewBar.set(utils.getSegmentsFromSponsorTimes(allSponsorTimes), types, video.duration)
|
||||
|
||||
if (Config.config.showTimeWithSkips) {
|
||||
showTimeWithoutSkips(allSponsorTimes);
|
||||
}
|
||||
|
||||
//update last video id
|
||||
lastPreviewBarUpdate = sponsorVideoID;
|
||||
}
|
||||
@@ -893,13 +897,13 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
||||
*/
|
||||
function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideHiddenSponsors: boolean = true): number {
|
||||
// Only combine segments for AutoSkip
|
||||
if (index == -1 ||
|
||||
utils.getCategorySelection(sponsorTimes[index].category).option !== CategorySkipOption.AutoSkip) return index;
|
||||
if (index == -1 ||
|
||||
utils.getCategorySelection(sponsorTimes[index].category)?.option !== CategorySkipOption.AutoSkip) return index;
|
||||
|
||||
// Default to the normal endTime
|
||||
let latestEndTimeIndex = index;
|
||||
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||
let currentSegment = sponsorTimes[i].segment;
|
||||
let latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
|
||||
|
||||
@@ -935,7 +939,7 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|
||||
|
||||
let startTimes: number[] = [];
|
||||
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||
if ((minimum === undefined || (sponsorTimes[i].segment[0] >= minimum || (includeIntersectingSegments && sponsorTimes[i].segment[1] > minimum)))
|
||||
&& (!onlySkippableSponsors || utils.getCategorySelection(sponsorTimes[i].category).option !== CategorySkipOption.ShowOverlay)
|
||||
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)) {
|
||||
@@ -964,7 +968,7 @@ function previewTime(time: number) {
|
||||
//skip from the start time to the end time for a certain index sponsor time
|
||||
function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
|
||||
// There will only be one submission if it is manual skip
|
||||
let autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category).option === CategorySkipOption.AutoSkip;
|
||||
let autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category)?.option === CategorySkipOption.AutoSkip;
|
||||
|
||||
if (autoSkip || sponsorTimesSubmitting.includes(skippingSegments[0])) {
|
||||
v.currentTime = skipTime[1];
|
||||
@@ -1007,8 +1011,6 @@ function unskipSponsorTime(segment: SponsorTime) {
|
||||
if (sponsorTimes != null) {
|
||||
//add a tiny bit of time to make sure it is not skipped again
|
||||
video.currentTime = segment.segment[0] + 0.001;
|
||||
|
||||
checkIfInsideSegment();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,16 +1018,6 @@ function reskipSponsorTime(segment: SponsorTime) {
|
||||
video.currentTime = segment.segment[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if currently inside a segment and will trigger
|
||||
* a skip schedule if true.
|
||||
*
|
||||
* This is used for when a manual skip is finished or a reskip is complete
|
||||
*/
|
||||
function checkIfInsideSegment() {
|
||||
// for
|
||||
}
|
||||
|
||||
function createButton(baseID, title, callback, imageName, isDraggable=false): boolean {
|
||||
if (document.getElementById(baseID + "Button") != null) return false;
|
||||
|
||||
@@ -1034,18 +1026,10 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
|
||||
newButton.draggable = isDraggable;
|
||||
newButton.id = baseID + "Button";
|
||||
newButton.classList.add("playerButton");
|
||||
if (!onMobileYouTube) {
|
||||
newButton.classList.add("ytp-button");
|
||||
} else {
|
||||
newButton.classList.add("icon-button");
|
||||
newButton.style.padding = "0";
|
||||
}
|
||||
newButton.classList.add("ytp-button");
|
||||
newButton.setAttribute("title", chrome.i18n.getMessage(title));
|
||||
newButton.addEventListener("click", (event: Event) => {
|
||||
callback();
|
||||
|
||||
// Prevents the contols from closing when clicked
|
||||
if (onMobileYouTube) event.stopPropagation();
|
||||
});
|
||||
|
||||
// Image HTML
|
||||
@@ -1087,6 +1071,8 @@ function getControls(): HTMLElement | boolean {
|
||||
|
||||
//adds all the player controls buttons
|
||||
async function createButtons(): Promise<boolean> {
|
||||
if (onMobileYouTube) return;
|
||||
|
||||
let result = await utils.wait(getControls).catch();
|
||||
|
||||
//set global controls variable
|
||||
@@ -1164,36 +1150,28 @@ function startSponsorClicked() {
|
||||
sponsorTimesSubmitting.push({
|
||||
segment: [getRealCurrentTime()],
|
||||
UUID: null,
|
||||
// Default to sponsor
|
||||
category: "sponsor"
|
||||
category: Config.config.defaultCategory
|
||||
});
|
||||
}
|
||||
|
||||
// Create raw segment list
|
||||
let segments: number[][] = [];
|
||||
for (const sponsorTime of sponsorTimesSubmitting) {
|
||||
segments.push(sponsorTime.segment);
|
||||
}
|
||||
|
||||
//save this info
|
||||
Config.config.sponsorTimes.set(sponsorVideoID, segments);
|
||||
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
|
||||
|
||||
updateSponsorTimesSubmitting(false)
|
||||
}
|
||||
|
||||
function updateSponsorTimesSubmitting(getFromConfig: boolean = true) {
|
||||
let segments = Config.config.sponsorTimes.get(sponsorVideoID);
|
||||
let segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
|
||||
|
||||
//see if this data should be saved in the sponsorTimesSubmitting variable
|
||||
if (getFromConfig && segments != undefined) {
|
||||
if (getFromConfig && segmentTimes != undefined) {
|
||||
sponsorTimesSubmitting = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
for (const segmentTime of segmentTimes) {
|
||||
sponsorTimesSubmitting.push({
|
||||
segment: segment,
|
||||
segment: segmentTime.segment,
|
||||
UUID: null,
|
||||
// Default to sponsor
|
||||
category: "sponsor"
|
||||
category: segmentTime.category
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1220,9 +1198,9 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
|
||||
(<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
|
||||
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
|
||||
|
||||
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible && !Config.config.hideUploadButtonPlayerControls) {
|
||||
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible && !Config.config.hideUploadButtonPlayerControls && !onInvidious) {
|
||||
document.getElementById("submitButton").style.display = "unset";
|
||||
} else if (!uploadButtonVisible) {
|
||||
} else if (!uploadButtonVisible || onInvidious) {
|
||||
//disable submit button
|
||||
document.getElementById("submitButton").style.display = "none";
|
||||
}
|
||||
@@ -1317,7 +1295,7 @@ function clearSponsorTimes() {
|
||||
|
||||
let currentVideoID = sponsorVideoID;
|
||||
|
||||
let sponsorTimes = Config.config.sponsorTimes.get(currentVideoID);
|
||||
let sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
|
||||
|
||||
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
|
||||
let confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes)
|
||||
@@ -1325,7 +1303,7 @@ function clearSponsorTimes() {
|
||||
if(!confirm(confirmMessage)) return;
|
||||
|
||||
//clear the sponsor times
|
||||
Config.config.sponsorTimes.delete(currentVideoID);
|
||||
Config.config.segmentTimes.delete(currentVideoID);
|
||||
|
||||
//clear sponsor times submitting
|
||||
sponsorTimesSubmitting = [];
|
||||
@@ -1447,14 +1425,14 @@ async function sendSubmitMessage(){
|
||||
}
|
||||
|
||||
//update sponsorTimes
|
||||
Config.config.sponsorTimes.set(sponsorVideoID, utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting));
|
||||
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
|
||||
|
||||
// Check to see if any of the submissions are below the minimum duration set
|
||||
if (Config.config.minDuration > 0) {
|
||||
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
||||
if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration) {
|
||||
let confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||
getSegmentsMessage(utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting));
|
||||
getSegmentsMessage(sponsorTimesSubmitting);
|
||||
|
||||
if(!confirm(confirmShort)) return;
|
||||
}
|
||||
@@ -1484,7 +1462,7 @@ async function sendSubmitMessage(){
|
||||
submitButton.addEventListener("animationend", animationEndListener);
|
||||
|
||||
//clear the sponsor times
|
||||
Config.config.sponsorTimes.delete(sponsorVideoID);
|
||||
Config.config.segmentTimes.delete(sponsorVideoID);
|
||||
|
||||
//add submissions to current sponsors list
|
||||
if (sponsorTimes === null) sponsorTimes = [];
|
||||
@@ -1512,12 +1490,12 @@ async function sendSubmitMessage(){
|
||||
}
|
||||
|
||||
//get the message that visually displays the video times
|
||||
function getSegmentsMessage(segments: number[][]): string {
|
||||
function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
|
||||
let sponsorTimesMessage = "";
|
||||
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
for (let s = 0; s < segments[i].length; s++) {
|
||||
let timeMessage = utils.getFormattedTime(segments[i][s]);
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
for (let s = 0; s < sponsorTimes[i].segment.length; s++) {
|
||||
let timeMessage = utils.getFormattedTime(sponsorTimes[i].segment[s]);
|
||||
//if this is an end time
|
||||
if (s == 1) {
|
||||
timeMessage = " to " + timeMessage;
|
||||
@@ -1593,3 +1571,38 @@ function updateAdFlag() {
|
||||
updateVisibilityOfPlayerControlsButton();
|
||||
}
|
||||
}
|
||||
|
||||
function showTimeWithoutSkips(allSponsorTimes): void {
|
||||
if (onMobileYouTube || onInvidious) return;
|
||||
|
||||
let skipDuration = 0;
|
||||
|
||||
// Calculate skipDuration based from the segments in the preview bar
|
||||
for (let i = 0; i < allSponsorTimes.length; i++) {
|
||||
// If an end time exists
|
||||
if (allSponsorTimes[i].segment[1]) {
|
||||
skipDuration += allSponsorTimes[i].segment[1] - allSponsorTimes[i].segment[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// YouTube player time display
|
||||
let display = document.getElementsByClassName("ytp-time-display notranslate")[0];
|
||||
if (!display) return;
|
||||
|
||||
let formatedTime = utils.getFormattedTime(video.duration - skipDuration);
|
||||
|
||||
const durationID = "sponsorBlockDurationAfterSkips";
|
||||
let duration = document.getElementById(durationID);
|
||||
|
||||
// Create span if needed
|
||||
if(duration === null) {
|
||||
duration = document.createElement('span');
|
||||
duration.id = durationID;
|
||||
duration.classList.add("ytp-time-duration");
|
||||
|
||||
display.appendChild(duration);
|
||||
}
|
||||
|
||||
duration.innerText = (skipDuration <= 0 || isNaN(skipDuration)) ? "" : " ("+formatedTime+")";
|
||||
}
|
||||
|
||||
@@ -6,21 +6,25 @@
|
||||
'use strict';
|
||||
|
||||
import Config from "../config";
|
||||
import Utils from "../utils";
|
||||
let utils = new Utils();
|
||||
|
||||
class PreviewBar {
|
||||
container: HTMLUListElement;
|
||||
parent: any;
|
||||
onMobileYouTube: boolean;
|
||||
onInvidious: boolean;
|
||||
|
||||
timestamps: number[][];
|
||||
types: string;
|
||||
|
||||
constructor(parent, onMobileYouTube) {
|
||||
constructor(parent, onMobileYouTube, onInvidious) {
|
||||
this.container = document.createElement('ul');
|
||||
this.container.id = 'previewbar';
|
||||
this.parent = parent;
|
||||
|
||||
this.onMobileYouTube = onMobileYouTube;
|
||||
this.onInvidious = onInvidious;
|
||||
|
||||
this.updatePosition(parent);
|
||||
|
||||
@@ -28,6 +32,8 @@ class PreviewBar {
|
||||
}
|
||||
|
||||
setupHoverText() {
|
||||
if (this.onMobileYouTube || this.onInvidious) return;
|
||||
|
||||
let seekBar = document.querySelector(".ytp-progress-bar-container");
|
||||
|
||||
// Create label placeholder
|
||||
@@ -79,8 +85,8 @@ class PreviewBar {
|
||||
tooltipTextWrapper.classList.remove("sbTooltipOneTitleThumbnailOffset");
|
||||
} else if (category !== null) {
|
||||
categoryTooltip.classList.remove("sbHidden");
|
||||
categoryTooltip.textContent = chrome.i18n.getMessage("category_" + category)
|
||||
|| (chrome.i18n.getMessage("preview") + " " + chrome.i18n.getMessage("category_" + category.split("preview-")[1]));
|
||||
categoryTooltip.textContent = utils.shortCategoryName(category)
|
||||
|| (chrome.i18n.getMessage("preview") + " " + utils.shortCategoryName(category.split("preview-")[1]));
|
||||
|
||||
// There is a title now
|
||||
tooltip.classList.remove("ytp-tooltip-text-no-title");
|
||||
|
||||
@@ -237,7 +237,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
|
||||
|
||||
let setButton = element.querySelector(".text-change-set");
|
||||
setButton.addEventListener("click", async function(e) {
|
||||
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http") || textBox.value.includes(":")) {
|
||||
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http")) {
|
||||
alert(chrome.i18n.getMessage("addInvidiousInstanceError"));
|
||||
} else {
|
||||
// Add this
|
||||
@@ -298,19 +298,23 @@ function invidiousInit(checkbox: HTMLInputElement, option: string) {
|
||||
* @param checkbox
|
||||
* @param option
|
||||
*/
|
||||
function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
|
||||
if (checkbox.checked) {
|
||||
utils.setupExtraSitePermissions(function (granted) {
|
||||
if (!granted) {
|
||||
Config.config[option] = false;
|
||||
checkbox.checked = false;
|
||||
} else {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
utils.removeExtraSiteRegistration();
|
||||
}
|
||||
async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
|
||||
return new Promise((resolve) => {
|
||||
if (checkbox.checked) {
|
||||
utils.setupExtraSitePermissions(function (granted) {
|
||||
if (!granted) {
|
||||
Config.config[option] = false;
|
||||
checkbox.checked = false;
|
||||
} else {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
utils.removeExtraSiteRegistration();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,15 +362,6 @@ function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
|
||||
let button: HTMLElement = element.querySelector(".trigger-button");
|
||||
let option = element.getAttribute("sync-option");
|
||||
|
||||
// Don't allow keys which are already listened for by youtube
|
||||
let restrictedKeys = "1234567890,.jklftcibmJKLFTCIBMNP/<> -+";
|
||||
if (restrictedKeys.indexOf(key) !== -1 ) {
|
||||
closeKeybindOption(element, button);
|
||||
|
||||
alert(chrome.i18n.getMessage("theKey") + " " + key + " " + chrome.i18n.getMessage("keyAlreadyUsedByYouTube"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure keybind isn't used by the other listener
|
||||
// TODO: If other keybindings are going to be added, we need a better way to find the other keys used.
|
||||
let otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
|
||||
@@ -435,8 +430,8 @@ function activatePrivateTextChange(element: HTMLElement) {
|
||||
case "*":
|
||||
let jsonData = JSON.parse(JSON.stringify(Config.localConfig));
|
||||
|
||||
// Fix sponsorTimes data as it is destroyed from the JSON stringify
|
||||
jsonData.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes);
|
||||
// Fix segmentTimes data as it is destroyed from the JSON stringify
|
||||
jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes);
|
||||
|
||||
result = JSON.stringify(jsonData);
|
||||
break;
|
||||
@@ -445,7 +440,7 @@ function activatePrivateTextChange(element: HTMLElement) {
|
||||
textBox.value = result;
|
||||
|
||||
let setButton = element.querySelector(".text-change-set");
|
||||
setButton.addEventListener("click", () => {
|
||||
setButton.addEventListener("click", async () => {
|
||||
let confirmMessage = element.getAttribute("confirm-message");
|
||||
|
||||
if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) {
|
||||
@@ -460,15 +455,14 @@ function activatePrivateTextChange(element: HTMLElement) {
|
||||
}
|
||||
Config.convertJSON();
|
||||
|
||||
// Reload options on page
|
||||
init();
|
||||
|
||||
if (newConfig.supportInvidious) {
|
||||
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
|
||||
|
||||
checkbox.checked = true;
|
||||
invidiousOnClick(checkbox, "supportInvidious");
|
||||
await invidiousOnClick(checkbox, "supportInvidious");
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
|
||||
} catch (e) {
|
||||
alert(chrome.i18n.getMessage("incorrectlyFormattedOptions"));
|
||||
@@ -519,8 +513,8 @@ function copyDebugOutputToClipboard() {
|
||||
config: JSON.parse(JSON.stringify(Config.localConfig)) // Deep clone config object
|
||||
};
|
||||
|
||||
// Fix sponsorTimes data as it is destroyed from the JSON stringify
|
||||
output.config.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes);
|
||||
// Fix segmentTimes data as it is destroyed from the JSON stringify
|
||||
output.config.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes);
|
||||
|
||||
// Sanitise sensitive user config values
|
||||
delete output.config.userID;
|
||||
|
||||
38
src/popup.ts
38
src/popup.ts
@@ -119,7 +119,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
let startTimeChosen = false;
|
||||
|
||||
//the start and end time pairs (2d)
|
||||
let sponsorTimes = [];
|
||||
let sponsorTimes: SponsorTime[] = [];
|
||||
|
||||
//current video ID of this tab
|
||||
let currentVideoID = null;
|
||||
@@ -252,9 +252,9 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
}
|
||||
|
||||
//load video times for this video
|
||||
let sponsorTimesStorage = Config.config.sponsorTimes.get(currentVideoID);
|
||||
let sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
|
||||
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
||||
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) {
|
||||
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
|
||||
startTimeChosen = true;
|
||||
PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
|
||||
}
|
||||
@@ -336,13 +336,17 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
|
||||
|
||||
if (sponsorTimes[sponsorTimesIndex] == undefined) {
|
||||
sponsorTimes[sponsorTimesIndex] = [];
|
||||
sponsorTimes[sponsorTimesIndex] = {
|
||||
segment: [],
|
||||
category: Config.config.defaultCategory,
|
||||
UUID: null
|
||||
};
|
||||
}
|
||||
|
||||
sponsorTimes[sponsorTimesIndex][startTimeChosen ? 1 : 0] = response.time;
|
||||
sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
|
||||
|
||||
let localStartTimeChosen = startTimeChosen;
|
||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
//send a message to the client script
|
||||
if (localStartTimeChosen) {
|
||||
@@ -528,7 +532,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
}
|
||||
|
||||
function previewSponsorTime(index) {
|
||||
let skipTime = sponsorTimes[index][0];
|
||||
let skipTime = sponsorTimes[index].segment[0];
|
||||
|
||||
if (document.getElementById("startTimeMinutes" + index) != null) {
|
||||
//edit is currently open, use that time
|
||||
@@ -575,28 +579,28 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
startTimeMinutes.id = "startTimeMinutes" + index;
|
||||
startTimeMinutes.className = "sponsorTime popupElement";
|
||||
startTimeMinutes.type = "text";
|
||||
startTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][0]));
|
||||
startTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index].segment[0]));
|
||||
startTimeMinutes.style.width = "45px";
|
||||
|
||||
let startTimeSeconds = document.createElement("input");
|
||||
startTimeSeconds.id = "startTimeSeconds" + index;
|
||||
startTimeSeconds.className = "sponsorTime popupElement";
|
||||
startTimeSeconds.type = "text";
|
||||
startTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][0]);
|
||||
startTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index].segment[0]);
|
||||
startTimeSeconds.style.width = "60px";
|
||||
|
||||
let endTimeMinutes = document.createElement("input");
|
||||
endTimeMinutes.id = "endTimeMinutes" + index;
|
||||
endTimeMinutes.className = "sponsorTime popupElement";
|
||||
endTimeMinutes.type = "text";
|
||||
endTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][1]));
|
||||
endTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index].segment[1]));
|
||||
endTimeMinutes.style.width = "45px";
|
||||
|
||||
let endTimeSeconds = document.createElement("input");
|
||||
endTimeSeconds.id = "endTimeSeconds" + index;
|
||||
endTimeSeconds.className = "sponsorTime popupElement";
|
||||
endTimeSeconds.type = "text";
|
||||
endTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][1]);
|
||||
endTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index].segment[1]);
|
||||
endTimeSeconds.style.width = "60px";
|
||||
|
||||
//the button to set the current time
|
||||
@@ -668,11 +672,11 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
}
|
||||
|
||||
function saveSponsorTimeEdit(index, closeEditMode = true) {
|
||||
sponsorTimes[index][0] = getSponsorTimeEditTimes("startTime", index);
|
||||
sponsorTimes[index][1] = getSponsorTimeEditTimes("endTime", index);
|
||||
sponsorTimes[index].segment[0] = getSponsorTimeEditTimes("startTime", index);
|
||||
sponsorTimes[index].segment[1] = getSponsorTimeEditTimes("endTime", index);
|
||||
|
||||
//save this
|
||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
messageHandler.query({
|
||||
active: true,
|
||||
@@ -692,7 +696,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
//deletes the sponsor time submitted at an index
|
||||
function deleteSponsorTime(index) {
|
||||
//if it is not a complete sponsor time
|
||||
if (sponsorTimes[index].length < 2) {
|
||||
if (sponsorTimes[index].segment.length < 2) {
|
||||
messageHandler.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
@@ -710,7 +714,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
sponsorTimes.splice(index, 1);
|
||||
|
||||
//save this
|
||||
Config.config.sponsorTimes.set(currentVideoID, sponsorTimes);
|
||||
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
//if they are all removed
|
||||
if (sponsorTimes.length == 0) {
|
||||
@@ -780,7 +784,7 @@ async function runThePopup(messageListener?: MessageListener) {
|
||||
//hides and shows the submit times button when needed
|
||||
function showSubmitTimesIfNecessary() {
|
||||
//check if an end time has been specified for the latest sponsor time
|
||||
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length > 1) {
|
||||
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].segment.length > 1) {
|
||||
//show submit times button
|
||||
document.getElementById("submitTimesContainer").style.display = "unset";
|
||||
} else {
|
||||
|
||||
20
src/utils.ts
20
src/utils.ts
@@ -331,25 +331,35 @@ class Utils {
|
||||
return seconds % 60;
|
||||
}
|
||||
|
||||
getFormattedTime(seconds: number, precise?: boolean) {
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
let secondsNum: number = seconds - minutes * 60;
|
||||
getFormattedTime(seconds: number, precise?: boolean): string {
|
||||
let hours = Math.floor(seconds / 60 / 60);
|
||||
let minutes = Math.floor(seconds / 60) % 60;
|
||||
let minutesDisplay = String(minutes);
|
||||
let secondsNum = seconds % 60;
|
||||
if (!precise) {
|
||||
secondsNum = Math.floor(secondsNum);
|
||||
}
|
||||
|
||||
let secondsDisplay: string = String(secondsNum.toFixed(3));
|
||||
let secondsDisplay: string = 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;
|
||||
}
|
||||
|
||||
let formatted = minutes + ":" + secondsDisplay;
|
||||
let formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
shortCategoryName(categoryName: string): string {
|
||||
return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName);
|
||||
}
|
||||
|
||||
getRawSeconds(minutes: number, seconds: number): number {
|
||||
return minutes * 60 + seconds;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user