Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into chapters

This commit is contained in:
Ajay 2022-01-21 20:18:42 -05:00
parent e2d56d32fe
commit 46983bec24
27 changed files with 450 additions and 130 deletions

View file

@ -13,7 +13,7 @@
"preview": ["skip", "mute"], "preview": ["skip", "mute"],
"filler": ["skip", "mute"], "filler": ["skip", "mute"],
"music_offtopic": ["skip"], "music_offtopic": ["skip"],
"poi_highlight": ["skip"], "poi_highlight": ["poi"],
"chapter": ["chapter"] "chapter": ["chapter"]
}, },
"wikiLinks": { "wikiLinks": {

View file

@ -1,7 +1,7 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "SponsorBlock", "short_name": "SponsorBlock",
"version": "4.0.3", "version": "4.0.5",
"default_locale": "en", "default_locale": "en",
"description": "__MSG_Description__", "description": "__MSG_Description__",
"homepage_url": "https://sponsor.ajay.app", "homepage_url": "https://sponsor.ajay.app",

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Следното предложение е по-кратко от опцията за минимална продължителност. Това може да означава, че вече е изпратено и просто е игнорирано поради тази опция. Наистина ли искате да го изпратите?" "message": "Следното предложение е по-кратко от опцията за минимална продължителност. Това може да означава, че вече е изпратено и просто е игнорирано поради тази опция. Наистина ли искате да го изпратите?"
}, },
"liveOrPremiere": {
"message": "Не е разрешено изпращането по време на активен поток на живо или премиера. Моля, изчакайте, докато приключи, след което опреснете страницата и проверете дали сегментите са все още валидни."
},
"showUploadButton": { "showUploadButton": {
"message": "Показване на бутона за качване" "message": "Показване на бутона за качване"
}, },

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Váš příspěvek je kratší než vaše možnost nejkratší doby trvání. To by mohlo znamenat, že někdo segment již odeslal, a je jenom ignorován kvůli této možnosti. Opravdu chcete odeslat váš příspěvek?" "message": "Váš příspěvek je kratší než vaše možnost nejkratší doby trvání. To by mohlo znamenat, že někdo segment již odeslal, a je jenom ignorován kvůli této možnosti. Opravdu chcete odeslat váš příspěvek?"
}, },
"liveOrPremiere": {
"message": "Odesílání na aktivním streamu nebo premiéře. Počkejte prosím, než skončí, poté obnovte stránku a ověřte, zda jsou segmenty stále platné."
},
"showUploadButton": { "showUploadButton": {
"message": "Zobrazit tlačítko Nahrát" "message": "Zobrazit tlačítko Nahrát"
}, },

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Die folgende Einreichung ist kürzer als deine Mindestdauer. Das könnte bedeuten, dass dieses Videosegment bereits eingereicht wurde und aufgrund dieser Option einfach ignoriert wird. Bist du dir sicher, dass du es übermitteln möchtest?" "message": "Die folgende Einreichung ist kürzer als deine Mindestdauer. Das könnte bedeuten, dass dieses Videosegment bereits eingereicht wurde und aufgrund dieser Option einfach ignoriert wird. Bist du dir sicher, dass du es übermitteln möchtest?"
}, },
"liveOrPremiere": {
"message": "Das Einreichen eines aktiven Livestreams oder Premiere ist nicht erlaubt. Bitte warte bis es zu ende ist, lade die Seite neu und überprüfe, dass die Segmente noch immer korrekt sind."
},
"showUploadButton": { "showUploadButton": {
"message": "Upload-Knopf anzeigen" "message": "Upload-Knopf anzeigen"
}, },
@ -544,7 +547,7 @@
"message": "Nur für das Kennzeichnen ganzer Videos. Wird verwendet wenn ein Video ein Produkt, eine Dienstleistung oder einen Ort präsentiert, zu welchem sie freien oder subventionierten Zugriff erhalten haben." "message": "Nur für das Kennzeichnen ganzer Videos. Wird verwendet wenn ein Video ein Produkt, eine Dienstleistung oder einen Ort präsentiert, zu welchem sie freien oder subventionierten Zugriff erhalten haben."
}, },
"category_exclusive_access_pill": { "category_exclusive_access_pill": {
"message": "Dieses Video präsentiert ein Produkt, eine Dienstleistung oder einen Ort, zu welchem sie freien oder subventionierten Zigriff erhalten haben", "message": "Dieses Video präsentiert ein Produkt, eine Dienstleistung oder einen Ort, zu welchem sie freien oder subventionierten Zugriff erhalten haben",
"description": "Short description for this category" "description": "Short description for this category"
}, },
"category_interaction": { "category_interaction": {
@ -638,7 +641,7 @@
"message": "Segmente zulassen, die den Ton ausschalten anstatt zu überspringen" "message": "Segmente zulassen, die den Ton ausschalten anstatt zu überspringen"
}, },
"fullVideoSegments": { "fullVideoSegments": {
"message": "Zeige an, wenn das gesamte Video Werbung ist", "message": "Zeige ein Icon, wenn ein ganzes Video Werbung ist",
"description": "Referring to the category pill that is now shown on videos that are entirely sponsor or entirely selfpromo" "description": "Referring to the category pill that is now shown on videos that are entirely sponsor or entirely selfpromo"
}, },
"colorFormatIncorrect": { "colorFormatIncorrect": {

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "El siguiente envío es más corto que su opción de duración mínima. Esto podría significar que esto ya se ha enviado y que simplemente se ha ignorado debido a esta opción. ¿Está seguro de que desea enviar?" "message": "El siguiente envío es más corto que su opción de duración mínima. Esto podría significar que esto ya se ha enviado y que simplemente se ha ignorado debido a esta opción. ¿Está seguro de que desea enviar?"
}, },
"liveOrPremiere": {
"message": "No se permite enviar en una transmisión en vivo activa o estreno. Espere hasta que finalice, luego actualice la página y verifique que los segmentos aún sean válidos."
},
"showUploadButton": { "showUploadButton": {
"message": "Mostrar botón de subida" "message": "Mostrar botón de subida"
}, },
@ -537,6 +540,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Similar a \"sponsor\", excepto que para la promoción propia o no remunerada. Esto incluye secciones sobre mercancía, donaciones o información sobre con quiénes colaboraron." "message": "Similar a \"sponsor\", excepto que para la promoción propia o no remunerada. Esto incluye secciones sobre mercancía, donaciones o información sobre con quiénes colaboraron."
}, },
"category_exclusive_access": {
"message": "Acceso Exclusivo"
},
"category_exclusive_access_description": {
"message": "Solo para etiquetar videos completos. Utilizado cuando un video exhibe un producto, servicio o ubicación al que han recibido acceso gratuito o subsidiado."
},
"category_exclusive_access_pill": {
"message": "Este video exhibe un producto, servicio o ubicación al que han recibido acceso gratuito o subsidiado",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Recordatorio de interacción (subscribir)" "message": "Recordatorio de interacción (subscribir)"
}, },
@ -618,6 +631,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Mostrar en la barra de búsqueda" "message": "Mostrar en la barra de búsqueda"
}, },
"showOverlay_full": {
"message": "Mostrar Etiqueta"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Omitir automáticamente todos los segmentos cuando hay un segmento sin música" "message": "Omitir automáticamente todos los segmentos cuando hay un segmento sin música"
}, },

View file

@ -537,6 +537,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Sarnaneb \"sponsorile\", ent on mõeldud tasumata või enesepromo jaoks. Selle alla kuuluvad jaotised oma müüdava kauba, annetuste ja koostööpartnerite kohta." "message": "Sarnaneb \"sponsorile\", ent on mõeldud tasumata või enesepromo jaoks. Selle alla kuuluvad jaotised oma müüdava kauba, annetuste ja koostööpartnerite kohta."
}, },
"category_exclusive_access": {
"message": "Eksklusiivne ligipääs"
},
"category_exclusive_access_description": {
"message": "Ainult tervete videote märkimiseks. Kasutatakse, kui video esitleb toodet, teenust või asukohta, millele isik on tasuta või toetusega ligipääsu saanud."
},
"category_exclusive_access_pill": {
"message": "See video esitleb toodet, teenust või asukohta, millele isik on tasuta või toetusega ligipääsu saanud",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Tegutsemise meeldetuletus (kanali tellimine)" "message": "Tegutsemise meeldetuletus (kanali tellimine)"
}, },
@ -618,6 +628,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Kuva mängija ajaribal" "message": "Kuva mängija ajaribal"
}, },
"showOverlay_full": {
"message": "Kuva silt"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Jäta automaatselt kõik segmendid vahele, kui eksisteerib mitte-muusika segment" "message": "Jäta automaatselt kõik segmendid vahele, kui eksisteerib mitte-muusika segment"
}, },
@ -745,6 +758,9 @@
"message": "Sain aru", "message": "Sain aru",
"description": "Used as the button to dismiss a tooltip" "description": "Used as the button to dismiss a tooltip"
}, },
"categoryPillTitleText": {
"message": "See terve video on selle kategooriaga sildistatud ning on liiga tihedalt integreeritud, et eraldada saaks"
},
"experiementOptOut": { "experiementOptOut": {
"message": "Keeldu kõigist tulevikus tehtavatatest eksperimentidest", "message": "Keeldu kõigist tulevikus tehtavatatest eksperimentidest",
"description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private." "description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private."

View file

@ -3,11 +3,15 @@
"message": "اسپانسربلاک برای یوتیوب - اسپانسر ها را رد کنید", "message": "اسپانسربلاک برای یوتیوب - اسپانسر ها را رد کنید",
"description": "Name of the extension." "description": "Name of the extension."
}, },
"Description": {
"message": "بخش های اسپانسر شده، درخواست ساب اسکرایب و خیلی چیز های دیگر در ویدیو های یوتیوب را رد کنید. قسمت های اسپانسری ویدیو هایی که میبینید را گزارش کنید تا در وقت دیگران صرفه جویی شود.",
"description": "Description of the extension."
},
"400": { "400": {
"message": "سرور گفت که این درخواست نامعتبر است" "message": "سرور گفت که این درخواست نامعتبر است"
}, },
"429": { "429": {
"message": "شما برای این یک ویدیو تعداد بیش از حدی زمان اسپانسر ثبت کرده‌اید، آیا مطمئن هستید که به این تعداد وجود دارد؟" "message": "شما برای این ویدیو قسمت های اسپانسری خیلی زیادی ثبت کرده اید، آیا مطمئنید که به این تعداد وجوددارد؟"
}, },
"409": { "409": {
"message": "این قبلاً ثبت شده است" "message": "این قبلاً ثبت شده است"
@ -127,7 +131,7 @@
"message": "شما دیگران را نجات دادید از " "message": "شما دیگران را نجات دادید از "
}, },
"viewLeaderboard": { "viewLeaderboard": {
"message": "لیست سرنشینان" "message": "نفرات برتر"
}, },
"recordTimesDescription": { "recordTimesDescription": {
"message": "ثبت" "message": "ثبت"
@ -144,6 +148,9 @@
"setUsername": { "setUsername": {
"message": "تنظیم نام کاربری" "message": "تنظیم نام کاربری"
}, },
"discordAdvert": {
"message": "به سرور رسمی دیسکورد بپیوندید تا پیشنهادات و بازخورد‌های خود را ارائه دهید!"
},
"hideThis": { "hideThis": {
"message": "مخفی‌سازی" "message": "مخفی‌سازی"
}, },
@ -156,6 +163,9 @@
"hideButtons": { "hideButtons": {
"message": "مخفی‌سازی کلید ها در پخش‌کننده یوتیوب" "message": "مخفی‌سازی کلید ها در پخش‌کننده یوتیوب"
}, },
"showNotice": {
"message": "نمایش مجدد اطلاعیه"
},
"longDescription": { "longDescription": {
"message": "افزونه اسپانسر بلاک به شما امکان رد کردن بخش‌های تبلیغاتی (اسپانسر شده)، قسمت‌های شروع و پایان ویدیو، درخواست ساب‌اسکرایب و سایر قسمت‌های آزار دهنده یوتیوب را می‌دهد. اسپانسر بلاک یک افزونه مرورگر است که به هر کسی امکان ثبت زمان شروع و پایان بخش های اسپانسر شده و سایر بخش های ویدیو های یوتیوب را می‌دهد. پس از اینکه هر کاربر این اطلاعات را ثبت کرده و گزارش دهد، بقیه کاربرانی که از این افزونه استفاده می‌کنند مستقیماً بخش اسپانسر شده ویدیو را رد خواهند کرد. شما همچنین می‌توانید در ویدیو های نماهنگ (موزیک ویدیو)، قسمت‌های غیر موسیقی ویدیو را رد کنید.", "message": "افزونه اسپانسر بلاک به شما امکان رد کردن بخش‌های تبلیغاتی (اسپانسر شده)، قسمت‌های شروع و پایان ویدیو، درخواست ساب‌اسکرایب و سایر قسمت‌های آزار دهنده یوتیوب را می‌دهد. اسپانسر بلاک یک افزونه مرورگر است که به هر کسی امکان ثبت زمان شروع و پایان بخش های اسپانسر شده و سایر بخش های ویدیو های یوتیوب را می‌دهد. پس از اینکه هر کاربر این اطلاعات را ثبت کرده و گزارش دهد، بقیه کاربرانی که از این افزونه استفاده می‌کنند مستقیماً بخش اسپانسر شده ویدیو را رد خواهند کرد. شما همچنین می‌توانید در ویدیو های نماهنگ (موزیک ویدیو)، قسمت‌های غیر موسیقی ویدیو را رد کنید.",
"description": "Full description of the extension on the store pages." "description": "Full description of the extension on the store pages."
@ -172,6 +182,9 @@
"message": "اگر همچنان این را نمی‌پسندید، گزینه هرگز نمایش نده را انتخاب کنید.", "message": "اگر همچنان این را نمی‌پسندید، گزینه هرگز نمایش نده را انتخاب کنید.",
"description": "The second line of the message displayed after the notice was upgraded." "description": "The second line of the message displayed after the notice was upgraded."
}, },
"keybindDescription": {
"message": "یک کلید را با تایپ نمودن انتخاب نمایید"
},
"disableSkipping": { "disableSkipping": {
"message": "ردکردن فعال است" "message": "ردکردن فعال است"
}, },
@ -194,6 +207,20 @@
"mute": { "mute": {
"message": "بی‌صدا" "message": "بی‌صدا"
}, },
"skip_category": {
"message": "{0} رد شود؟"
},
"mute_category": {
"message": "{0} بی‌صدا شود؟"
},
"skipped": {
"message": "{0} رد شد",
"description": "Example: Sponsor Skipped"
},
"muted": {
"message": "{0} بی‌صدا شد",
"description": "Example: Sponsor Muted"
},
"minLower": { "minLower": {
"message": "دقیقه" "message": "دقیقه"
}, },
@ -203,6 +230,12 @@
"createdBy": { "createdBy": {
"message": "ایجاد شده توسط" "message": "ایجاد شده توسط"
}, },
"keybindCurrentlySet": {
"message": ". در حال حاضر تنظیم شده است به:"
},
"supportedSites": {
"message": "وب‌سایت‌های پشتیبانی شده: "
},
"add": { "add": {
"message": "افزودن" "message": "افزودن"
}, },
@ -221,6 +254,9 @@
"setOptions": { "setOptions": {
"message": "تنظیم گزینه‌ها" "message": "تنظیم گزینه‌ها"
}, },
"confirmNoticeTitle": {
"message": "ثبت بخش"
},
"submit": { "submit": {
"message": "ثبت" "message": "ثبت"
}, },
@ -242,6 +278,9 @@
"edit": { "edit": {
"message": "ویرایش" "message": "ویرایش"
}, },
"theKey": {
"message": "کلید"
},
"to": { "to": {
"message": "به", "message": "به",
"description": "Used between segments. Example: 1:20 to 1:30" "description": "Used between segments. Example: 1:20 to 1:30"
@ -249,6 +288,12 @@
"category_sponsor": { "category_sponsor": {
"message": "اسپانسر" "message": "اسپانسر"
}, },
"category_exclusive_access": {
"message": "دسترسی اختصاصی"
},
"category_filler_short": {
"message": "پر کننده"
},
"category_music_offtopic_short": { "category_music_offtopic_short": {
"message": "غیر موسیقی" "message": "غیر موسیقی"
}, },
@ -261,12 +306,43 @@
"manualSkip": { "manualSkip": {
"message": "ردکردن دستی" "message": "ردکردن دستی"
}, },
"showOverlay": {
"message": "نمایش در نوار پیشرفت"
},
"disable": {
"message": "غیرفعال کردن"
},
"showOverlay_POI": {
"message": "نمایش در نوار پیشرفت"
},
"showOverlay_full": {
"message": "نمایش نام"
},
"category": {
"message": "دسته بندی"
},
"bracketNow": { "bracketNow": {
"message": "(اکنون)" "message": "(اکنون)"
}, },
"moreCategories": {
"message": "نمایش دسته‌بندی‌ها"
},
"bracketEnd": { "bracketEnd": {
"message": "(پایان)" "message": "(پایان)"
}, },
"acceptPermission": {
"message": "تأیید دسترسی"
},
"incorrectCategory": {
"message": "تغییر دسته بندی"
},
"guidelines": {
"message": "دستورالعمل‌ها"
},
"readTheGuidelines": {
"message": "دستورالعمل‌ها را بخوانید!!",
"description": "Show the first time they submit or if they are \"high risk\""
},
"help": { "help": {
"message": "راهنما" "message": "راهنما"
}, },
@ -274,7 +350,61 @@
"message": "فهمیدم", "message": "فهمیدم",
"description": "Used as the button to dismiss a tooltip" "description": "Used as the button to dismiss a tooltip"
}, },
"hideForever": {
"message": "مخفی‌سازی برای همیشه"
},
"warningChatInfo": {
"message": "شما یک اخطار دریافت کرده اید و موقتاً نمیتوانید بخشی را ثبت کنید. این یعنی ما متوجه شدیم که شما یک سری اشتباهات متداول داشتید اما قصد و نیت خرابکاری نداشتید، لطفاً فقط تایید کنید که شما از قوانین آگاهی دارید و سپس ما این اخطار را حذف خواهیم کرد. شما میتوانید به کمک discord.gg/SponsorBlock یا matrix.to/#/#sponsor:ajay.app عضو گروه ما بشوید."
},
"voteRejectedWarning": {
"message": "رای شما رد شد چون شما یک اخطار دارید. برای حل این مشکل یک کلیک کنید تا گروه چت ما باز بشود، یا اینکه بعداً هر موقع وقت داشتید این کار را انجام بدهید.",
"description": "This is an integrated chat panel that will appearing allowing them to talk to the Discord/Matrix chat without leaving their browser."
},
"Donate": { "Donate": {
"message": "کمک مالی" "message": "کمک مالی"
},
"hideDonationLink": {
"message": "پنهان کردن لینک کمک مالی"
},
"helpPageThanksForInstalling": {
"message": "ازینکه افزونه SponserBlock را نصب کردید سپاسگزاریم."
},
"helpPageReviewOptions": {
"message": "لطفاً تنظیمات زیر را بررسی کنید"
},
"helpPageHowSkippingWorks": {
"message": "چگونه ردکردن کار می‌کند"
},
"Submitting": {
"message": "ثبت نمودن"
},
"Editing": {
"message": "ویرایش"
},
"helpPageCopyOfDatabase": {
"message": "آیا می‌توان یک کپی از پایگاه داده را دریافت کرد؟ اگر شما یک روز ناپدید شدید چه می‌شود؟"
},
"helpPageCopyOfDatabase1": {
"message": "پایگاه داده به‌صورت عمومی در دسترس است در"
},
"helpPageCopyOfDatabase2": {
"message": "سورس کد به‌صورت رایگان ارائه شده است. بنابراین، اگر اتفاقی برای من بیفتد، ارسالات شما از بین نمی‌روند."
},
"helpPageSourceCode": {
"message": "از کجا سورس کد را بگیرم؟"
},
"Credits": {
"message": "سازندگان"
},
"LearnMore": {
"message": "بیشتر بدانید"
},
"dayAbbreviation": {
"message": "روز",
"description": "100d"
},
"hourAbbreviation": {
"message": "ساعت",
"description": "100h"
} }
} }

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Seuraava lähetys on lyhyempi kuin vähimmäiskeston asetuksesi. Tämä voi tarkoittaa sitä, että tämä on jo lähetetty, ja sitä ei vain oteta huomioon tämän asetuksen vuoksi. Oletko varma, että haluat lähettää?" "message": "Seuraava lähetys on lyhyempi kuin vähimmäiskeston asetuksesi. Tämä voi tarkoittaa sitä, että tämä on jo lähetetty, ja sitä ei vain oteta huomioon tämän asetuksen vuoksi. Oletko varma, että haluat lähettää?"
}, },
"liveOrPremiere": {
"message": "Aktiivisen livestriimin tai ensiesityksen aikana lähettäminen ei ole sallittua. Odota kunnes se loppuu, sitten päivitä sivu ja varmista, että segmentit ovat yhä oikein."
},
"showUploadButton": { "showUploadButton": {
"message": "Näytä lähetä-painike" "message": "Näytä lähetä-painike"
}, },
@ -537,6 +540,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Samankaltainen \"sponsorin\" kanssa, mutta maksamattomalle tai itsensä mainostukselle. Tämä sisältää osioita kauppatavarasta, lahjoituksista tai tietoa siitä, kenen kanssa he ovat tehneet yhteistyötä." "message": "Samankaltainen \"sponsorin\" kanssa, mutta maksamattomalle tai itsensä mainostukselle. Tämä sisältää osioita kauppatavarasta, lahjoituksista tai tietoa siitä, kenen kanssa he ovat tehneet yhteistyötä."
}, },
"category_exclusive_access": {
"message": "Yksinoikeudellinen ensikatsaus"
},
"category_exclusive_access_description": {
"message": "Vain kokonaisten videoiden merkitsemiseen. Käytetään kun videossa esitellään tuote, palvelu tai sijainti, johon he ovat saaneet ilmaisen tai tuetun käyttöoikeuden."
},
"category_exclusive_access_pill": {
"message": "Tämä video esittelee tuotteen, palvelun tai sijainnin, johon he ovat saaneet ilmaisen tai tuetun käyttöoikeuden",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Vuorovaikutusmuistutus (tilaaminen)" "message": "Vuorovaikutusmuistutus (tilaaminen)"
}, },
@ -618,6 +631,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Näytä liukusäätimessä" "message": "Näytä liukusäätimessä"
}, },
"showOverlay_full": {
"message": "Näytä merkki"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Ohita kaikki segmentit automaattisesti, kun videossa on \"Musiikiton\" segmentti" "message": "Ohita kaikki segmentit automaattisesti, kun videossa on \"Musiikiton\" segmentti"
}, },

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "A következő szegmens rövidebb, mint az általad beállított minimális időtartam. Ez azt jelentheti, hogy már beküldhették, csak emiatt az opció miatt nálad nem jelenik meg. Biztosan beküldöd?" "message": "A következő szegmens rövidebb, mint az általad beállított minimális időtartam. Ez azt jelentheti, hogy már beküldhették, csak emiatt az opció miatt nálad nem jelenik meg. Biztosan beküldöd?"
}, },
"liveOrPremiere": {
"message": "Aktív élő közvetítés vagy premier közben nem lehet szegmenseket beküldeni. Kérjük várd meg a végét, majd frissítsd az oldalt és ellenőrizd, hogy a szegmensek nem csúsztak-e el."
},
"showUploadButton": { "showUploadButton": {
"message": "Feltöltés gomb megjelenítése" "message": "Feltöltés gomb megjelenítése"
}, },
@ -541,10 +544,10 @@
"message": "Exkluzív hozzáférés" "message": "Exkluzív hozzáférés"
}, },
"category_exclusive_access_description": { "category_exclusive_access_description": {
"message": "Csak teljes videók megjelölésére. Akkor használt, amikor egy videó egy olyan terméket, szolgáltatást vagy helyszínt mutat be, amihez ingyen vagy kedvezményes hozzáférést kaptak." "message": "Csak teljes videók megjelölésére. Akkor használt, amikor egy videó egy olyan terméket, szolgáltatást vagy helyszínt mutat be, amihez ingyenes vagy kedvezményes hozzáférést kaptak."
}, },
"category_exclusive_access_pill": { "category_exclusive_access_pill": {
"message": "Ez a videó olyan terméket, szolgáltatást vagy helyszínt mutat be, amihez ingyen vagy kedvezményes hozzáférést kaptak", "message": "Ez a videó olyan terméket, szolgáltatást vagy helyszínt mutat be, amihez ingyenes vagy kedvezményes hozzáférést kaptak",
"description": "Short description for this category" "description": "Short description for this category"
}, },
"category_interaction": { "category_interaction": {

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Il seguente contributo è più breve della tua opzione di durata minima. Ciò potrebbe significare che questo è già stato inviato e viene semplicemente ignorato a causa di questa opzione. Sei sicuro di voler inviare?" "message": "Il seguente contributo è più breve della tua opzione di durata minima. Ciò potrebbe significare che questo è già stato inviato e viene semplicemente ignorato a causa di questa opzione. Sei sicuro di voler inviare?"
}, },
"liveOrPremiere": {
"message": "Inviare segmenti per una live o premiere non è consentito. Per favore aspetta che finisca, poi ricarica la pagina e verifica che i segmenti siano ancora validi."
},
"showUploadButton": { "showUploadButton": {
"message": "Mostra Pulsante di Caricamento" "message": "Mostra Pulsante di Caricamento"
}, },
@ -537,6 +540,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Simile alle \"sponsorizzazioni\" tranne che per promozioni non pagate o autopromozioni. Ciò include sezioni riguardanti vendita di merce, donazioni o informazioni in merito a collaboratori." "message": "Simile alle \"sponsorizzazioni\" tranne che per promozioni non pagate o autopromozioni. Ciò include sezioni riguardanti vendita di merce, donazioni o informazioni in merito a collaboratori."
}, },
"category_exclusive_access": {
"message": "Accesso Esclusivo"
},
"category_exclusive_access_description": {
"message": "Solo per etichettare interi video. Usato quando un video mostra un prodotto, un servizio o un posto che hanno ricevuto gratuitamente o a cui hanno avuto un accesso sovvenzionato."
},
"category_exclusive_access_pill": {
"message": "Questo video mostra un prodotto, un servizio o un posto che hanno ricevuto gratuitamente o a cui hanno avuto un accesso sovvenzionato",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Promemoria di Interazione (Sottoscrizione)" "message": "Promemoria di Interazione (Sottoscrizione)"
}, },
@ -568,10 +581,10 @@
"message": "Riepilogo rapido degli episodi precedenti, o un'anteprima di ciò che sta arrivando più tardi nel video attuale. Inteso per clip, non per riassunti a voce." "message": "Riepilogo rapido degli episodi precedenti, o un'anteprima di ciò che sta arrivando più tardi nel video attuale. Inteso per clip, non per riassunti a voce."
}, },
"category_filler": { "category_filler": {
"message": "Tangente di Riempimento" "message": "Tangente riempitiva"
}, },
"category_filler_description": { "category_filler_description": {
"message": "Le scene tangenziali aggiunte solo per riempire o per umorismo che non sono richieste per comprendere il contenuto principale del video. Questo non dovrebbe includere segmenti che forniscono contesto o dettagli di sfondo." "message": "Le scene riempitive sono aggiunte solo per riempire o per umorismo che non sono richieste per comprendere il contenuto principale del video. Questo non dovrebbe includere segmenti che forniscono contesto o dettagli di sfondo."
}, },
"category_filler_short": { "category_filler_short": {
"message": "Riempimento" "message": "Riempimento"
@ -618,6 +631,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Mostra Nella Barra di Ricerca" "message": "Mostra Nella Barra di Ricerca"
}, },
"showOverlay_full": {
"message": "Mostra Etichetta"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Salta automaticamente tutti i segmenti quando c'è un segmento non musicale" "message": "Salta automaticamente tutti i segmenti quando c'è un segmento non musicale"
}, },
@ -832,7 +848,7 @@
"message": "Scopri di Più" "message": "Scopri di Più"
}, },
"CopyDownvoteButtonInfo": { "CopyDownvoteButtonInfo": {
"message": "Vota negativamente e crea una copia locale per reinviare" "message": "Vota negativamente e crea una copia locale da reinviare"
}, },
"OpenCategoryWikiPage": { "OpenCategoryWikiPage": {
"message": "Apri la pagina della wiki di questa categoria." "message": "Apri la pagina della wiki di questa categoria."

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "De volgende inzending is korter dan uw \"minimale tijdsduur\"-instelling. Dit kan betekenen dat dit al is ingediend en genegeerd wordt door deze optie. Weet u zeker dat u dit wilt indienen?" "message": "De volgende inzending is korter dan uw \"minimale tijdsduur\"-instelling. Dit kan betekenen dat dit al is ingediend en genegeerd wordt door deze optie. Weet u zeker dat u dit wilt indienen?"
}, },
"liveOrPremiere": {
"message": "Indienen op een actieve livesteam of première is niet toegestaan. Wacht tot hij klaar is, vernieuw dan de pagina en controleer of de segmenten nog geldig zijn."
},
"showUploadButton": { "showUploadButton": {
"message": "Uploaden-knop weergeven" "message": "Uploaden-knop weergeven"
}, },
@ -537,6 +540,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Vergelijkbaar met \"sponsor\", behalve voor onbetaalde of zelfpromotie. Dit is inclusief secties over koopwaar, donaties of informatie over met wie ze hebben samengewerkt." "message": "Vergelijkbaar met \"sponsor\", behalve voor onbetaalde of zelfpromotie. Dit is inclusief secties over koopwaar, donaties of informatie over met wie ze hebben samengewerkt."
}, },
"category_exclusive_access": {
"message": "Exclusieve toegang"
},
"category_exclusive_access_description": {
"message": "Alleen voor het labelen van volledige video's. Wordt gebruikt wanneer een video een product, dienst of locatie laat zien waartoe men gratis of gesubsidieerd toegang heeft gekregen."
},
"category_exclusive_access_pill": {
"message": "Deze video toont een product, dienst of locatie waartoe men gratis of gesubsidieerd toegang heeft gekregen",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Interactieherinnering (abonneren)" "message": "Interactieherinnering (abonneren)"
}, },
@ -618,6 +631,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Weergeven in tijdbalk" "message": "Weergeven in tijdbalk"
}, },
"showOverlay_full": {
"message": "Label weergeven"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Automatisch alle segmenten overslaan wanneer er een niet-muziek-segment is" "message": "Automatisch alle segmenten overslaan wanneer er een niet-muziek-segment is"
}, },

View file

@ -302,6 +302,10 @@
"mute": { "mute": {
"message": "Silenciar" "message": "Silenciar"
}, },
"full": {
"message": "Vídeo completo",
"description": "Used for the name of the option to label an entire video as sponsor or self promotion."
},
"skip_category": { "skip_category": {
"message": "Pular {0}?" "message": "Pular {0}?"
}, },
@ -533,6 +537,9 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Similar a \"patrocinador\", mas para auto promoções e segmentos não-pagos. Isso inclui seções sobre vendas, doações ou informações sobre com quem colaboraram." "message": "Similar a \"patrocinador\", mas para auto promoções e segmentos não-pagos. Isso inclui seções sobre vendas, doações ou informações sobre com quem colaboraram."
}, },
"category_exclusive_access": {
"message": "Acesso Exclusivo"
},
"category_interaction": { "category_interaction": {
"message": "Lembrete de interação (inscrever-se)" "message": "Lembrete de interação (inscrever-se)"
}, },
@ -614,12 +621,19 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Mostrar na barra de progresso" "message": "Mostrar na barra de progresso"
}, },
"showOverlay_full": {
"message": "Mostrar Rótulo"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Pular automaticamente todos os segmentos quando há um segmento que não é música" "message": "Pular automaticamente todos os segmentos quando há um segmento que não é música"
}, },
"muteSegments": { "muteSegments": {
"message": "Permitir segmentos que silenciem o áudio ao invés de pular" "message": "Permitir segmentos que silenciem o áudio ao invés de pular"
}, },
"fullVideoSegments": {
"message": "Mostrar um ícone quando um vídeo é inteiramente um anúncio",
"description": "Referring to the category pill that is now shown on videos that are entirely sponsor or entirely selfpromo"
},
"colorFormatIncorrect": { "colorFormatIncorrect": {
"message": "Sua cor está formatada incorretamente. Deve ser um código hexadecimal de 3 ou 6 dígitos com uma cerquilha (hashtag) no início." "message": "Sua cor está formatada incorretamente. Deve ser um código hexadecimal de 3 ou 6 dígitos com uma cerquilha (hashtag) no início."
}, },
@ -737,6 +751,12 @@
"message": "Entendi", "message": "Entendi",
"description": "Used as the button to dismiss a tooltip" "description": "Used as the button to dismiss a tooltip"
}, },
"fullVideoTooltipWarning": {
"message": "Este segmento é grande. Se o vídeo inteiro for sobre um tópico, altere de \"Pular\" para \"Vídeo completo\". Consulte as diretrizes para obter mais informações."
},
"categoryPillTitleText": {
"message": "Este vídeo inteiro está rotulado como esta categoria e está muito integrado para poder ser separado"
},
"experiementOptOut": { "experiementOptOut": {
"message": "Optar por sair de todos os experimentos futuros", "message": "Optar por sair de todos os experimentos futuros",
"description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private." "description": "This is used in a popup about a new experiment to get a list of unlisted videos to back up since all unlisted videos uploaded before 2017 will be set to private."
@ -835,6 +855,9 @@
"SponsorTimeEditScrollNewFeature": { "SponsorTimeEditScrollNewFeature": {
"message": "Use a roda do mouse enquanto mantêm o cursor sobre a caixa de edição para ajustar o tempo rapidamente. Combinações das teclas ctrl e shift podem ser usadas para refinar as mudanças." "message": "Use a roda do mouse enquanto mantêm o cursor sobre a caixa de edição para ajustar o tempo rapidamente. Combinações das teclas ctrl e shift podem ser usadas para refinar as mudanças."
}, },
"categoryPillNewFeature": {
"message": "Novo! Veja quando um vídeo é inteiramente patrocinado ou de autopromoção"
},
"dayAbbreviation": { "dayAbbreviation": {
"message": "d", "message": "d",
"description": "100d" "description": "100d"

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Следующий диапазон времени короче, чем Ваша настройка минимальной длительности. Это может означать, что он уже был отправлен, и просто игнорируется из-за этой настройки. Вы действительно хотите отправить?" "message": "Следующий диапазон времени короче, чем Ваша настройка минимальной длительности. Это может означать, что он уже был отправлен, и просто игнорируется из-за этой настройки. Вы действительно хотите отправить?"
}, },
"liveOrPremiere": {
"message": "Отправка сегментов на стримах или премьерах не допускается. Пожалуйста, дождитесь окончания видео, затем обновите страницу и убедитесь, что сегменты всё ещё верные."
},
"showUploadButton": { "showUploadButton": {
"message": "Показывать кнопку отправки" "message": "Показывать кнопку отправки"
}, },
@ -541,7 +544,7 @@
"message": "Эксклюзивный доступ" "message": "Эксклюзивный доступ"
}, },
"category_exclusive_access_description": { "category_exclusive_access_description": {
"message": "Категория для всего видео. Используется когда видео демонстрирует продукт, сервис или местоположение, к которому автор получил бесплатный или проспонсированный доступ." "message": "Категория для всего видео. Используется, когда видео демонстрирует продукт, сервис или местоположение, к которому автор получил бесплатный или проспонсированный доступ."
}, },
"category_exclusive_access_pill": { "category_exclusive_access_pill": {
"message": "Это видео демонстрирует продукт, сервис или местоположение, к которому автор получил бесплатный или проспонсированный доступ", "message": "Это видео демонстрирует продукт, сервис или местоположение, к которому автор получил бесплатный или проспонсированный доступ",
@ -629,7 +632,7 @@
"message": "Показывать в полосе прокрутки" "message": "Показывать в полосе прокрутки"
}, },
"showOverlay_full": { "showOverlay_full": {
"message": "Отображать название" "message": "Показывать категорию"
}, },
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Пропускать все сегменты автоматически при наличии сегмента без музыки" "message": "Пропускать все сегменты автоматически при наличии сегмента без музыки"

View file

@ -537,6 +537,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Podobné ako sponzor, okrem neplatenej alebo vlastnej propagácie. Patria sem sekcie týkajúce sa merchu, donatov alebo informácií o tom, s kým spolupracovali." "message": "Podobné ako sponzor, okrem neplatenej alebo vlastnej propagácie. Patria sem sekcie týkajúce sa merchu, donatov alebo informácií o tom, s kým spolupracovali."
}, },
"category_exclusive_access": {
"message": "Exkluzívny Prístup"
},
"category_exclusive_access_description": {
"message": "Iba pre označovanie celých videí. Používa sa, keď video predstavuje produkt, službu alebo miesto, ku ktorým získali bezplatný alebo dotovaný prístup."
},
"category_exclusive_access_pill": {
"message": "Toto video predstavuje produkt, službu alebo miesto, ku ktorým získali bezplatný alebo dotovaný prístup",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Pripomienka interakcie (Prihlásiť sa na odber)" "message": "Pripomienka interakcie (Prihlásiť sa na odber)"
}, },
@ -618,6 +628,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Zobraziť v časovej lište" "message": "Zobraziť v časovej lište"
}, },
"showOverlay_full": {
"message": "Ukázať Označenie"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Automaticky preskočiť všetky segmenty ak neexistuje segment bez hudby" "message": "Automaticky preskočiť všetky segmenty ak neexistuje segment bez hudby"
}, },

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Följande rapport är kortare än ditt minstavärde i inställningarna. Det skulle kunna betyda att det redan är rapporterat och bara ignorerat på grund av denna inställning. Är du säker på att du vill rapportera?" "message": "Följande rapport är kortare än ditt minstavärde i inställningarna. Det skulle kunna betyda att det redan är rapporterat och bara ignorerat på grund av denna inställning. Är du säker på att du vill rapportera?"
}, },
"liveOrPremiere": {
"message": "Att skicka in på en aktiv liveström eller premiär är inte tillåtet. Vänta tills den är färdig, uppdatera sedan sidan och kontrollera att segmenten fortfarande är giltiga."
},
"showUploadButton": { "showUploadButton": {
"message": "Visa uppladdningsknapp" "message": "Visa uppladdningsknapp"
}, },

View file

@ -71,7 +71,7 @@
"message": "Sponsor sürelerini yollarken bir sorun oluştur, lütfen tekrar deneyin." "message": "Sponsor sürelerini yollarken bir sorun oluştur, lütfen tekrar deneyin."
}, },
"sponsorFound": { "sponsorFound": {
"message": "Bu videonun kısımları veri tabanımızda mevut" "message": "Bu videonun kısımları veri tabanımızda mevcut"
}, },
"sponsor404": { "sponsor404": {
"message": "Kısım bulunamadı" "message": "Kısım bulunamadı"
@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Sıradaki öneri belirlediğiniz minimum süre ayarından daha kısa. Bu zaten yollandığı ve bu ayardan dolayı yok sayıldığı anlamına gelebilir. Göndermek istediğinizden emin misiniz?" "message": "Sıradaki öneri belirlediğiniz minimum süre ayarından daha kısa. Bu zaten yollandığı ve bu ayardan dolayı yok sayıldığı anlamına gelebilir. Göndermek istediğinizden emin misiniz?"
}, },
"liveOrPremiere": {
"message": "Bir canlı yayın veya ön gösterim sırasında kısım gönderilemez. Yayının bitmesini bekleyin, sonra sayfayı tazeleyip kısımların geçerli olduğunu kontrol edin."
},
"showUploadButton": { "showUploadButton": {
"message": "Karşıya Yükleme Butonunu Göster" "message": "Karşıya Yükleme Butonunu Göster"
}, },
@ -537,6 +540,16 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "\"Sponsor\" seçeneğinden farkı para karşılığı olmaması veya kendi reklamını yapmasıdır. Buna kendi markalı ürünlerini satmak, bağış toplamak ve videoda işbirliği yaptığı kimselerden bahsetmek dahildir." "message": "\"Sponsor\" seçeneğinden farkı para karşılığı olmaması veya kendi reklamını yapmasıdır. Buna kendi markalı ürünlerini satmak, bağış toplamak ve videoda işbirliği yaptığı kimselerden bahsetmek dahildir."
}, },
"category_exclusive_access": {
"message": "Özel Erişim"
},
"category_exclusive_access_description": {
"message": "Yalnızca bütün videoyu etiketlemek için kullanın. Video; ücretli veya ücretsiz elde edilen bir ürünün, hizmetin veya bir yerin reklamını yapıyorsa kullanılır."
},
"category_exclusive_access_pill": {
"message": "Bu video; yayıncıya özel ücretle veya ücretsiz sunulan bir ürünün, hizmetin veya bir yerin reklamını yapıyor",
"description": "Short description for this category"
},
"category_interaction": { "category_interaction": {
"message": "Etkileşim Hatırlatıcısı (Abonelik)" "message": "Etkileşim Hatırlatıcısı (Abonelik)"
}, },
@ -618,6 +631,9 @@
"showOverlay_POI": { "showOverlay_POI": {
"message": "Arama Çubuğunda Göster" "message": "Arama Çubuğunda Göster"
}, },
"showOverlay_full": {
"message": "Etiketi Göster"
},
"autoSkipOnMusicVideos": { "autoSkipOnMusicVideos": {
"message": "Müzik olmayan kısım varsa tüm kısımları otomatik atla" "message": "Müzik olmayan kısım varsa tüm kısımları otomatik atla"
}, },
@ -850,7 +866,7 @@
"message": "Zaman aralığını hızlı bir şekilde ayarlamak için düzenleme kutusunun üzerinde fare tekerini kullanın. Değişikliklere ince ayar yapmak için ctrl veya shift tuşunun kombinasyonları kullanılabilir." "message": "Zaman aralığını hızlı bir şekilde ayarlamak için düzenleme kutusunun üzerinde fare tekerini kullanın. Değişikliklere ince ayar yapmak için ctrl veya shift tuşunun kombinasyonları kullanılabilir."
}, },
"categoryPillNewFeature": { "categoryPillNewFeature": {
"message": "Yeni! Videonun bütünü sponsor veya kendi reklamıysa öngörün" "message": "Yeni! Videonun bütünü sponsor veya kendi reklamıysa bu uyarıyı görün"
}, },
"dayAbbreviation": { "dayAbbreviation": {
"message": "d", "message": "d",

View file

@ -440,6 +440,9 @@
"shortCheck": { "shortCheck": {
"message": "Наступний діапазон часу коротше, ніж Ваше налаштування мінімальної тривалості. Це може означати, що він вже був надісланий, і просто ігнорується через це налаштування. Ви дійсно хочете надіслати?" "message": "Наступний діапазон часу коротше, ніж Ваше налаштування мінімальної тривалості. Це може означати, що він вже був надісланий, і просто ігнорується через це налаштування. Ви дійсно хочете надіслати?"
}, },
"liveOrPremiere": {
"message": "Надсилання під час прямого ефіру чи прем'єри не дозволено. Будь ласка, зачекайте до завершення, потім оновіть сторінку і переконайтеся, що сегменти все ще дійсні."
},
"showUploadButton": { "showUploadButton": {
"message": "Показувати кнопку надсилання" "message": "Показувати кнопку надсилання"
}, },

View file

@ -537,6 +537,9 @@
"category_selfpromo_description": { "category_selfpromo_description": {
"message": "Tương tự như 'nhà tài trợ' ngoại trừ việc quảng cáo không được trả tiền hay tự quảng cáo. Điều này bao gồm các phần hàng hóa, đóng góp, hoặc thông tin về người mà họ hợp tác cùng." "message": "Tương tự như 'nhà tài trợ' ngoại trừ việc quảng cáo không được trả tiền hay tự quảng cáo. Điều này bao gồm các phần hàng hóa, đóng góp, hoặc thông tin về người mà họ hợp tác cùng."
}, },
"category_exclusive_access": {
"message": "Truy cập riêng"
},
"category_interaction": { "category_interaction": {
"message": "Nhắc tương tác (Đăng ký)" "message": "Nhắc tương tác (Đăng ký)"
}, },

View file

@ -1,13 +1,13 @@
import * as React from "react"; import * as React from "react";
import * as CompileConfig from "../../config.json"; import * as CompileConfig from "../../config.json";
import Config from "../config" import Config from "../config"
import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types"; import { Category, ContentContainer, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
import NoticeComponent from "./NoticeComponent"; import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent"; import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
import Utils from "../utils"; import Utils from "../utils";
const utils = new Utils(); const utils = new Utils();
import { getCategoryActionType, getSkippingText } from "../utils/categoryUtils"; import { getSkippingText } from "../utils/categoryUtils";
import ThumbsUpSvg from "../svg-icons/thumbs_up_svg"; import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg"; import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
@ -326,7 +326,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
getSkipButton(): JSX.Element { getSkipButton(): JSX.Element {
if (this.state.showSkipButton && (this.segments.length > 1 if (this.state.showSkipButton && (this.segments.length > 1
|| getCategoryActionType(this.segments[0].category) !== CategoryActionType.POI || this.segments[0].actionType !== ActionType.Poi
|| this.props.unskipTime)) { || this.props.unskipTime)) {
const style: React.CSSProperties = { const style: React.CSSProperties = {
@ -547,7 +547,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
getCategoryOptions(): React.ReactElement[] { getCategoryOptions(): React.ReactElement[] {
const elements = []; const elements = [];
const categories = (CompileConfig.categoryList.filter((cat => getCategoryActionType(cat as Category) === CategoryActionType.Skippable))) as Category[]; const categories = (CompileConfig.categoryList.filter((cat => CompileConfig.categorySupport[cat].includes(ActionType.Skip)))) as Category[];
for (const category of categories) { for (const category of categories) {
elements.push( elements.push(
<option value={category} <option value={category}
@ -601,7 +601,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
} }
getUnskippedModeInfo(index: number, buttonText: string): SkipNoticeState { getUnskippedModeInfo(index: number, buttonText: string): SkipNoticeState {
const changeCountdown = getCategoryActionType(this.segments[index].category) === CategoryActionType.Skippable; const changeCountdown = this.segments[index].actionType !== ActionType.Poi;
const maxCountdownTime = changeCountdown ? () => { const maxCountdownTime = changeCountdown ? () => {
const sponsorTime = this.segments[index]; const sponsorTime = this.segments[index];

View file

@ -1,9 +1,8 @@
import * as React from "react"; import * as React from "react";
import * as CompileConfig from "../../config.json"; import * as CompileConfig from "../../config.json";
import Config from "../config"; import Config from "../config";
import { ActionType, Category, CategoryActionType, ChannelIDStatus, ContentContainer, SponsorTime } from "../types"; import { ActionType, Category, ChannelIDStatus, ContentContainer, SponsorTime } from "../types";
import Utils from "../utils"; import Utils from "../utils";
import { getCategoryActionType } from "../utils/categoryUtils";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent"; import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
import { RectangleTooltip } from "../render/RectangleTooltip"; import { RectangleTooltip } from "../render/RectangleTooltip";
import SelectorComponent, { SelectorOption } from "./SelectorComponent"; import SelectorComponent, { SelectorOption } from "./SelectorComponent";
@ -43,8 +42,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
configUpdateListener: () => void; configUpdateListener: () => void;
previousSkipType: CategoryActionType; previousSkipType: ActionType;
timeBeforeChangingToPOI: number; // Initialized when first selecting POI // Used when selecting POI or Full
timesBeforeChanging: number[] = [];
fullVideoWarningShown = false; fullVideoWarningShown = false;
// For description auto-complete // For description auto-complete
@ -58,8 +58,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.descriptionOptionRef = React.createRef(); this.descriptionOptionRef = React.createRef();
this.idSuffix = this.props.idSuffix; this.idSuffix = this.props.idSuffix;
this.previousSkipType = CategoryActionType.Skippable; this.previousSkipType = ActionType.Skip;
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index]; const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.state = { this.state = {
editing: false, editing: false,
@ -142,7 +142,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
onWheel={(e) => this.changeTimesWhenScrolling(0, e, sponsorTime)}> onWheel={(e) => this.changeTimesWhenScrolling(0, e, sponsorTime)}>
</input> </input>
{getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable ? ( {sponsorTime.actionType !== ActionType.Poi ? (
<span> <span>
<span> <span>
{" " + chrome.i18n.getMessage("to") + " "} {" " + chrome.i18n.getMessage("to") + " "}
@ -180,7 +180,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
className="sponsorTimeDisplay" className="sponsorTimeDisplay"
onClick={this.toggleEditTime.bind(this)}> onClick={this.toggleEditTime.bind(this)}>
{utils.getFormattedTime(segment[0], true) + {utils.getFormattedTime(segment[0], true) +
((!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable) ((!isNaN(segment[1]) && sponsorTime.actionType !== ActionType.Poi)
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")} ? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
</div> </div>
); );
@ -215,13 +215,13 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{/* Action Type */} {/* Action Type */}
{CompileConfig.categorySupport[sponsorTime.category] && {CompileConfig.categorySupport[sponsorTime.category] &&
(CompileConfig.categorySupport[sponsorTime.category]?.length > 1 (CompileConfig.categorySupport[sponsorTime.category]?.length > 1
|| CompileConfig.categorySupport[sponsorTime.category]?.[0] !== "skip") ? ( || ![ActionType.Skip, ActionType.Poi].includes(CompileConfig.categorySupport[sponsorTime.category]?.[0])) ? (
<div style={{position: "relative"}}> <div style={{position: "relative"}}>
<select id={"sponsorTimeActionTypes" + this.idSuffix} <select id={"sponsorTimeActionTypes" + this.idSuffix}
className="sponsorTimeEditSelector sponsorTimeActionTypes" className="sponsorTimeEditSelector sponsorTimeActionTypes"
defaultValue={sponsorTime.actionType} defaultValue={sponsorTime.actionType}
ref={this.actionTypeOptionRef} ref={this.actionTypeOptionRef}
onChange={() => this.saveEditTimes()}> onChange={(e) => this.actionTypeSelectionChange(e)}>
{this.getActionTypeOptions(sponsorTime)} {this.getActionTypeOptions(sponsorTime)}
</select> </select>
</div> </div>
@ -258,7 +258,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{chrome.i18n.getMessage("delete")} {chrome.i18n.getMessage("delete")}
</span> </span>
{(!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable) ? ( {(!isNaN(segment[1]) && ![ActionType.Poi, ActionType.Full].includes(sponsorTime.actionType)) ? (
<span id={"sponsorTimePreviewButton" + this.idSuffix} <span id={"sponsorTimePreviewButton" + this.idSuffix}
className="sponsorTimeEditButton" className="sponsorTimeEditButton"
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey)}> onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey)}>
@ -295,7 +295,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
if (0 < difference && difference < 0.5) this.showScrollToEditToolTip(); if (0 < difference && difference < 0.5) this.showScrollToEditToolTip();
sponsorTimeEdits[index] = targetValue; sponsorTimeEdits[index] = targetValue;
if (index === 0 && getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTimeEdits[1] = targetValue; if (index === 0 && sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = targetValue;
this.setState({sponsorTimeEdits}, () => this.saveEditTimes()); this.setState({sponsorTimeEdits}, () => this.saveEditTimes());
} }
@ -324,7 +324,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
sponsorTimeEdits[index] = utils.getFormattedTime(timeAsNumber, true); sponsorTimeEdits[index] = utils.getFormattedTime(timeAsNumber, true);
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTimeEdits[1] = sponsorTimeEdits[0]; if (sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = sponsorTimeEdits[0];
this.setState({sponsorTimeEdits}); this.setState({sponsorTimeEdits});
this.saveEditTimes(); this.saveEditTimes();
@ -415,21 +415,51 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
return; return;
} }
if (getCategoryActionType(chosenCategory) === CategoryActionType.POI) { const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
if (this.previousSkipType === CategoryActionType.Skippable) this.timeBeforeChangingToPOI = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1]); this.handleReplacingLostTimes(chosenCategory, sponsorTime.actionType);
this.saveEditTimes();
}
actionTypeSelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.handleReplacingLostTimes(sponsorTime.category, event.target.value as ActionType);
this.saveEditTimes();
}
private handleReplacingLostTimes(category: Category, actionType: ActionType): void {
if (CompileConfig.categorySupport[category]?.includes(ActionType.Poi)) {
if (this.previousSkipType !== ActionType.Poi) {
this.timesBeforeChanging = [null, utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1])];
}
this.setTimeTo(1, null); this.setTimeTo(1, null);
this.props.contentContainer().updateEditButtonsOnPlayer(); this.props.contentContainer().updateEditButtonsOnPlayer();
if (this.props.contentContainer().sponsorTimesSubmitting if (this.props.contentContainer().sponsorTimesSubmitting
.some((segment, i) => segment.category === chosenCategory && i !== this.props.index)) { .some((segment, i) => segment.category === category && i !== this.props.index)) {
alert(chrome.i18n.getMessage("poiOnlyOneSegment")); alert(chrome.i18n.getMessage("poiOnlyOneSegment"));
} }
} else if (getCategoryActionType(chosenCategory) === CategoryActionType.Skippable && this.previousSkipType === CategoryActionType.POI) {
this.setTimeTo(1, this.timeBeforeChangingToPOI);
}
this.previousSkipType = getCategoryActionType(chosenCategory); this.previousSkipType = ActionType.Poi;
this.saveEditTimes(); } else if (CompileConfig.categorySupport[category]?.length === 1
&& CompileConfig.categorySupport[category]?.[0] === ActionType.Full) {
if (this.previousSkipType !== ActionType.Full) {
this.timesBeforeChanging = [utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]), utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[1])];
}
this.previousSkipType = ActionType.Full;
} else if (CompileConfig.categorySupport[category]?.includes(ActionType.Skip)
&& ![ActionType.Poi, ActionType.Full].includes(this.getNextActionType(category, actionType)) && this.previousSkipType !== ActionType.Skip) {
if (this.timesBeforeChanging[0]) {
this.setTimeTo(0, this.timesBeforeChanging[0]);
}
if (this.timesBeforeChanging[1]) {
this.setTimeTo(1, this.timesBeforeChanging[1]);
}
this.previousSkipType = ActionType.Skip;
}
} }
getActionTypeOptions(sponsorTime: SponsorTime): React.ReactElement[] { getActionTypeOptions(sponsorTime: SponsorTime): React.ReactElement[] {
@ -464,7 +494,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
if (time === null) time = sponsorTime.segment[0]; if (time === null) time = sponsorTime.segment[0];
sponsorTime.segment[index] = time; sponsorTime.segment[index] = time;
if (getCategoryActionType(sponsorTime.category) === CategoryActionType.POI) sponsorTime.segment[1] = time; if (sponsorTime.actionType === ActionType.Poi) sponsorTime.segment[1] = time;
this.setState({ this.setState({
sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime) sponsorTimeEdits: this.getFormattedSponsorTimesEdits(sponsorTime)
@ -511,9 +541,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
const category = this.categoryOptionRef.current.value as Category const category = this.categoryOptionRef.current.value as Category
sponsorTimesSubmitting[this.props.index].category = category; sponsorTimesSubmitting[this.props.index].category = category;
const inputActionType = this.actionTypeOptionRef?.current?.value as ActionType; const actionType = this.getNextActionType(category, this.actionTypeOptionRef?.current?.value as ActionType);
const actionType = inputActionType && CompileConfig.categorySupport[category]?.includes(inputActionType) ? inputActionType as ActionType
: CompileConfig.categorySupport[category]?.[0] ?? ActionType.Skip;
sponsorTimesSubmitting[this.props.index].actionType = actionType; sponsorTimesSubmitting[this.props.index].actionType = actionType;
const description = actionType === ActionType.Chapter ? this.descriptionOptionRef?.current?.value : ""; const description = actionType === ActionType.Chapter ? this.descriptionOptionRef?.current?.value : "";
@ -530,6 +558,11 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
} }
} }
private getNextActionType(category: Category, actionType: ActionType): ActionType {
return actionType && CompileConfig.categorySupport[category]?.includes(actionType) ? actionType
: CompileConfig.categorySupport[category]?.[0] ?? ActionType.Skip
}
previewTime(ctrlPressed = false, shiftPressed = false): void { previewTime(ctrlPressed = false, shiftPressed = false): void {
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index; const index = this.props.index;

View file

@ -1,5 +1,5 @@
import Config from "./config"; import Config from "./config";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, CategoryActionType, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime } from "./types"; import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime } from "./types";
import { ContentContainer } from "./types"; import { ContentContainer } from "./types";
import Utils from "./utils"; import Utils from "./utils";
@ -13,7 +13,6 @@ import SkipNoticeComponent from "./components/SkipNoticeComponent";
import SubmissionNotice from "./render/SubmissionNotice"; import SubmissionNotice from "./render/SubmissionNotice";
import { Message, MessageResponse, VoteResponse } from "./messageTypes"; import { Message, MessageResponse, VoteResponse } from "./messageTypes";
import * as Chat from "./js-components/chat"; import * as Chat from "./js-components/chat";
import { getCategoryActionType } from "./utils/categoryUtils";
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar"; import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
import { getStartTimeFromUrl } from "./utils/urlParser"; import { getStartTimeFromUrl } from "./utils/urlParser";
import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils"; import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils";
@ -91,13 +90,11 @@ const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImage
// Direct Links after the config is loaded // Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document))); utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document)));
// wait for hover preview to appear, and refresh attachments if ever found
window.addEventListener("DOMContentLoaded", () => utils.waitForElement(".ytp-inline-preview-ui").then(() => refreshVideoAttachments()));
addPageListeners(); addPageListeners();
addHotkeyListener(); addHotkeyListener();
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
let sponsorLookupRetries = 0;
/** Segments created by the user which have not yet been submitted. */ /** Segments created by the user which have not yet been submitted. */
let sponsorTimesSubmitting: SponsorTime[] = []; let sponsorTimesSubmitting: SponsorTime[] = [];
@ -237,7 +234,6 @@ function resetValues() {
//reset sponsor times //reset sponsor times
sponsorTimes = null; sponsorTimes = null;
sponsorLookupRetries = 0;
sponsorSkipped = []; sponsorSkipped = [];
videoInfo = null; videoInfo = null;
@ -689,9 +685,6 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
setupVideoMutationListener(); setupVideoMutationListener();
//check database for sponsor times
//made true once a setTimeout has been created to try again after a server error
let recheckStarted = false;
// Create categories list // Create categories list
const categories: string[] = []; const categories: string[] = [];
for (const categorySelection of Config.config.categorySelections) { for (const categorySelection of Config.config.categorySelections) {
@ -744,7 +737,7 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
if (Config.config.minDuration !== 0) { if (Config.config.minDuration !== 0) {
for (let i = 0; i < sponsorTimes.length; i++) { for (let i = 0; i < sponsorTimes.length; i++) {
if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration
&& getCategoryActionType(sponsorTimes[i].category) !== CategoryActionType.POI) { && sponsorTimes[i].actionType !== ActionType.Poi) {
sponsorTimes[i].hidden = SponsorHideType.MinimumDuration; sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
} }
} }
@ -770,29 +763,15 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
//otherwise the listener can handle it //otherwise the listener can handle it
updatePreviewBar(); updatePreviewBar();
} }
sponsorLookupRetries = 0;
} else if (response?.status === 404) { } else if (response?.status === 404) {
retryFetch(); retryFetch();
} else if (sponsorLookupRetries < 15 && !recheckStarted) {
recheckStarted = true;
//TODO lower when server becomes better (back to 1 second)
//some error occurred, try again in a second
setTimeout(() => {
if (sponsorVideoID && sponsorTimes?.length === 0) {
sponsorsLookup(sponsorVideoID);
}
}, 5000 + Math.random() * 15000 + 5000 * sponsorLookupRetries);
sponsorLookupRetries++;
} }
lookupVipInformation(id); lookupVipInformation(id);
} }
function getEnabledActionTypes(): ActionType[] { function getEnabledActionTypes(): ActionType[] {
const actionTypes = [ActionType.Skip, ActionType.Chapter]; const actionTypes = [ActionType.Skip, ActionType.Poi, ActionType.Chapter];
if (Config.config.muteSegments) { if (Config.config.muteSegments) {
actionTypes.push(ActionType.Mute); actionTypes.push(ActionType.Mute);
} }
@ -860,8 +839,6 @@ function retryFetch(): void {
sponsorsLookup(sponsorVideoID); sponsorsLookup(sponsorVideoID);
} }
}, 10000 + Math.random() * 30000); }, 10000 + Math.random() * 30000);
sponsorLookupRetries = 0;
} }
/** /**
@ -877,7 +854,7 @@ function startSkipScheduleCheckingForStartSponsors() {
let startingSegment: SponsorTime = null; let startingSegment: SponsorTime = null;
for (const time of sponsorTimes) { for (const time of sponsorTimes) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) { && time.actionType !== ActionType.Poi) {
startingSegmentTime = time.segment[0]; startingSegmentTime = time.segment[0];
startingSegment = time; startingSegment = time;
found = true; found = true;
@ -887,7 +864,7 @@ function startSkipScheduleCheckingForStartSponsors() {
if (!found) { if (!found) {
for (const time of sponsorTimesSubmitting) { for (const time of sponsorTimesSubmitting) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
&& getCategoryActionType(time.category) === CategoryActionType.Skippable) { && time.actionType !== ActionType.Poi) {
startingSegmentTime = time.segment[0]; startingSegmentTime = time.segment[0];
startingSegment = time; startingSegment = time;
found = true; found = true;
@ -898,7 +875,7 @@ function startSkipScheduleCheckingForStartSponsors() {
// For highlight category // For highlight category
const poiSegments = sponsorTimes const poiSegments = sponsorTimes
.filter((time) => time.segment[1] > video.currentTime && getCategoryActionType(time.category) === CategoryActionType.POI) .filter((time) => time.segment[1] > video.currentTime && time.actionType === ActionType.Poi)
.sort((a, b) => b.segment[0] - a.segment[0]); .sort((a, b) => b.segment[0] - a.segment[0]);
for (const time of poiSegments) { for (const time of poiSegments) {
const skipOption = utils.getCategorySelection(time.category)?.option; const skipOption = utils.getCategorySelection(time.category)?.option;
@ -956,7 +933,7 @@ function getYouTubeVideoID(document: Document): string | boolean {
// skip to document if matches pattern // skip to document if matches pattern
if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document); if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document);
// not sure, try URL then document // not sure, try URL then document
return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document); return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document, false);
} }
function getYouTubeVideoIDFromDocument(document: Document, hideIcon = true): string | boolean { function getYouTubeVideoIDFromDocument(document: Document, hideIcon = true): string | boolean {
@ -1041,8 +1018,8 @@ function updatePreviewBar(): void {
category: segment.category, category: segment.category,
actionType: segment.actionType, actionType: segment.actionType,
unsubmitted: false, unsubmitted: false,
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI, showLarger: segment.actionType === ActionType.Poi,
description: segment.description, description: segment.description
}); });
}); });
} }
@ -1053,8 +1030,8 @@ function updatePreviewBar(): void {
category: segment.category, category: segment.category,
actionType: segment.actionType, actionType: segment.actionType,
unsubmitted: true, unsubmitted: true,
showLarger: getCategoryActionType(segment.category) === CategoryActionType.POI, showLarger: segment.actionType === ActionType.Poi,
description: segment.description, description: segment.description
}); });
}); });
@ -1226,7 +1203,7 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|| (includeIntersectingSegments && possibleTimes[i].scheduledTime < minimum && possibleTimes[i].segment[1] > minimum))) || (includeIntersectingSegments && possibleTimes[i].scheduledTime < minimum && possibleTimes[i].segment[1] > minimum)))
&& (!hideHiddenSponsors || possibleTimes[i].hidden === SponsorHideType.Visible) && (!hideHiddenSponsors || possibleTimes[i].hidden === SponsorHideType.Visible)
&& possibleTimes[i].segment.length === 2 && possibleTimes[i].segment.length === 2
&& getCategoryActionType(possibleTimes[i].category) === CategoryActionType.Skippable) { && possibleTimes[i].actionType !== ActionType.Poi) {
scheduledTimes.push(possibleTimes[i].scheduledTime); scheduledTimes.push(possibleTimes[i].scheduledTime);
includedTimes.push(possibleTimes[i]); includedTimes.push(possibleTimes[i]);
@ -1280,6 +1257,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
if ((autoSkip || sponsorTimesSubmitting.some((time) => time.segment === skippingSegments[0].segment)) if ((autoSkip || sponsorTimesSubmitting.some((time) => time.segment === skippingSegments[0].segment))
&& v.currentTime !== skipTime[1]) { && v.currentTime !== skipTime[1]) {
switch(skippingSegments[0].actionType) { switch(skippingSegments[0].actionType) {
case ActionType.Poi:
case ActionType.Skip: { case ActionType.Skip: {
// Fix for looped videos not working when skipping to the end #426 // Fix for looped videos not working when skipping to the end #426
// for some reason you also can't skip to 1 second before the end // for some reason you also can't skip to 1 second before the end
@ -1312,7 +1290,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
if (!autoSkip if (!autoSkip
&& skippingSegments.length === 1 && skippingSegments.length === 1
&& getCategoryActionType(skippingSegments[0].category) === CategoryActionType.POI) { && skippingSegments[0].actionType === ActionType.Poi) {
skipButtonControlBar.enable(skippingSegments[0]); skipButtonControlBar.enable(skippingSegments[0]);
if (onMobileYouTube) skipButtonControlBar.setShowKeybindHint(false); if (onMobileYouTube) skipButtonControlBar.setShowKeybindHint(false);
@ -1403,7 +1381,7 @@ function createButton(baseID: string, title: string, callback: () => void, image
function shouldAutoSkip(segment: SponsorTime): boolean { function shouldAutoSkip(segment: SponsorTime): boolean {
return utils.getCategorySelection(segment.category)?.option === CategorySkipOption.AutoSkip || return utils.getCategorySelection(segment.category)?.option === CategorySkipOption.AutoSkip ||
(Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic") (Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic")
&& getCategoryActionType(segment.category) === CategoryActionType.Skippable); && segment.actionType !== ActionType.Poi);
} }
function shouldSkip(segment: SponsorTime): boolean { function shouldSkip(segment: SponsorTime): boolean {

View file

@ -6,10 +6,9 @@ https://github.com/videosegments/videosegments/commits/f1e111bdfe231947800c6efdd
'use strict'; 'use strict';
import Config from "../config"; import Config from "../config";
import { ActionType, Category, CategoryActionType, SegmentContainer, SponsorTime } from "../types"; import { ActionType, Category, SegmentContainer, SponsorTime } from "../types";
import Utils from "../utils"; import Utils from "../utils";
import { partition } from "../utils/arrayUtils"; import { partition } from "../utils/arrayUtils";
import { getCategoryActionType } from "../utils/categoryUtils";
const utils = new Utils(); const utils = new Utils();
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible'; const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
@ -585,7 +584,7 @@ class PreviewBar {
} }
private chapterFilter(segment: PreviewBarSegment): boolean { private chapterFilter(segment: PreviewBarSegment): boolean {
return getCategoryActionType(segment.category) !== CategoryActionType.POI return segment.actionType !== ActionType.Poi
&& this.chapterGroupFilter(segment); && this.chapterGroupFilter(segment);
} }

View file

@ -1,10 +1,9 @@
import Config from "./config"; import Config from "./config";
import Utils from "./utils"; import Utils from "./utils";
import { SponsorTime, SponsorHideType, CategoryActionType, ActionType, SegmentUUID } from "./types"; import { SponsorTime, SponsorHideType, ActionType, SegmentUUID } from "./types";
import { Message, MessageResponse, IsInfoFoundMessageResponse } from "./messageTypes"; import { Message, MessageResponse, IsInfoFoundMessageResponse } from "./messageTypes";
import { showDonationLink } from "./utils/configUtils"; import { showDonationLink } from "./utils/configUtils";
import { getCategoryActionType } from "./utils/categoryUtils";
import { AnimationUtils } from "./utils/animationUtils"; import { AnimationUtils } from "./utils/animationUtils";
import { GenericUtils } from "./utils/genericUtils"; import { GenericUtils } from "./utils/genericUtils";
const utils = new Utils(); const utils = new Utils();
@ -456,7 +455,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full"); segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full");
} else { } else {
segmentTimeFromToNode.innerText = utils.getFormattedTime(segmentTimes[i].segment[0], true) + segmentTimeFromToNode.innerText = utils.getFormattedTime(segmentTimes[i].segment[0], true) +
(getCategoryActionType(category) !== CategoryActionType.POI (actionType !== ActionType.Poi
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true) ? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true)
: ""); : "");
} }

View file

@ -53,16 +53,12 @@ export enum SponsorHideType {
MinimumDuration MinimumDuration
} }
export enum CategoryActionType {
Skippable = "", // Strings are used to find proper language configs
POI = "_POI"
}
export enum ActionType { export enum ActionType {
Skip = "skip", Skip = "skip",
Mute = "mute", Mute = "mute",
Chapter = "chapter", Chapter = "chapter",
Full = "full" Full = "full",
Poi = "poi"
} }
export const ActionTypes = [ActionType.Skip, ActionType.Mute]; export const ActionTypes = [ActionType.Skip, ActionType.Mute];

View file

@ -21,6 +21,10 @@ export default class Utils {
"popup.css" "popup.css"
]; ];
/* Used for waitForElement */
waitingMutationObserver:MutationObserver = null;
waitingElements: { selector: string, callback: (element: Element) => void }[] = [];
constructor(backgroundScriptContainer: BackgroundScriptContainer = null) { constructor(backgroundScriptContainer: BackgroundScriptContainer = null) {
this.backgroundScriptContainer = backgroundScriptContainer; this.backgroundScriptContainer = backgroundScriptContainer;
} }
@ -29,6 +33,41 @@ export default class Utils {
return GenericUtils.wait(condition, timeout, check); return GenericUtils.wait(condition, timeout, check);
} }
/* Uses a mutation observer to wait asynchronously */
async waitForElement(selector: string): Promise<Element> {
return await new Promise((resolve) => {
this.waitingElements.push({
selector,
callback: resolve
});
if (!this.waitingMutationObserver) {
this.waitingMutationObserver = new MutationObserver(() => {
const foundSelectors = [];
for (const { selector, callback } of this.waitingElements) {
const element = document.querySelector(selector);
if (element) {
callback(element);
foundSelectors.push(selector);
}
}
this.waitingElements = this.waitingElements.filter((element) => !foundSelectors.includes(element.selector));
if (this.waitingElements.length === 0) {
this.waitingMutationObserver.disconnect();
this.waitingMutationObserver = null;
}
});
this.waitingMutationObserver.observe(document.body, {
childList: true,
subtree: true
});
}
});
}
containsPermission(permissions: chrome.permissions.Permissions): Promise<boolean> { containsPermission(permissions: chrome.permissions.Permissions): Promise<boolean> {
return new Promise((resolve) => { return new Promise((resolve) => {
chrome.permissions.contains(permissions, resolve) chrome.permissions.contains(permissions, resolve)
@ -331,9 +370,9 @@ export default class Utils {
findReferenceNode(): HTMLElement { findReferenceNode(): HTMLElement {
const selectors = [ const selectors = [
"#player-container-id",
"#movie_player", "#movie_player",
"#c4-player", // Channel Trailer "#c4-player", // Channel Trailer
"#player-container", // Preview on hover
"#main-panel.ytmusic-player-page", // YouTube music "#main-panel.ytmusic-player-page", // YouTube music
"#player-container .video-js", // Invidious "#player-container .video-js", // Invidious
".main-video-section > .video-container" // Cloudtube ".main-video-section > .video-container" // Cloudtube
@ -347,7 +386,7 @@ export default class Utils {
let index = 1; let index = 1;
//find the child that is the video player (sometimes it is not the first) //find the child that is the video player (sometimes it is not the first)
while (index < player.children.length && (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed"))) { while (index < player.children.length && (!referenceNode.classList?.contains("html5-video-player") || !referenceNode.classList?.contains("ytp-embed"))) {
referenceNode = player.children[index] as HTMLElement; referenceNode = player.children[index] as HTMLElement;
index++; index++;

View file

@ -1,51 +1,41 @@
import { ActionType, Category, CategoryActionType, SponsorTime } from "../types"; import { ActionType, Category, SponsorTime } from "../types";
export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): string { export function getSkippingText(segments: SponsorTime[], autoSkip: boolean): string {
const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments" const categoryName = chrome.i18n.getMessage(segments.length > 1 ? "multipleSegments"
: "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category); : "category_" + segments[0].category + "_short") || chrome.i18n.getMessage("category_" + segments[0].category);
if (autoSkip) { if (autoSkip) {
let messageId = ""; let messageId = "";
if (getCategoryActionType(segments[0].category) === CategoryActionType.Skippable) { switch (segments[0].actionType) {
switch (segments[0].actionType) { case ActionType.Skip:
case ActionType.Skip: messageId = "skipped";
messageId = "skipped"; break;
break; case ActionType.Mute:
case ActionType.Mute: messageId = "muted";
messageId = "muted"; break;
break; case ActionType.Poi:
} messageId = "skipped_to_category";
} else { break;
messageId = "skipped_to_category";
} }
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName); return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
} else { } else {
let messageId = ""; let messageId = "";
if (getCategoryActionType(segments[0].category) === CategoryActionType.Skippable) { switch (segments[0].actionType) {
switch (segments[0].actionType) { case ActionType.Skip:
case ActionType.Skip: messageId = "skip_category";
messageId = "skip_category"; break;
break; case ActionType.Mute:
case ActionType.Mute: messageId = "mute_category";
messageId = "mute_category"; break;
break; case ActionType.Poi:
} messageId = "skip_to_category";
} else { break;
messageId = "skip_to_category";
} }
return chrome.i18n.getMessage(messageId).replace("{0}", categoryName); return chrome.i18n.getMessage(messageId).replace("{0}", categoryName);
} }
} }
export function getCategoryActionType(category: Category): CategoryActionType {
if (category.startsWith("poi_")) {
return CategoryActionType.POI;
} else {
return CategoryActionType.Skippable;
}
}
export function getCategorySuffix(category: Category): string { export function getCategorySuffix(category: Category): string {
if (category.startsWith("poi_")) { if (category.startsWith("poi_")) {
return "_POI"; return "_POI";