From 2681dc18906fc07b2b97d646806ea884dfc3e751 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 20 Mar 2021 11:50:15 -0400 Subject: [PATCH 01/63] Submit video duration --- src/content.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/content.ts b/src/content.ts index ba1bd757..a7f6e172 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1458,6 +1458,7 @@ async function sendSubmitMessage(): Promise { const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", { videoID: sponsorVideoID, userID: Config.config.userID, + videoDuration: video.duration, segments: sponsorTimesSubmitting }); From cc935624e9a1e2fb5312c891399f12f499b3fe9b Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 20 Mar 2021 12:24:49 -0400 Subject: [PATCH 02/63] Add homepage url --- manifest/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest/manifest.json b/manifest/manifest.json index 393f5011..0f15941b 100644 --- a/manifest/manifest.json +++ b/manifest/manifest.json @@ -4,6 +4,7 @@ "version": "2.0.13", "default_locale": "en", "description": "__MSG_Description__", + "homepage_url": "https://sponsor.ajay.app", "content_scripts": [{ "run_at": "document_start", "matches": [ From 3ff5fdb3a10d09de34d979f696133c17b5b58c31 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Wed, 24 Mar 2021 20:13:33 -0400 Subject: [PATCH 03/63] Prompt to accept youtube.com permission if video info fails to load Should fix #698, #687, #611 and #635 --- public/_locales/en/messages.json | 15 ++ public/permissions/index.html | 28 +++ public/permissions/styles.css | 356 +++++++++++++++++++++++++++++++ src/background.ts | 3 + src/content.ts | 23 +- src/options.ts | 2 +- src/permissions.ts | 33 +++ src/utils.ts | 41 ++-- webpack/webpack.common.js | 3 +- 9 files changed, 483 insertions(+), 21 deletions(-) create mode 100644 public/permissions/index.html create mode 100644 public/permissions/styles.css create mode 100644 src/permissions.ts diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 51eceee8..fbf06ec7 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -594,9 +594,24 @@ "adblockerIssue": { "message": "It seems that something is blocking SponsorBlock's ability to get video data. This is probably your ad blocker. Please check https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests" }, + "youtubePermissionRequest": { + "message": "It seems that SponsorBlock is unable to reach the YouTube API. To fix this, accept the permission prompt that will appear next, wait a few seconds, and then reload the page." + }, + "acceptPermission": { + "message": "Accept permission" + }, + "permissionRequestSuccess": { + "message": "Permission success succeeded!" + }, + "permissionRequestFailed": { + "message": "Permission request failed, did you click deny?" + }, "adblockerIssueUnlistedVideosInfo": { "message": "If you are unable to resolve this, then disable the setting 'Ignore unlisted/private videos', as SponsorBlock is unable to retrieve the visibility information for this video" }, + "adblockerIssueWhitelist": { + "message": "If you are unable to resolve this, then disable the setting 'Force Channel Check Before Skipping', as SponsorBlock is unable to retrieve the visibility information for this video" + }, "itCouldBeAdblockerIssue": { "message": "If this keeps occuring, it could be caused by your ad blocker. Please check https://github.com/ajayyy/SponsorBlock/wiki/Fix-Ad-Blocker-Blocking-SponsorBlock's-Requests" }, diff --git a/public/permissions/index.html b/public/permissions/index.html new file mode 100644 index 00000000..4475f039 --- /dev/null +++ b/public/permissions/index.html @@ -0,0 +1,28 @@ + + + + Permissions - SponsorBlock + + + + + + + + + + +
+ + SponsorBlock +
+ +
+ +
+
+ __MSG_acceptPermission__ +
+
+ + diff --git a/public/permissions/styles.css b/public/permissions/styles.css new file mode 100644 index 00000000..971c6893 --- /dev/null +++ b/public/permissions/styles.css @@ -0,0 +1,356 @@ +/* Options page CSS */ +body { + font-family: sans-serif; +} + +.center { + text-align: center; +} + +.inline { + display: inline-block; +} + +.bold { + font-weight: bold; +} + +.hidden { + display: none !important; +} + +.keybind-status { + display: inline; +} + +.small-description { + color: white; + font-size: 13px; +} + +.medium-description { + color: white; + font-size: 15px; +} + +.option-text-box { + width: 300px; +} + +.option-button { + cursor: pointer; + + background-color: #c00000; + padding: 10px; + color: white; + border-radius: 5px; + font-size: 14px; + + width: max-content; +} + +.option-button:hover { + background-color: #fc0303; +} + +.option-button.disabled { + cursor: default; + + background-color: #520000; + color: grey; +} + +#options { + max-width: 60%; + text-align: left; + display: inline-block; +} + +.switch-container:after { + content: attr(label-name); + position: absolute; + padding: 4px; + width: max-content; + + font-size: 14px; + color: white; +} + +.text-label-container { + font-size: 14px; + color: white; +} + +.switch { + position: relative; + display: inline-block; + width: 40px; + height: 24px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #707070; +} + +.animated * { + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; +} + +.animated .slider:before { + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #fc0303; +} + +input:checked + .slider:before { + -webkit-transform: translateX(16px); + -ms-transform: translateX(16px); + transform: translateX(16px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + + +/* Boilerplate CSS from https://ajay.app */ + +body { + background-color: #333333; +} + +.projectPreview { + position: relative; +} + +.projectPreviewImage { + position: absolute; + left: -90px; + width: 80px; + top: 50%; + transform: translateY(-50%); +} + +.projectPreviewImageLarge { + position: absolute; + left: -210px; + width: 200px; + top: 50%; + transform: translateY(-20%); +} + +.projectPreviewImageLargeRight { + position: absolute; + right: -210px; + width: 200px; + top: 50%; + transform: translateY(-50%); +} + +.createdBy { + font-size: 14px; + text-align: center; + padding-top: 0px; + padding-bottom: 0px; + + display: inline-block; +} + +#title { + background-color: #636363; + + text-align: center; + vertical-align: middle; + + font-size: 50px; + color: #212121; + + padding: 20px; + + text-decoration: none; + + transition: font-size 1s; +} + +.subtitle { + font-size: 40px; + color: #dad8d8; + + padding-top: 10px; + + transition: font-size 0.4s; +} + +.subtitle:hover { + font-size: 45px; + + transition: font-size 0.4s; +} + +.profilepic { + background-color: #636363 !important; + vertical-align: middle; +} + +.profilepiccircle { + vertical-align: middle; + overflow: hidden; + border-radius: 50%; +} + +a { + text-decoration: underline; + color: inherit; +} + +.link { + padding: 20px; + + height: 80px; + + transition: height 0.2s; +} + +.link:hover { + height: 95px; + + transition: height 0.2s; +} + +#contact,.smalllink { + font-size: 25px; + color: #e8e8e8; + + text-align: center; + + padding: 10px; +} + +#contact { + text-decoration: none; +} + +p,li { + font-size: 20px; + color: #c4c4c4; + + padding: 10px; +} + +p,li,code,a { + max-width: 60%; + text-align: left; + overflow-wrap: break-word; +} + +@media screen and (orientation:portrait) { + p,li,code,a { + max-width: 100%; + } + + .projectPreviewImage { + position: unset; + width: 130px; + display: block; + margin: auto; + transform: none; + } +} + +.previewImage { + max-height: 200px; +} + +img { + max-width: 100%; + + text-align: center; +} + +#recentPostTitle { + font-size: 30px; + color: #dad8d8; +} + +#recentPostDate { + font-size: 15px; + color: #dad8d8; +} + +h1,h2,h3,h4,h5,h6 { + color: #dad8d8; +} + +svg { + text-decoration: none; +} + +.number-container:before { + content: attr(label-name); + padding-right: 4px; + width: max-content; + + font-size: 14px; + color: white; +} + +/* React styles */ + +.categoryTableElement { + font-size: 16px; + + color: white; +} + +.categoryTableElement > * { + padding-right: 15px; + padding-bottom: 15px; +} + +.categoryOptionsSelector { + background-color: #c00000; + color: white; + + border: none; + font-size: 14px; + padding: 5px; + border-radius: 5px; +} + +.categoryColorTextBox { + width: 60px; + + background: none; + border: none; +} \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index b562c4e7..e44dd28a 100644 --- a/src/background.ts +++ b/src/background.ts @@ -37,6 +37,9 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { case "openHelp": chrome.tabs.create({url: chrome.runtime.getURL('help/index_en.html')}); return; + case "openPage": + chrome.tabs.create({url: chrome.runtime.getURL(request.url)}); + return; case "sendRequest": sendRequestToCustomServer(request.type, request.url, request.data).then(async (response) => { callback({ diff --git a/src/content.ts b/src/content.ts index a7f6e172..0c2abd0f 100644 --- a/src/content.ts +++ b/src/content.ts @@ -258,7 +258,7 @@ async function videoIDChange(id) { try { await utils.wait(() => !!videoInfo, 5000, 1); } catch (err) { - alert(chrome.i18n.getMessage("adblockerIssue") + "\n\n" + chrome.i18n.getMessage("adblockerIssueUnlistedVideosInfo")); + await videoInfoFetchFailed("adblockerIssueUnlistedVideosInfo"); } if (isUnlisted()) { @@ -268,7 +268,11 @@ async function videoIDChange(id) { } // Update whitelist data when the video data is loaded - utils.wait(() => !!videoInfo, 5000, 10).then(whitelistCheck); + utils.wait(() => !!videoInfo, 5000, 10).then(whitelistCheck).catch(() => { + if (Config.config.forceChannelCheck) { + videoInfoFetchFailed("adblockerIssueWhitelist"); + } + }); //setup the preview bar if (previewBar === null) { @@ -727,6 +731,21 @@ async function getVideoInfo(): Promise { } } +async function videoInfoFetchFailed(errorMessage: string): Promise { + console.log("failed\t" + errorMessage) + if (utils.isFirefox()) { + // Attempt to ask permission for youtube.com domain + alert(chrome.i18n.getMessage("youtubePermissionRequest")); + + chrome.runtime.sendMessage({ + message: "openPage", + url: "permissions/index.html#youtube.com" + }); + } else { + alert(chrome.i18n.getMessage("adblockerIssue") + "\n\n" + chrome.i18n.getMessage(errorMessage)); + } +} + function getYouTubeVideoID(url: string) { // For YouTube TV support if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); diff --git a/src/options.ts b/src/options.ts index 186e442a..815aff02 100644 --- a/src/options.ts +++ b/src/options.ts @@ -288,7 +288,7 @@ function invidiousInit(checkbox: HTMLInputElement, option: string) { if (utils.isFirefox()) permissions = []; chrome.permissions.contains({ - origins: utils.getInvidiousInstancesRegex(), + origins: utils.getPermissionRegex(), permissions: permissions }, function (result) { if (result != checkbox.checked) { diff --git a/src/permissions.ts b/src/permissions.ts new file mode 100644 index 00000000..b6a41dce --- /dev/null +++ b/src/permissions.ts @@ -0,0 +1,33 @@ +import Config from "./config"; +import Utils from "./utils"; +const utils = new Utils(); + +// This is needed, if Config is not imported before Utils, things break. +// Probably due to cyclic dependencies +Config.config; + +window.addEventListener('DOMContentLoaded', init); + +async function init() { + utils.localizeHtmlPage(); + + const domains = document.location.hash.replace("#", "").split(","); + + const acceptButton = document.getElementById("acceptPermissionButton"); + acceptButton.addEventListener("click", () => { + chrome.permissions.request({ + origins: utils.getPermissionRegex(domains), + permissions: [] + }, (granted) => { + if (granted) { + alert(chrome.i18n.getMessage("permissionRequestSuccess")); + + chrome.tabs.getCurrent((tab) => { + chrome.tabs.remove(tab.id); + }); + } else { + alert(chrome.i18n.getMessage("permissionRequestFailed")); + } + }); + }); +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 7658cab0..bb42afc5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,10 +3,10 @@ import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContaine import * as CompileConfig from "../config.json"; -class Utils { +export default class Utils { // Contains functions needed from the background script - backgroundScriptContainer: BackgroundScriptContainer | null = null; + backgroundScriptContainer: BackgroundScriptContainer | null; // Used to add content scripts and CSS required js = [ @@ -19,7 +19,7 @@ class Utils { "popup.css" ]; - constructor(backgroundScriptContainer?: BackgroundScriptContainer) { + constructor(backgroundScriptContainer: BackgroundScriptContainer = null) { this.backgroundScriptContainer = backgroundScriptContainer; } @@ -43,6 +43,12 @@ class Utils { }); } + containsPermission(permissions: chrome.permissions.Permissions): Promise { + return new Promise((resolve) => { + chrome.permissions.contains(permissions, resolve) + }); + } + /** * Asks for the optional permissions required for all extra sites. * It also starts the content script registrations. @@ -57,7 +63,7 @@ class Utils { if (this.isFirefox()) permissions = []; chrome.permissions.request({ - origins: this.getInvidiousInstancesRegex(), + origins: this.getPermissionRegex(), permissions: permissions }, async (granted) => { if (granted) { @@ -78,7 +84,6 @@ class Utils { * For now, it is just SB.config.invidiousInstances. */ setupExtraSiteContentScripts(): void { - if (this.isFirefox()) { const firefoxJS = []; for (const file of this.js) { @@ -95,7 +100,7 @@ class Utils { allFrames: true, js: firefoxJS, css: firefoxCSS, - matches: this.getInvidiousInstancesRegex() + matches: this.getPermissionRegex() }; if (this.backgroundScriptContainer) { @@ -106,7 +111,7 @@ class Utils { } else { chrome.declarativeContent.onPageChanged.removeRules(["invidious"], () => { const conditions = []; - for (const regex of this.getInvidiousInstancesRegex()) { + for (const regex of this.getPermissionRegex()) { conditions.push(new chrome.declarativeContent.PageStateMatcher({ pageUrl: { urlMatches: regex } })); @@ -149,7 +154,7 @@ class Utils { } chrome.permissions.remove({ - origins: this.getInvidiousInstancesRegex() + origins: this.getPermissionRegex() }); } @@ -250,16 +255,20 @@ class Utils { } /** - * @returns {String[]} Invidious Instances in regex form + * @returns {String[]} Domains in regex form */ - getInvidiousInstancesRegex(): string[] { - const invidiousInstancesRegex: string[] = []; - for (const url of Config.config.invidiousInstances) { - invidiousInstancesRegex.push("https://*." + url + "/*"); - invidiousInstancesRegex.push("http://*." + url + "/*"); + getPermissionRegex(domains: string[] = []): string[] { + const permissionRegex: string[] = []; + if (domains.length === 0) { + domains = [...Config.config.invidiousInstances]; } - return invidiousInstancesRegex; + for (const url of domains) { + permissionRegex.push("https://*." + url + "/*"); + permissionRegex.push("http://*." + url + "/*"); + } + + return permissionRegex; } generateUserID(length = 36): string { @@ -434,5 +443,3 @@ class Utils { } } - -export default Utils; diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 79fea60b..1d130af0 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -9,7 +9,8 @@ module.exports = env => ({ popup: path.join(__dirname, srcDir + 'popup.ts'), background: path.join(__dirname, srcDir + 'background.ts'), content: path.join(__dirname, srcDir + 'content.ts'), - options: path.join(__dirname, srcDir + 'options.ts') + options: path.join(__dirname, srcDir + 'options.ts'), + permissions: path.join(__dirname, srcDir + 'permissions.ts') }, output: { path: path.join(__dirname, '../dist/js'), From a10d7c338cc5f486351bc350780ffb8377e69b5d Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Tue, 30 Mar 2021 22:50:36 -0400 Subject: [PATCH 04/63] Add preview category Closes https://github.com/ajayyy/SponsorBlock/issues/444 --- config.json.example | 2 +- public/_locales/en/messages.json | 38 +++++++++++++++++++---------- src/config.ts | 42 ++++++++++++++++++++------------ src/content.ts | 24 +++++++++--------- src/js-components/previewBar.ts | 10 ++++---- 5 files changed, 69 insertions(+), 47 deletions(-) diff --git a/config.json.example b/config.json.example index 09cf2bba..0d5db7f5 100644 --- a/config.json.example +++ b/config.json.example @@ -2,5 +2,5 @@ "serverAddress": "https://sponsor.ajay.app", "testingServerAddress": "https://sponsor.ajay.app/test", "serverAddressComment": "This specifies the default SponsorBlock server to connect to", - "categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"] + "categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic"] } diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index fbf06ec7..bf491142 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -449,6 +449,9 @@ "preview": { "message": "Preview" }, + "unsubmitted": { + "message": "Unsubmitted" + }, "inspect": { "message": "Inspect" }, @@ -483,6 +486,21 @@ "category_sponsor_description": { "message": "Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like." }, + "category_selfpromo": { + "message": "Unpaid/Self Promotion" + }, + "category_selfpromo_description": { + "message": "Similar to \"sponsor\" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with." + }, + "category_interaction": { + "message": "Interaction Reminder (Subscribe)" + }, + "category_interaction_description": { + "message": "When there is a short reminder to like, subscribe or follow them in the middle of content. If it is long or about something specific, it should be under self promotion instead." + }, + "category_interaction_short": { + "message": "Interaction Reminder" + }, "category_intro": { "message": "Intermission/Intro Animation" }, @@ -498,20 +516,14 @@ "category_outro_description": { "message": "Credits or when the YouTube endcards appear. Not for conclusions with information." }, - "category_interaction": { - "message": "Interaction Reminder (Subscribe)" + "category_preview": { + "message": "Preview/Recap" }, - "category_interaction_description": { - "message": "When there is a short reminder to like, subscribe or follow them in the middle of content. If it is long or about something specific, it should be under self promotion instead." + "category_preview_description": { + "message": "Quick recap of previous episodes, or a preview of what's coming up later in the current video. Meant for edited together clips, not for spoken summaries." }, - "category_interaction_short": { - "message": "Interaction Reminder" - }, - "category_selfpromo": { - "message": "Unpaid/Self Promotion" - }, - "category_selfpromo_description": { - "message": "Similar to \"sponsor\" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with." + "category_preview_short": { + "message": "Preview" }, "category_music_offtopic": { "message": "Music: Non-Music Section" @@ -541,7 +553,7 @@ "message": "Your color is formatted incorrectly. It should be a 3 or 6 digit hex code with a number sign at the beginning." }, "previewColor": { - "message": "Preview Color", + "message": "Unsubmitted Color", "description": "Referring to submissions that have not been sent to the server yet." }, "seekBarColor": { diff --git a/src/config.ts b/src/config.ts index 2b2ad8ce..962564fd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -45,14 +45,16 @@ interface SBConfig { "preview-chooseACategory": PreviewBarOption, "sponsor": PreviewBarOption, "preview-sponsor": PreviewBarOption, + "selfpromo": PreviewBarOption, + "preview-selfpromo": PreviewBarOption, + "interaction": PreviewBarOption, + "preview-interaction": PreviewBarOption, "intro": PreviewBarOption, "preview-intro": PreviewBarOption, "outro": PreviewBarOption, "preview-outro": PreviewBarOption, - "interaction": PreviewBarOption, - "preview-interaction": PreviewBarOption, - "selfpromo": PreviewBarOption, - "preview-selfpromo": PreviewBarOption, + "preview": PreviewBarOption, + "preview-preview": PreviewBarOption, "music_offtopic": PreviewBarOption, "preview-music_offtopic": PreviewBarOption, } @@ -189,6 +191,22 @@ const Config: SBObject = { color: "#007800", opacity: "0.7" }, + "selfpromo": { + color: "#ffff00", + opacity: "0.7" + }, + "preview-selfpromo": { + color: "#bfbf35", + opacity: "0.7" + }, + "interaction": { + color: "#cc00ff", + opacity: "0.7" + }, + "preview-interaction": { + color: "#6c0087", + opacity: "0.7" + }, "intro": { color: "#00ffff", opacity: "0.7" @@ -205,20 +223,12 @@ const Config: SBObject = { color: "#000070", opacity: "0.7" }, - "interaction": { - color: "#cc00ff", + "preview": { + color: "#ff1684", opacity: "0.7" }, - "preview-interaction": { - color: "#6c0087", - opacity: "0.7" - }, - "selfpromo": { - color: "#ffff00", - opacity: "0.7" - }, - "preview-selfpromo": { - color: "#bfbf35", + "preview-preview": { + color: "#9b044c", opacity: "0.7" }, "music_offtopic": { diff --git a/src/content.ts b/src/content.ts index 0c2abd0f..9d92d259 100644 --- a/src/content.ts +++ b/src/content.ts @@ -816,7 +816,7 @@ function updatePreviewBar(): void { previewBarSegments.push({ segment: segment.segment as [number, number], category: segment.category, - preview: false, + unsubmitted: false, }); }); } @@ -825,7 +825,7 @@ function updatePreviewBar(): void { previewBarSegments.push({ segment: segment.segment as [number, number], category: segment.category, - preview: true, + unsubmitted: true, }); }); @@ -873,14 +873,14 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime)); const endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex); - const previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments); - const previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false); + const unsubmittedSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments); + const unsubmittedSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false); - const minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime)); - const previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex); + const minUnsubmittedSponsorTimeIndex = unsubmittedSponsorStartTimes.indexOf(Math.min(...unsubmittedSponsorStartTimesAfterCurrentTime)); + const previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minUnsubmittedSponsorTimeIndex); - if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) || - sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) { + if ((minUnsubmittedSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) || + sponsorStartTimes[minSponsorTimeIndex] < unsubmittedSponsorStartTimes[minUnsubmittedSponsorTimeIndex]) { return { array: sponsorTimes, index: minSponsorTimeIndex, @@ -890,7 +890,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool } else { return { array: sponsorTimesSubmitting, - index: minPreviewSponsorTimeIndex, + index: minUnsubmittedSponsorTimeIndex, endIndex: previewEndTimeIndex, openNotice: false }; @@ -1007,7 +1007,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S //send telemetry that a this sponsor was skipped if (Config.config.trackViewCount && autoSkip) { let alreadySkipped = false; - let isPreviewSegment = false; + let isUnsubmittedSegment = false; for (const segment of skippingSegments) { const index = sponsorTimes.indexOf(segment); @@ -1019,11 +1019,11 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S alreadySkipped = true; } - if (index === -1) isPreviewSegment = true; + if (index === -1) isUnsubmittedSegment = true; } // Count this as a skip - if (!alreadySkipped && !isPreviewSegment) { + if (!alreadySkipped && !isUnsubmittedSegment) { Config.config.minutesSaved = Config.config.minutesSaved + (skipTime[1] - skipTime[0]) / 60; Config.config.skipCount = Config.config.skipCount + 1; } diff --git a/src/js-components/previewBar.ts b/src/js-components/previewBar.ts index 7f011f0d..5cb8b95f 100644 --- a/src/js-components/previewBar.ts +++ b/src/js-components/previewBar.ts @@ -14,7 +14,7 @@ const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible'; export interface PreviewBarSegment { segment: [number, number]; category: string; - preview: boolean; + unsubmitted: boolean; } class PreviewBar { @@ -117,8 +117,8 @@ class PreviewBar { } else if (segment !== null) { this.tooltipContainer.classList.add(TOOLTIP_VISIBLE_CLASS); - if (segment.preview) { - this.categoryTooltip.textContent = chrome.i18n.getMessage("preview") + " " + utils.shortCategoryName(segment.category); + if (segment.unsubmitted) { + this.categoryTooltip.textContent = chrome.i18n.getMessage("unsubmitted") + " " + utils.shortCategoryName(segment.category); } else { this.categoryTooltip.textContent = utils.shortCategoryName(segment.category); } @@ -185,12 +185,12 @@ class PreviewBar { }); } - createBar({category, preview, segment}: PreviewBarSegment): HTMLLIElement { + createBar({category, unsubmitted, segment}: PreviewBarSegment): HTMLLIElement { const bar = document.createElement('li'); bar.classList.add('previewbar'); bar.innerHTML = ' '; - const barSegmentType = (preview ? 'preview-' : '') + category; + const barSegmentType = (unsubmitted ? 'preview-' : '') + category; bar.setAttribute('data-vs-segment-type', barSegmentType); From edcda31db4581b262fcd8cff4c7a173cb15fee9b Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Thu, 1 Apr 2021 21:15:11 -0400 Subject: [PATCH 05/63] Raise z index of notice to be in front of endcards --- src/components/NoticeComponent.tsx | 2 +- src/components/SkipNoticeComponent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/NoticeComponent.tsx b/src/components/NoticeComponent.tsx index 3867fa0a..24dd3eed 100644 --- a/src/components/NoticeComponent.tsx +++ b/src/components/NoticeComponent.tsx @@ -70,7 +70,7 @@ class NoticeComponent extends React.Component { render(): React.ReactElement { const noticeStyle: React.CSSProperties = { - zIndex: this.props.zIndex || (50 + this.amountOfPreviousNotices) + zIndex: this.props.zIndex || (1000 + this.amountOfPreviousNotices) } return ( diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index 01b2a15e..2990eee3 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -127,7 +127,7 @@ class SkipNoticeComponent extends React.Component Date: Mon, 5 Apr 2021 23:35:05 -0400 Subject: [PATCH 06/63] Add submitting for highlight category --- config.json.example | 2 +- public/_locales/en/messages.json | 25 +++- src/components/CategoryChooserComponent.tsx | 3 +- .../CategorySkipOptionsComponent.tsx | 12 +- src/components/SkipNoticeComponent.tsx | 12 +- src/components/SponsorTimeEditComponent.tsx | 81 ++++++----- src/config.ts | 128 +++--------------- src/content.ts | 19 +-- src/js-components/previewBar.ts | 2 +- src/types.ts | 18 ++- src/utils.ts | 11 +- 11 files changed, 143 insertions(+), 170 deletions(-) diff --git a/config.json.example b/config.json.example index 0d5db7f5..fae36834 100644 --- a/config.json.example +++ b/config.json.example @@ -2,5 +2,5 @@ "serverAddress": "https://sponsor.ajay.app", "testingServerAddress": "https://sponsor.ajay.app/test", "serverAddressComment": "This specifies the default SponsorBlock server to connect to", - "categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic"] + "categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "highlight"] } diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index bf491142..0396ec2d 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -335,9 +335,6 @@ "createdBy": { "message": "Created By" }, - "autoSkip": { - "message": "Auto Skip" - }, "showSkipNotice": { "message": "Show Notice After A Segment Is Skipped" }, @@ -534,14 +531,20 @@ "category_music_offtopic_short": { "message": "Non-Music" }, + "category_highlight": { + "message": "Highlight" + }, + "category_highlight_description": { + "message": "The part of the video that most people are looking for. Similar to \"Video starts at x\" comments." + }, "category_livestream_messages": { "message": "Livestream: Donation/Message Readings" }, "category_livestream_messages_short": { "message": "Message Reading" }, - "disable": { - "message": "Disable" + "autoSkip": { + "message": "Auto Skip" }, "manualSkip": { "message": "Manual Skip" @@ -549,6 +552,18 @@ "showOverlay": { "message": "Show In Seek Bar" }, + "disable": { + "message": "Disable" + }, + "autoSkip_POI": { + "message": "Auto skip to the start" + }, + "manualSkip_POI": { + "message": "Ask when video loads" + }, + "showOverlay_POI": { + "message": "Show In Seek Bar" + }, "colorFormatIncorrect": { "message": "Your color is formatted incorrectly. It should be a 3 or 6 digit hex code with a number sign at the beginning." }, diff --git a/src/components/CategoryChooserComponent.tsx b/src/components/CategoryChooserComponent.tsx index bb86e10f..e5b08923 100644 --- a/src/components/CategoryChooserComponent.tsx +++ b/src/components/CategoryChooserComponent.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import * as CompileConfig from "../../config.json"; +import { Category } from "../types"; import CategorySkipOptionsComponent from "./CategorySkipOptionsComponent"; export interface CategoryChooserProps { @@ -58,7 +59,7 @@ class CategoryChooserComponent extends React.Component ); diff --git a/src/components/CategorySkipOptionsComponent.tsx b/src/components/CategorySkipOptionsComponent.tsx index 3cc03527..a51d7a26 100644 --- a/src/components/CategorySkipOptionsComponent.tsx +++ b/src/components/CategorySkipOptionsComponent.tsx @@ -1,10 +1,13 @@ import * as React from "react"; import Config from "../config" -import { CategorySkipOption } from "../types"; +import { Category, CategorySkipOption } from "../types"; + +import Utils from "../utils"; +const utils = new Utils(); export interface CategorySkipOptionsProps { - category: string; + category: Category; defaultColor?: string; defaultPreviewColor?: string; } @@ -146,10 +149,13 @@ class CategorySkipOptionsComponent extends React.Component - {chrome.i18n.getMessage(optionName)} + {chrome.i18n.getMessage(optionName !== "disable" ? optionName + utils.getCategoryActionType(this.props.category) + : optionName)} ); } diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index 2990eee3..f042bcd5 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -1,10 +1,13 @@ import * as React from "react"; import * as CompileConfig from "../../config.json"; import Config from "../config" -import { ContentContainer, SponsorHideType, SponsorTime } from "../types"; +import { Category, ContentContainer, CategoryActionType, SponsorHideType, SponsorTime } from "../types"; import NoticeComponent from "./NoticeComponent"; import NoticeTextSelectionComponent from "./NoticeTextSectionComponent"; +import Utils from "../utils"; +const utils = new Utils(); + export enum SkipNoticeAction { None, Upvote, @@ -342,7 +345,7 @@ class SkipNoticeComponent extends React.Component utils.getCategoryActionType(cat.name as Category) === CategoryActionType.Skippable)); + for (const category of categories) { elements.push(