diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 48365c37..2437a778 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -68,11 +68,13 @@ jobs:
with:
args: builds/ChromeExtension.zip
name: ChromeExtension.zip
+ path: ./builds/ChromeExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to release
uses: Shopify/upload-to-release@master
with:
args: builds/FirefoxExtension.zip
name: FirefoxExtension.zip
+ path: ./builds/FirefoxExtension.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/manifest/manifest.json b/manifest/manifest.json
index 8830c74a..5b302f1f 100644
--- a/manifest/manifest.json
+++ b/manifest/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "__MSG_Name__",
- "version": "1.2.26",
+ "version": "1.2.27",
"default_locale": "en",
"description": "__MSG_Description__",
"content_scripts": [{
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 7f1c2bf6..086ce0d5 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -189,7 +189,7 @@
"message": "Hide Buttons On YouTube Player"
},
"hideButtonsDescription": {
- "message": "This hides the buttons that appear on the YouTube player to submit sponsors. I can see this being annoying for some\n people. Instead of using the button there, this popup can be used to submit sponsors. To hide the notice that appears, \n use the button that appears on the notice saying \"Don't show this again\". You can always enable these settings again later."
+ "message": "This hides the buttons that appear on the YouTube player to submit skip segments."
},
"showInfoButton": {
"message": "Show Info Button On YouTube Player"
@@ -207,7 +207,7 @@
"message": "Show Delete Button On YouTube Player"
},
"whatDeleteButton": {
- "message": "This is the button that allows you to clear all sponsors on the YouTube player."
+ "message": "This is the button on the YouTube player that will clear all your un-submitted segments for the current video."
},
"disableViewTracking": {
"message": "Disable Sponsor Skip Count Tracking"
@@ -420,10 +420,10 @@
"message": "The video has been detected as unlisted. Click cancel if you do not want to check for sponsors."
},
"unlistedCheck": {
- "message": "Ignore Unlisted Videos"
+ "message": "Ignore Unlisted/Private Videos"
},
"whatUnlistedCheck": {
- "message": "This setting will significantly slow down SponsorBlock. Sponsor lookups require sending the video ID to the server. If you are concerned about unlisted video IDs being sent over the internet, enable this option."
+ "message": "This setting will slightly slow down SponsorBlock. Sponsor lookups require sending the video ID to the server. If you are concerned about unlisted video IDs being sent over the internet, enable this option."
},
"mobileUpdateInfo": {
"message": "m.youtube.com is now supported"
@@ -527,5 +527,32 @@
},
"moreCategories": {
"message": "More Categories"
+ },
+ "bracketEnd": {
+ "message": "(End)"
+ },
+ "hiddenDueToDownvote": {
+ "message": "hidden: downvote"
+ },
+ "hiddenDueToDuration": {
+ "message": "hidden: too short"
+ },
+ "channelDataNotFound": {
+ "message": "Channel ID not loaded yet."
+ },
+ "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"
+ },
+ "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"
+ },
+ "forceChannelCheck": {
+ "message": "Force Channel Check Before Skipping Sponsors"
+ },
+ "whatForceChannelCheck": {
+ "message": "By default, it will skip sponsors right away before it even knows what the channel is. By default, some zero second sponsors might be skipped on whitelisted channels. Enabling this option will prevent this but making all skipping have a slight delay as getting the channelID can take some time. This delay might be unnoticeable if you have fast internet."
+ },
+ "forceChannelCheckPopup": {
+ "message": "Consider Enabling Force Channel Check Before Skipping Sponsors"
}
}
diff --git a/public/content.css b/public/content.css
index 24d8b9ac..51213bd2 100644
--- a/public/content.css
+++ b/public/content.css
@@ -83,6 +83,8 @@
border-spacing: 5px 10px;
padding-left: 5px;
padding-right: 5px;
+
+ border-collapse: unset;
}
.sponsorSkipNoticeFadeIn {
diff --git a/public/popup.css b/public/popup.css
index dccfd881..fab03e3d 100644
--- a/public/popup.css
+++ b/public/popup.css
@@ -39,10 +39,8 @@ sub.popupElement {
vertical-align: text-bottom;
}
-.popupElement {
- font-family: 'Source Sans Pro', sans-serif;
-
- color: black;
+.logoText {
+ color: white;
}
h1.popupElement {
@@ -52,12 +50,21 @@ h1.popupElement {
.popupBody {
font-size: 14px;
- background-color: #ffd9d9;
+ background-color: #333;
padding: 0px 5px;
+
+ font-family: 'Source Sans Pro', sans-serif;
+
+ color: #dddddd;
+}
+
+.outerPopupBody {
+ background-color: #222626;
+ overflow-y: scroll;
}
.discreteLink.popupElement {
- color: black;
+ color: #dddddd;
}
.recordingSubtitle.popupElement {
@@ -102,7 +109,7 @@ h1.popupElement {
}
.whitelistButton.popupElement {
- background-color:#3acc3a;
+ background-color:#27a52d;
-moz-border-radius:28px;
-webkit-border-radius:28px;
border-radius:28px;
@@ -114,13 +121,15 @@ h1.popupElement {
padding:8px 37px;
text-decoration:none;
text-shadow:0px 0px 0px #27663c;
- }
+
+ transition: 0.01s background-color;
+}
.whitelistButton:hover.popupElement {
- background-color:#218b26;
+ background-color:#3acc3a;
}
.whitelistButton:focus.popupElement {
outline: none;
- background-color:#218b26;
+ background-color:#3acc3a;
}
.whitelistButton:active.popupElement {
position:relative;
@@ -128,25 +137,27 @@ h1.popupElement {
}
.greenButton.popupElement {
- background-color:#ec1c1c;
+ background-color:#cc1717;
-moz-border-radius:28px;
-webkit-border-radius:28px;
border-radius:28px;
- border:1px solid #d31919;
+ border: none;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-size:16px;
padding:8px 37px;
text-decoration:none;
- text-shadow:0px 0px 0px #662727;
+ text-shadow:0px 0px 0px #662727;
+
+ transition: 0.01s background-color;
}
.greenButton:hover.popupElement {
- background-color:#bf2a2a;
+ background-color:#ec1c1c;
}
.greenButton:focus.popupElement {
outline: none;
- background-color:#bf2a2a;
+ background-color:#ec1c1c;
}
.greenButton:active.popupElement {
position:relative;
@@ -154,14 +165,11 @@ h1.popupElement {
}
.dangerButton.popupElement {
- -moz-box-shadow:inset 0px 1px 0px 0px #cf866c;
- -webkit-box-shadow:inset 0px 1px 0px 0px #cf866c;
- box-shadow:inset 0px 1px 0px 0px #cf866c;
- background-color:#d0451b;
+ background-color:#bc3315;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
- border:1px solid #942911;
+ border: none;
display:inline-block;
cursor:pointer;
color:#ffffff;
@@ -171,11 +179,11 @@ h1.popupElement {
text-shadow:0px 1px 0px #854629;
}
.dangerButton:hover.popupElement {
- background-color:#bc3315;
+ background-color:#d0451b;
}
.dangerButton:focus.popupElement {
outline: none;
- background-color:#bc3315;
+ background-color:#d0451b;
}
.dangerButton:active.popupElement {
position:relative;
@@ -183,14 +191,11 @@ h1.popupElement {
}
.warningButton.popupElement {
- -moz-box-shadow:inset 0px 1px 0px 0px #cfbd6c;
- -webkit-box-shadow:inset 0px 1px 0px 0px #cfbd6c;
- box-shadow:inset 0px 1px 0px 0px #cfbd6c;
- background-color:#d0821b;
+ background-color:#bc8215;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
- border:1px solid #948b11;
+ border: none;
display:inline-block;
cursor:pointer;
color:#ffffff;
@@ -200,11 +205,11 @@ h1.popupElement {
text-shadow:0px 1px 0px #856829;
}
.warningButton:hover.popupElement {
- background-color:#bc8215;
+ background-color:#d0821b;
}
.warningButton:focus.popupElement {
outline: none;
- background-color:#bc8215;
+ background-color:#d0821b;
}
.warningButton:active.popupElement {
position:relative;
diff --git a/public/popup.html b/public/popup.html
index aa8060a8..3fdb3a73 100644
--- a/public/popup.html
+++ b/public/popup.html
@@ -5,10 +5,10 @@
-
+
-
diff --git a/src/background.ts b/src/background.ts
index 1582327c..b204b64c 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -31,11 +31,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
case "openConfig":
chrome.runtime.openOptionsPage();
return
- case "submitTimes":
- submitTimes(request.videoID, callback);
-
- //this allows the callback to be called later by the submitTimes function
- return true;
case "addSponsorTime":
addSponsorTime(request.time, request.videoID, callback);
@@ -182,61 +177,4 @@ function submitVote(type, UUID, callback) {
}
});
-}
-
-async function submitTimes(videoID: string, callback) {
- //get the video times from storage
- let sponsorTimes = Config.config.sponsorTimes.get(videoID);
- let userID = Config.config.userID;
-
- if (sponsorTimes != undefined && sponsorTimes.length > 0) {
- let durationResult = await new Promise((resolve, reject) => {
- chrome.tabs.query({
- active: true,
- currentWindow: true
- }, function(tabs) {
- chrome.tabs.sendMessage(tabs[0].id, {
- message: "getVideoDuration"
- }, (response) => resolve(response));
- });
- });
-
- //check if a sponsor exceeds the duration of the video
- for (let i = 0; i < sponsorTimes.length; i++) {
- if (sponsorTimes[i][1] > durationResult.duration) {
- sponsorTimes[i][1] = durationResult.duration;
- }
- }
-
- //submit these times
- for (let i = 0; i < sponsorTimes.length; i++) {
- //to prevent it from happeneing twice
- let increasedContributionAmount = false;
-
- //submit the sponsorTime
- utils.sendRequestToServer("GET", "/api/postVideoSponsorTimes?videoID=" + videoID + "&startTime=" + sponsorTimes[i][0] + "&endTime=" + sponsorTimes[i][1]
- + "&userID=" + userID, function(xmlhttp, error) {
- if (xmlhttp.readyState == 4 && !error) {
- callback({
- statusCode: xmlhttp.status,
- responseText: xmlhttp.responseText
- });
-
-
-
- if (xmlhttp.status == 200) {
- //save the amount contributed
- if (!increasedContributionAmount) {
- increasedContributionAmount = true;
- Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimes.length;
- }
- }
- } else if (error) {
- callback({
- statusCode: -1
- });
- }
- });
- }
- }
}
\ No newline at end of file
diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx
index 08d817e6..8d53efbb 100644
--- a/src/components/SkipNoticeComponent.tsx
+++ b/src/components/SkipNoticeComponent.tsx
@@ -1,6 +1,6 @@
import * as React from "react";
import Config from "../config"
-import { ContentContainer } from "../types";
+import { ContentContainer, SponsorHideType } from "../types";
import Utils from "../utils";
var utils = new Utils();
@@ -269,7 +269,7 @@ class SkipNoticeComponent extends React.Component this.setTimeToNow(1)}>
{chrome.i18n.getMessage("bracketNow")}
+
+ this.setTimeToEnd()}>
+ {chrome.i18n.getMessage("bracketEnd")}
+
);
} else {
@@ -227,10 +236,18 @@ class SponsorTimeEditComponent extends React.Component,
- whitelistedChannels: Array,
+ whitelistedChannels: string[],
+ forceChannelCheck: boolean,
startSponsorKeybind: string,
submitKeybind: string,
minutesSaved: number,
@@ -104,6 +108,7 @@ var Config: SBObject = {
userID: null,
sponsorTimes: new SBMap("sponsorTimes"),
whitelistedChannels: [],
+ forceChannelCheck: false,
startSponsorKeybind: ";",
submitKeybind: "'",
minutesSaved: 0,
@@ -236,7 +241,7 @@ function fetchConfig() {
});
}
-function migrateOldFormats() {
+async function migrateOldFormats() {
if (Config.config["disableAutoSkip"]) {
for (const selection of Config.config.categorySelections) {
if (selection.name === "sponsor") {
@@ -246,6 +251,36 @@ function migrateOldFormats() {
}
}
}
+
+ // Channel URLS
+ if (Config.config.whitelistedChannels.length > 0 &&
+ (Config.config.whitelistedChannels[0].includes("/") || Config.config.whitelistedChannels[0] == null)) {
+ let newChannelList: string[] = [];
+ for (const item of Config.config.whitelistedChannels) {
+ if (item != null) {
+ if (item.includes("/channel/")) {
+ newChannelList.push(item.split("/")[2]);
+ } else if (item.includes("/user/") && utils.isContentScript()) {
+ // Replace channel URL with channelID
+ let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
+
+ if (response.ok) {
+ newChannelList.push((await response.json()).authorId);
+ } else {
+ // Add it at the beginning so it gets converted later
+ newChannelList.unshift(item);
+ }
+ } else if (item.includes("/user/")) {
+ // Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues)
+ newChannelList.unshift(item);
+ } else {
+ newChannelList.push(item);
+ }
+ }
+ }
+
+ Config.config.whitelistedChannels = newChannelList;
+ }
}
async function setupConfig() {
diff --git a/src/content.ts b/src/content.ts
index 09fca172..758a5bb4 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -1,6 +1,6 @@
import Config from "./config";
-import { SponsorTime, CategorySkipOption, CategorySelection, VideoID } from "./types";
+import { SponsorTime, CategorySkipOption, CategorySelection, VideoID, SponsorHideType } from "./types";
import { ContentContainer } from "./types";
import Utils from "./utils";
@@ -24,15 +24,17 @@ var sponsorTimes: SponsorTime[] = null;
//what video id are these sponsors for
var sponsorVideoID: VideoID = null;
+// JSON video info
+var videoInfo: any = null;
+//the channel this video is about
+var channelID;
+
// Skips are scheduled to ensure precision.
// Skips are rescheduled every seeking event.
// Skips are canceled every seeking event
var currentSkipSchedule: NodeJS.Timeout = null;
var seekListenerSetUp = false
-//these are sponsors that have been downvoted
-var hiddenSponsorTimes: number[] = [];
-
/** @type {Array[boolean]} Has the sponsor been skipped */
var sponsorSkipped: boolean[] = [];
@@ -59,12 +61,6 @@ var switchingVideos = null;
var lastCheckTime = 0;
var lastCheckVideoTime = -1;
-//the channel this video is about
-var channelURL;
-
-//the title of the last video loaded. Used to make sure the channel URL has been updated yet.
-var title;
-
//is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false;
@@ -110,7 +106,6 @@ var skipNoticeContentContainer: ContentContainer = () => ({
unskipSponsorTime,
sponsorTimes,
sponsorTimesSubmitting,
- hiddenSponsorTimes,
v: video,
sponsorVideoID,
reskipSponsorTime,
@@ -143,8 +138,7 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
//send the sponsor times along with if it's found
sendResponse({
found: sponsorDataFound,
- sponsorTimes: sponsorTimes,
- hiddenSponsorTimes: hiddenSponsorTimes
+ sponsorTimes: sponsorTimes
});
if (popupInitialised && document.getElementById("sponsorBlockPopupContainer") != null) {
@@ -188,9 +182,9 @@ function messageListener(request: any, sender: any, sendResponse: (response: any
});
break;
- case "getChannelURL":
+ case "getChannelID":
sendResponse({
- channelURL: channelURL
+ channelID: channelID
});
break;
@@ -267,6 +261,10 @@ function resetValues() {
sponsorTimes = null;
sponsorLookupRetries = 0;
+ videoInfo = null;
+ channelWhitelisted = false;
+ channelID = null;
+
//empty the preview bar
if (previewBar !== null) {
previewBar.set([], [], 0);
@@ -298,9 +296,16 @@ async function videoIDChange(id) {
// Wait for options to be ready
await utils.wait(() => Config.config !== null, 5000, 1);
+ // Get new video info
+ getVideoInfo();
+
// If enabled, it will check if this video is private or unlisted and double check with the user if the sponsors should be looked up
if (Config.config.checkForUnlistedVideos) {
- await utils.wait(isPrivacyInfoAvailable);
+ try {
+ await utils.wait(() => !!videoInfo, 5000, 1);
+ } catch (err) {
+ alert(chrome.i18n.getMessage("adblockerIssue"));
+ }
if (isUnlisted()) {
let shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
@@ -308,10 +313,8 @@ async function videoIDChange(id) {
}
}
- // TODO: Use a better method here than using type any
- // This is done to be able to do channelIDPromise.isFulfilled and channelIDPromise.isRejected
- let channelIDPromise: any = utils.wait(getChannelID);
- channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true);
+ // Update whitelist data when the video data is loaded
+ utils.wait(() => !!videoInfo, 5000, 10).then(whitelistCheck);
//setup the preview bar
if (previewBar === null) {
@@ -351,7 +354,7 @@ async function videoIDChange(id) {
//close popup
closeInfoMenu();
- sponsorsLookup(id, channelIDPromise);
+ sponsorsLookup(id);
//make sure everything is properly added
updateVisibilityOfPlayerControlsButton().then(() => {
@@ -460,10 +463,12 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
cancelSponsorSchedule();
if (video.paused) return;
- if (Config.config.disableSkipping || channelWhitelisted){
+ if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){
return;
}
+ if (incorrectVideoIDCheck()) return;
+
if (currentTime === undefined || currentTime === null) currentTime = video.currentTime;
let skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments);
@@ -481,27 +486,17 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false;
+ if (incorrectVideoIDCheck()) return;
+
if (video.currentTime >= skipTime[0] && video.currentTime < skipTime[1]) {
- // Double check that the videoID is correct
- // TODO: Remove this bug catching if statement when the bug is found
- let currentVideoID = getYouTubeVideoID(document.URL);
- if (currentVideoID == sponsorVideoID) {
- skipToTime(video, skipInfo.endIndex, skipInfo.array, skipInfo.openNotice);
+ skipToTime(video, skipInfo.endIndex, skipInfo.array, skipInfo.openNotice);
- // TODO: Know the autoSkip settings for ALL items being skipped
- if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ManualSkip) {
- forcedSkipTime = skipTime[0] + 0.001;
- } else {
- forcedSkipTime = skipTime[1];
- forcedIncludeIntersectingSegments = true;
- }
+ // TODO: Know the autoSkip settings for ALL items being skipped
+ if (utils.getCategorySelection(currentSkip.category).option === CategorySkipOption.ManualSkip) {
+ forcedSkipTime = skipTime[0] + 0.001;
} else {
- // Something has really gone wrong
- console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
- console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID);
-
- // Video ID change occured
- videoIDChange(currentVideoID);
+ forcedSkipTime = skipTime[1];
+ forcedIncludeIntersectingSegments = true;
}
}
@@ -515,11 +510,32 @@ function startSponsorSchedule(includeIntersectingSegments: boolean = false, curr
}
}
-function sponsorsLookup(id: string, channelIDPromise?) {
+/**
+ * This makes sure the videoID is still correct
+ *
+ * TODO: Remove this bug catching if statement when the bug is found
+ */
+function incorrectVideoIDCheck(): boolean {
+ let currentVideoID = getYouTubeVideoID(document.URL);
+ if (currentVideoID !== sponsorVideoID) {
+ // Something has really gone wrong
+ console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
+ console.error("[SponsorBlock] VideoID recorded: " + sponsorVideoID + ". Actual VideoID: " + currentVideoID);
+
+ // Video ID change occured
+ videoIDChange(currentVideoID);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+function sponsorsLookup(id: string) {
video = document.querySelector('video') // Youtube video player
//there is no video here
if (video == null) {
- setTimeout(() => sponsorsLookup(id, channelIDPromise), 100);
+ setTimeout(() => sponsorsLookup(id), 100);
return;
}
@@ -578,18 +594,6 @@ function sponsorsLookup(id: string, channelIDPromise?) {
startSponsorSchedule();
}
- if (channelIDPromise !== undefined) {
- if (channelIDPromise.isFulfilled) {
- whitelistCheck();
- } else if (channelIDPromise.isRejected) {
- //try again
- utils.wait(getChannelID).then(whitelistCheck).catch();
- } else {
- //add it as a then statement
- channelIDPromise.then(whitelistCheck);
- }
- }
-
//check database for sponsor times
//made true once a setTimeout has been created to try again after a server error
let recheckStarted = false;
@@ -624,43 +628,16 @@ function sponsorsLookup(id: string, channelIDPromise?) {
sponsorTimes = recievedSegments;
- // Remove all submissions smaller than the minimum duration
+ // Hide all submissions smaller than the minimum duration
if (Config.config.minDuration !== 0) {
- let smallSegments: SponsorTime[] = [];
-
for (let i = 0; i < sponsorTimes.length; i++) {
- if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] >= Config.config.minDuration) {
- smallSegments.push(sponsorTimes[i]);
+ if (sponsorTimes[i].segment[1] - sponsorTimes[i].segment[0] < Config.config.minDuration) {
+ sponsorTimes[i].hidden = SponsorHideType.MinimumDuration;
}
}
-
- sponsorTimes = smallSegments;
}
- if (!switchingVideos) {
- // See if there are any starting sponsors
- let startingSponsor: number = -1;
- for (const time of sponsorTimes) {
- if (time[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
- startingSponsor = time.segment[0];
- break;
- }
- }
- if (!startingSponsor) {
- for (const time of sponsorTimesSubmitting) {
- if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
- startingSponsor = time.segment[0];
- break;
- }
- }
- }
-
- if (startingSponsor !== -1) {
- startSponsorSchedule(false, startingSponsor);
- } else {
- startSponsorSchedule();
- }
- }
+ startSkipScheduleCheckingForStartSponsors();
// Reset skip save
sponsorSkipped = [];
@@ -678,23 +655,13 @@ function sponsorsLookup(id: string, channelIDPromise?) {
sponsorDataFound = false;
//check if this video was uploaded recently
- //use the invidious api to get the time published
- sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + id, function(xmlhttp, error) {
- if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
- let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
+ utils.wait(() => !!videoInfo).then(() => {
+ let dateUploaded = videoInfo.microformat.playerMicroformatRenderer.uploadDate;
- if (decodedData === undefined) {
- console.error("[SB] Failed at getting video upload date info from YouTube.");
- return;
- }
-
- let dateUploaded = JSON.parse(decodedData).microformat.playerMicroformatRenderer.uploadDate;
-
- //if less than 3 days old
- if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
- //TODO lower when server becomes better
- setTimeout(() => sponsorsLookup(id, channelIDPromise), 180000);
- }
+ //if less than 3 days old
+ if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
+ //TODO lower when server becomes better
+ setTimeout(() => sponsorsLookup(id), 180000);
}
});
@@ -704,13 +671,62 @@ function sponsorsLookup(id: string, channelIDPromise?) {
//TODO lower when server becomes better (back to 1 second)
//some error occurred, try again in a second
- setTimeout(() => sponsorsLookup(id, channelIDPromise), 10000);
+ setTimeout(() => sponsorsLookup(id), 10000);
sponsorLookupRetries++;
}
});
}
+/**
+ * Only should be used when it is okay to skip a sponsor when in the middle of it
+ *
+ * Ex. When segments are first loaded
+ */
+function startSkipScheduleCheckingForStartSponsors() {
+ if (!switchingVideos) {
+ // See if there are any starting sponsors
+ let startingSponsor: number = -1;
+ for (const time of sponsorTimes) {
+ if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
+ startingSponsor = time.segment[0];
+ break;
+ }
+ }
+ if (startingSponsor === -1) {
+ for (const time of sponsorTimesSubmitting) {
+ if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
+ startingSponsor = time.segment[0];
+ break;
+ }
+ }
+ }
+
+ if (startingSponsor !== -1) {
+ startSponsorSchedule(false, startingSponsor);
+ } else {
+ startSponsorSchedule();
+ }
+ }
+}
+
+/**
+ * Get the video info for the current tab from YouTube
+ */
+function getVideoInfo() {
+ sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) {
+ if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
+ let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
+ if (!decodedData) {
+ console.error("[SB] Failed at getting video info from YouTube.");
+ return;
+ }
+
+ videoInfo = JSON.parse(decodedData);
+ }
+ });
+}
+
function getYouTubeVideoID(url: string) {
// For YouTube TV support
if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
@@ -753,55 +769,6 @@ function getYouTubeVideoID(url: string) {
return false;
}
-function getChannelID() {
- //get channel id
- let channelURLContainer = null;
-
- channelURLContainer = document.querySelector("#channel-name > #container > #text-container > #text");
- if (channelURLContainer !== null) {
- channelURLContainer = channelURLContainer.firstElementChild;
- } else if (onInvidious) {
- // Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers...
- channelURLContainer = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a");
- } else {
- //old YouTube theme
- let channelContainers = document.getElementsByClassName("yt-user-info");
- if (channelContainers.length != 0) {
- channelURLContainer = channelContainers[0].firstElementChild;
- }
- }
-
- if (channelURLContainer === null) {
- //try later
- return false;
- }
-
- //first get the title to make sure a title change has occurred (otherwise the next video might still be loading)
- let titleInfoContainer = document.getElementById("info-contents");
- let currentTitle = "";
- if (titleInfoContainer != null) {
- currentTitle = ( titleInfoContainer.firstElementChild.firstElementChild.querySelector(".title").firstElementChild).innerText;
- } else if (onInvidious) {
- // Unfortunately, the Invidious HTML doesn't have much in the way of element identifiers...
- currentTitle = document.querySelector("body > div > div.pure-u-1.pure-u-md-20-24 div.pure-u-1.pure-u-lg-3-5 > div > a > div > span").textContent;
- } else {
- //old YouTube theme
- currentTitle = document.getElementById("eow-title").innerText;
- }
-
- if (title == currentTitle) {
- //video hasn't changed yet, wait
- //try later
- return false;
- }
- title = currentTitle;
-
- channelURL = channelURLContainer.getAttribute("href");
-
- //reset variables
- channelWhitelisted = false;
-}
-
/**
* This function is required on mobile YouTube and will keep getting called whenever the preview bar disapears
*/
@@ -822,7 +789,7 @@ function updatePreviewBar() {
//create an array of the sponsor types
let types = [];
for (let i = 0; i < localSponsorTimes.length; i++) {
- if (!hiddenSponsorTimes.includes(i)) {
+ if (localSponsorTimes[i].hidden === SponsorHideType.Visible) {
types.push(localSponsorTimes[i].category);
} else {
// Don't show this sponsor
@@ -841,12 +808,17 @@ function updatePreviewBar() {
//checks if this channel is whitelisted, should be done only after the channelID has been loaded
function whitelistCheck() {
+ channelID = videoInfo.videoDetails.channelId;
+
//see if this is a whitelisted channel
let whitelistedChannels = Config.config.whitelistedChannels;
- if (whitelistedChannels != undefined && whitelistedChannels.includes(channelURL)) {
+ if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
channelWhitelisted = true;
}
+
+ // check if the start of segments were missed
+ if (sponsorTimes && sponsorTimes.length > 0) startSkipScheduleCheckingForStartSponsors();
}
/**
@@ -856,13 +828,13 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
{array: SponsorTime[], index: number, endIndex: number, openNotice: boolean} {
let sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments);
- let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, currentTime, true);
+ let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, currentTime, true, true);
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
let endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments);
- let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, currentTime, false);
+ let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, currentTime, true, false);
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
let previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
@@ -911,7 +883,7 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
let latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime
- && (!hideHiddenSponsors || !hiddenSponsorTimes.includes(i))
+ && (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)
&& utils.getCategorySelection(sponsorTimes[i].category).option === CategorySkipOption.AutoSkip) {
// Overlapping segment
latestEndTimeIndex = i;
@@ -937,14 +909,16 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
* the current time, but end after
*/
function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments: boolean, minimum?: number,
- hideHiddenSponsors: boolean = false): number[] {
+ onlySkippableSponsors: boolean = false, hideHiddenSponsors: boolean = false): number[] {
if (sponsorTimes === null) return [];
let startTimes: number[] = [];
for (let i = 0; i < sponsorTimes.length; i++) {
if ((minimum === undefined || (sponsorTimes[i].segment[0] >= minimum || (includeIntersectingSegments && sponsorTimes[i].segment[1] > minimum)))
- && (!hideHiddenSponsors || !hiddenSponsorTimes.includes(i))) {
+ && (!onlySkippableSponsors || utils.getCategorySelection(sponsorTimes[i].category).option !== CategorySkipOption.ShowOverlay)
+ && (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)) {
+
startTimes.push(sponsorTimes[i].segment[0]);
}
}
@@ -1490,6 +1464,9 @@ async function sendSubmitMessage(){
sponsorTimes = sponsorTimes.concat(sponsorTimesSubmitting);
+ // Increase contribution count
+ Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
+
// Empty the submitting times
sponsorTimesSubmitting = [];
@@ -1525,30 +1502,12 @@ function getSegmentsMessage(segments: number[][]): string {
return sponsorTimesMessage;
}
-// Privacy utils
-function isPrivacyInfoAvailable(): boolean {
- if(document.location.pathname.startsWith("/embed/")) return true;
- return document.getElementsByClassName("style-scope ytd-badge-supported-renderer").length >= 2;
-}
-
-/**
- * What privacy level is this YouTube video?
- */
-function getPrivacy(): string {
- if(document.location.pathname.startsWith("/embed/")) return "Public";
-
- let privacyElement = document.getElementsByClassName("style-scope ytd-badge-supported-renderer")[2];
- return privacyElement.innerText;
-}
-
/**
* Is this an unlisted YouTube video.
* Assumes that the the privacy info is available.
*/
function isUnlisted(): boolean {
- let privacyElement = document.getElementsByClassName("style-scope ytd-badge-supported-renderer")[2];
-
- return privacyElement.innerText.toLocaleLowerCase() === "unlisted";
+ return videoInfo.microformat.playerMicroformatRenderer.isUnlisted || videoInfo.videoDetails.isPrivate;
}
/**
diff --git a/src/popup.ts b/src/popup.ts
index 14a75a94..914dddc5 100644
--- a/src/popup.ts
+++ b/src/popup.ts
@@ -1,7 +1,7 @@
import Config from "./config";
import Utils from "./utils";
-import { SponsorTime } from "./types";
+import { SponsorTime, SponsorHideType } from "./types";
var utils = new Utils();
interface MessageListener {
@@ -51,6 +51,7 @@ async function runThePopup(messageListener?: MessageListener) {
// Top toggles
"whitelistChannel",
"unwhitelistChannel",
+ "whitelistForceCheck",
"disableSkipping",
"enableSkipping",
// Options
@@ -102,6 +103,7 @@ async function runThePopup(messageListener?: MessageListener) {
//setup click listeners
PageElements.sponsorStart.addEventListener("click", sendSponsorStartMessage);
PageElements.whitelistChannel.addEventListener("click", whitelistChannel);
+ PageElements.whitelistForceCheck.addEventListener("click", openOptions);
PageElements.unwhitelistChannel.addEventListener("click", unwhitelistChannel);
PageElements.disableSkipping.addEventListener("click", () => toggleSkipping(true));
PageElements.enableSkipping.addEventListener("click", () => toggleSkipping(false));
@@ -273,7 +275,7 @@ async function runThePopup(messageListener?: MessageListener) {
);
}
- function infoFound(request: {found: boolean, sponsorTimes: SponsorTime[], hiddenSponsorTimes: number[]}) {
+ function infoFound(request: {found: boolean, sponsorTimes: SponsorTime[]}) {
if(chrome.runtime.lastError) {
//This page doesn't have the injected content script, or at least not yet
displayNoVideo();
@@ -364,7 +366,7 @@ async function runThePopup(messageListener?: MessageListener) {
}
//display the video times from the array at the top, in a different section
- function displayDownloadedSponsorTimes(request: {found: boolean, sponsorTimes: SponsorTime[], hiddenSponsorTimes: number[]}) {
+ function displayDownloadedSponsorTimes(request: {found: boolean, sponsorTimes: SponsorTime[]}) {
if (request.sponsorTimes != undefined) {
//set it to the message
if (PageElements.downloadedSponsorMessageTimes.innerText != chrome.i18n.getMessage("channelWhitelisted")) {
@@ -378,9 +380,12 @@ async function runThePopup(messageListener?: MessageListener) {
sponsorTimeButton.className = "warningButton popupElement";
let extraInfo = "";
- if (request.hiddenSponsorTimes.includes(i)) {
- //this one is hidden
- extraInfo = " (hidden)";
+ if (request.sponsorTimes[i].hidden === SponsorHideType.Downvoted) {
+ //this one is downvoted
+ extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")";
+ } else if (request.sponsorTimes[i].hidden === SponsorHideType.MinimumDuration) {
+ //this one is too short
+ extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")";
}
sponsorTimeButton.innerText = getFormattedTime(request.sponsorTimes[i].segment[0]) + " to " + getFormattedTime(request.sponsorTimes[i].segment[1]) + extraInfo;
@@ -444,6 +449,14 @@ async function runThePopup(messageListener?: MessageListener) {
//add commas if necessary
timeMessage = ", " + timeMessage;
}
+
+ if (sponsorTimes[i].hidden === SponsorHideType.Downvoted) {
+ //this one is downvoted
+ timeMessage += " (" + chrome.i18n.getMessage("hiddenDueToDownvote") + ")";
+ } else if (sponsorTimes[i].hidden === SponsorHideType.MinimumDuration) {
+ //this one is too short
+ timeMessage += " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")";
+ }
sponsorTimesMessage += timeMessage;
}
@@ -908,39 +921,46 @@ async function runThePopup(messageListener?: MessageListener) {
}, tabs => {
messageHandler.sendMessage(
tabs[0].id,
- {message: 'getChannelURL'},
+ {message: 'getChannelID'},
function(response) {
+ if (!response.channelID) {
+ alert(chrome.i18n.getMessage("channelDataNotFound") + "\n\n" +
+ chrome.i18n.getMessage("itCouldBeAdblockerIssue"));
+ return;
+ }
+
//get whitelisted channels
- let whitelistedChannels = Config.config.whitelistedChannels;
- if (whitelistedChannels == undefined) {
- whitelistedChannels = [];
+ let whitelistedChannels = Config.config.whitelistedChannels;
+ if (whitelistedChannels == undefined) {
+ whitelistedChannels = [];
+ }
+
+ //add on this channel
+ whitelistedChannels.push(response.channelID);
+
+ //change button
+ PageElements.whitelistChannel.style.display = "none";
+ PageElements.unwhitelistChannel.style.display = "unset";
+ if (!Config.config.forceChannelCheck) PageElements.whitelistForceCheck.style.display = "unset";
+
+ PageElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
+ PageElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
+
+ //save this
+ Config.config.whitelistedChannels = whitelistedChannels;
+
+ //send a message to the client
+ messageHandler.query({
+ active: true,
+ currentWindow: true
+ }, tabs => {
+ messageHandler.sendMessage(
+ tabs[0].id, {
+ message: 'whitelistChange',
+ value: true
+ });
}
-
- //add on this channel
- whitelistedChannels.push(response.channelURL);
-
- //change button
- PageElements.whitelistChannel.style.display = "none";
- PageElements.unwhitelistChannel.style.display = "unset";
-
- PageElements.downloadedSponsorMessageTimes.innerText = chrome.i18n.getMessage("channelWhitelisted");
- PageElements.downloadedSponsorMessageTimes.style.fontWeight = "bold";
-
- //save this
- Config.config.whitelistedChannels = whitelistedChannels;
-
- //send a message to the client
- messageHandler.query({
- active: true,
- currentWindow: true
- }, tabs => {
- messageHandler.sendMessage(
- tabs[0].id, {
- message: 'whitelistChange',
- value: true
- });
- }
- );
+ );
}
);
});
@@ -954,7 +974,7 @@ async function runThePopup(messageListener?: MessageListener) {
}, tabs => {
messageHandler.sendMessage(
tabs[0].id,
- {message: 'getChannelURL'},
+ {message: 'getChannelID'},
function(response) {
//get whitelisted channels
let whitelistedChannels = Config.config.whitelistedChannels;
@@ -963,7 +983,7 @@ async function runThePopup(messageListener?: MessageListener) {
}
//remove this channel
- let index = whitelistedChannels.indexOf(response.channelURL);
+ let index = whitelistedChannels.indexOf(response.channelID);
whitelistedChannels.splice(index, 1);
//change button
diff --git a/src/types.ts b/src/types.ts
index f61e8d69..befa1fc0 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -8,7 +8,6 @@ interface ContentContainer {
unskipSponsorTime: (UUID: any) => void,
sponsorTimes: SponsorTime[],
sponsorTimesSubmitting: SponsorTime[],
- hiddenSponsorTimes: number[],
v: HTMLVideoElement,
sponsorVideoID,
reskipSponsorTime: (UUID: any) => void,
@@ -36,11 +35,19 @@ interface CategorySelection {
option: CategorySkipOption
}
+enum SponsorHideType {
+ Visible = undefined,
+ Downvoted = 1,
+ MinimumDuration
+}
+
interface SponsorTime {
segment: number[];
UUID: string;
category: string;
+
+ hidden?: SponsorHideType;
}
type VideoID = string;
@@ -51,5 +58,6 @@ export {
CategorySelection,
CategorySkipOption,
SponsorTime,
- VideoID
+ VideoID,
+ SponsorHideType
};
\ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
index ce436d1e..a634112c 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -270,27 +270,26 @@ class Utils {
}
/**
- * Sends a request to the SponsorBlock server with address added as a query
+ * Sends a request to a custom server
*
* @param type The request type. "GET", "POST", etc.
* @param address The address to add to the SponsorBlock server address
* @param callback
*/
- async asyncRequestToServer(type: string, address: string, data = {}) {
- let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
+ async asyncRequestToCustomServer(type: string, url: string, data = {}) {
// If GET, convert JSON to parameters
if (type.toLowerCase() === "get") {
for (const key in data) {
- let seperator = address.includes("?") ? "&" : "?";
+ let seperator = url.includes("?") ? "&" : "?";
let value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
- address += seperator + key + "=" + value;
+ url += seperator + key + "=" + value;
}
data = null;
}
- const response = await fetch(serverAddress + address, {
+ const response = await fetch(url, {
method: type,
headers: {
'Content-Type': 'application/json'
@@ -302,6 +301,19 @@ class Utils {
return response;
}
+ /**
+ * Sends a request to the SponsorBlock server with address added as a query
+ *
+ * @param type The request type. "GET", "POST", etc.
+ * @param address The address to add to the SponsorBlock server address
+ * @param callback
+ */
+ async asyncRequestToServer(type: string, address: string, data = {}) {
+ let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
+
+ return await (this.asyncRequestToCustomServer(type, serverAddress + address, data));
+ }
+
/**
* Sends a request to the SponsorBlock server with address added as a query
*
@@ -361,10 +373,14 @@ class Utils {
return minutes * 60 + seconds;
}
+ isContentScript(): boolean {
+ return window.location.protocol === "http:" || window.location.protocol === "https:";
+ }
+
/**
* Is this Firefox (web-extensions)
*/
- isFirefox() {
+ isFirefox(): boolean {
return typeof(browser) !== "undefined";
}
}