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

# Conflicts:
#	manifest/manifest.json
#	src/components/SponsorTimeEditComponent.tsx
#	src/config.ts
#	src/content.ts
#	src/types.ts
This commit is contained in:
Ajay Ramachandran
2021-05-22 01:28:24 -04:00
57 changed files with 1383 additions and 828 deletions

View File

@@ -1,7 +1,5 @@
import Config from "./config";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject, CategoryActionType, SegmentUUID, Category } from "./types";
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject, CategoryActionType, ChannelIDInfo, ChannelIDStatus, SegmentUUID, Category } from "./types";
import { ContentContainer } from "./types";
import Utils from "./utils";
const utils = new Utils();
@@ -29,7 +27,7 @@ const skipNotices: SkipNotice[] = [];
// JSON video info
let videoInfo: VideoInfo = null;
//the channel this video is about
let channelID: string;
let channelIDInfo: ChannelIDInfo;
// Skips are scheduled to ensure precision.
// Skips are rescheduled every seeking event.
@@ -71,8 +69,11 @@ let channelWhitelisted = false;
// create preview bar
let previewBar: PreviewBar = null;
//the player controls on the YouTube player
let controls = null;
/** Element containing the player controls on the YouTube player. */
let controls: HTMLElement | null = null;
/** Contains buttons created by `createButton()`. */
const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImageElement}> = {};
// Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
@@ -81,10 +82,7 @@ utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYo
//this only happens if there is an error
let sponsorLookupRetries = 0;
//if showing the start sponsor button or the end sponsor button on the player
let showingStartSponsor = true;
//the sponsor times being prepared to be submitted
/** Segments created by the user which have not yet been submitted. */
let sponsorTimesSubmitting: SponsorTime[] = [];
//becomes true when isInfoFound is called
@@ -111,12 +109,15 @@ const skipNoticeContentContainer: ContentContainer = () => ({
onMobileYouTube,
sponsorSubmissionNotice: submissionNotice,
resetSponsorSubmissionNotice,
changeStartSponsorButton,
updateEditButtonsOnPlayer,
previewTime,
videoInfo,
getRealCurrentTime: getRealCurrentTime
});
// value determining when to count segment as skipped and send telemetry to server (percent based)
const manualSkipPercentCount = 0.5;
//get messages from the background script and the popup
chrome.runtime.onMessage.addListener(messageListener);
@@ -127,11 +128,11 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
videoIDChange(getYouTubeVideoID(document.URL));
break;
case "sponsorStart":
sponsorMessageStarted(sendResponse);
startOrEndTimingNewSegment()
break;
case "sponsorDataChanged":
updateSponsorTimesSubmitting();
sendResponse({
creatingSegment: isSegmentCreationInProgress(),
});
break;
case "isInfoFound":
@@ -150,13 +151,14 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
break;
case "getVideoID":
sendResponse({
videoID: sponsorVideoID
videoID: sponsorVideoID,
creatingSegment: isSegmentCreationInProgress(),
});
break;
case "getChannelID":
sendResponse({
channelID: channelID
channelID: channelIDInfo.id
});
break;
@@ -170,10 +172,6 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
channelWhitelisted = request.value;
sponsorsLookup(sponsorVideoID);
break;
case "changeStartSponsorButton":
changeStartSponsorButton(request.showStartSponsor, request.uploadButtonVisible);
break;
case "submitTimes":
submitSponsorTimes();
@@ -212,7 +210,10 @@ function resetValues() {
videoInfo = null;
channelWhitelisted = false;
channelID = null;
channelIDInfo = {
status: ChannelIDStatus.Fetching,
id: null
};
//empty the preview bar
if (previewBar !== null) {
@@ -250,29 +251,25 @@ 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) {
try {
await utils.wait(() => !!videoInfo, 5000, 1);
} catch (err) {
await videoInfoFetchFailed("adblockerIssueUnlistedVideosInfo");
}
if (isUnlisted()) {
const shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
if(!shouldContinue) return;
const shouldContinue = confirm("SponsorBlock: You have the setting 'Ignore Unlisted/Private Videos' enabled."
+ " Due to a change in how segment fetching works, this setting is not needed anymore as it cannot leak your video ID to the server."
+ " It instead sends just the first 4 characters of a longer hash of the videoID to the server, and filters through a subset of the database."
+ " More info about this implementation can be found here: https://github.com/ajayyy/SponsorBlockServer/issues/25"
+ "\n\nPlease click okay to confirm that you acknowledge this and continue using SponsorBlock.");
if (shouldContinue) {
Config.config.checkForUnlistedVideos = false;
} else {
return;
}
}
// Get new video info
getVideoInfo();
// Update whitelist data when the video data is loaded
utils.wait(() => !!videoInfo, 5000, 10).then(whitelistCheck).catch(() => {
if (Config.config.forceChannelCheck) {
videoInfoFetchFailed("adblockerIssueWhitelist");
}
});
whitelistCheck();
//setup the preview bar
if (previewBar === null) {
@@ -298,33 +295,20 @@ async function videoIDChange(id) {
//close popup
closeInfoMenu();
sponsorsLookup(id);
//make sure everything is properly added
updateVisibilityOfPlayerControlsButton().then(() => {
//see if the onvideo control image needs to be changed
const segments = Config.config.segmentTimes.get(sponsorVideoID);
if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
changeStartSponsorButton(true, true);
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) {
changeStartSponsorButton(false, true);
} else {
changeStartSponsorButton(true, false);
}
});
// Make sure all player buttons are properly added
updateVisibilityOfPlayerControlsButton();
//reset sponsor times submitting
// Clear unsubmitted segments from the previous video
sponsorTimesSubmitting = [];
updateSponsorTimesSubmitting();
//see if video controls buttons should be added
if (!onInvidious) {
updateVisibilityOfPlayerControlsButton();
}
}
function handleMobileControlsMutations(): void {
updateVisibilityOfPlayerControlsButton();
if (previewBar !== null) {
if (document.body.contains(previewBar.container)) {
const progressBarBackground = document.querySelector<HTMLElement>(".progress-bar-background");
@@ -409,7 +393,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
if (video.paused) return;
if (Config.config.disableSkipping || channelWhitelisted || (channelID === null && Config.config.forceChannelCheck)){
if (Config.config.disableSkipping || channelWhitelisted || (channelIDInfo.status === ChannelIDStatus.Fetching && Config.config.forceChannelCheck)){
return;
}
@@ -577,33 +561,22 @@ async function sponsorsLookup(id: string) {
}
// Check for hashPrefix setting
let getRequest;
if (Config.config.hashPrefix) {
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
getRequest = utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
categories
});
} else {
getRequest = utils.asyncRequestToServer('GET', "/api/skipSegments", {
videoID: id,
categories
});
}
getRequest.then(async (response: FetchResponse) => {
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
categories
}).then(async (response: FetchResponse) => {
if (response?.ok) {
let result = JSON.parse(response.responseText);
if (Config.config.hashPrefix) {
result = result.filter((video) => video.videoID === id);
if (result.length > 0) {
result = result[0].segments;
if (result.length === 0) { // return if no segments found
retryFetch(id);
return;
}
} else { // return if no video found
result = result.filter((video) => video.videoID === id);
if (result.length > 0) {
result = result[0].segments;
if (result.length === 0) { // return if no segments found
retryFetch(id);
return;
}
} else { // return if no video found
retryFetch(id);
return;
}
const recievedSegments: SponsorTime[] = result;
@@ -733,20 +706,6 @@ async function getVideoInfo(): Promise<void> {
}
}
async function videoInfoFetchFailed(errorMessage: string): Promise<void> {
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("#", "");
@@ -843,23 +802,34 @@ function updatePreviewBar(): void {
}
//checks if this channel is whitelisted, should be done only after the channelID has been loaded
function whitelistCheck() {
channelID = videoInfo?.videoDetails?.channelId;
if (!channelID) {
channelID = null;
async function whitelistCheck() {
const whitelistedChannels = Config.config.whitelistedChannels;
const channelID = document.querySelector(".ytd-channel-name a")?.getAttribute("href")?.replace(/\/.+\//, "") // YouTube
?? document.querySelector(".ytp-title-channel-logo")?.getAttribute("href")?.replace(/https:\/.+\//, "") // YouTube Embed
?? document.querySelector("a > .channel-profile")?.parentElement?.getAttribute("href")?.replace(/\/.+\//, ""); // Invidious
if (channelID) {
channelIDInfo = {
status: ChannelIDStatus.Found,
id: channelID
}
} else {
channelIDInfo = {
status: ChannelIDStatus.Failed,
id: null
}
return;
}
//see if this is a whitelisted channel
const whitelistedChannels = Config.config.whitelistedChannels;
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
channelWhitelisted = true;
}
// check if the start of segments were missed
if (Config.config.forceChannelCheck && sponsorTimes && sponsorTimes.length > 0) startSkipScheduleCheckingForStartSponsors();
if (Config.config.forceChannelCheck && sponsorTimes?.length > 0) startSkipScheduleCheckingForStartSponsors();
}
/**
@@ -984,6 +954,26 @@ function previewTime(time: number, unpause = true) {
}
}
//send telemetry and count skip
function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped: number, fullSkip: boolean) {
if (!Config.config.trackViewCount) return;
let counted = false;
for (const segment of skippingSegments) {
const index = sponsorTimes.indexOf(segment);
if (index !== -1 && !sponsorSkipped[index]) {
sponsorSkipped[index] = true;
if (!counted) {
Config.config.minutesSaved = Config.config.minutesSaved + secondsSkipped / 60;
Config.config.skipCount = Config.config.skipCount + 1;
counted = true;
}
if (fullSkip) utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
}
}
}
//skip from the start time to the end time for a certain index sponsor time
function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
// There will only be one submission if it is manual skip
@@ -1008,29 +998,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 isUnsubmittedSegment = false;
for (const segment of skippingSegments) {
const index = sponsorTimes.indexOf(segment);
if (index !== -1 && !sponsorSkipped[index]) {
utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
sponsorSkipped[index] = true;
} else if (sponsorSkipped[index]) {
alreadySkipped = true;
}
if (index === -1) isUnsubmittedSegment = true;
}
// Count this as a skip
if (!alreadySkipped && !isUnsubmittedSegment) {
Config.config.minutesSaved = Config.config.minutesSaved + (skipTime[1] - skipTime[0]) / 60;
Config.config.skipCount = Config.config.skipCount + 1;
}
}
if (autoSkip) sendTelemetryAndCount(skippingSegments, skipTime[1] - skipTime[0], true);
}
function unskipSponsorTime(segment: SponsorTime) {
@@ -1039,13 +1007,18 @@ function unskipSponsorTime(segment: SponsorTime) {
}
function reskipSponsorTime(segment: SponsorTime) {
const skippedTime = Math.max(segment.segment[1] - video.currentTime, 0);
const segmentDuration = segment.segment[1] - segment.segment[0];
const fullSkip = skippedTime / segmentDuration > manualSkipPercentCount;
video.currentTime = segment.segment[1];
sendTelemetryAndCount([segment], skippedTime, fullSkip);
startSponsorSchedule(true, segment.segment[1], false);
}
function createButton(baseID, title, callback, imageName, isDraggable=false): boolean {
if (document.getElementById(baseID + "Button") != null) return false;
function createButton(baseID: string, title: string, callback: () => void, imageName: string, isDraggable = false): HTMLElement {
const existingElement = document.getElementById(baseID + "Button");
if (existingElement !== null) return existingElement;
// Button HTML
const newButton = document.createElement("button");
@@ -1069,9 +1042,15 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
newButton.appendChild(newButtonImage);
// Add the button to player
controls.prepend(newButton);
if (controls) controls.prepend(newButton);
return true;
// Store the elements to prevent unnecessary querying
playerButtons[baseID] = {
button: newButton,
image: newButtonImage,
};
return newButton;
}
function getControls(): HTMLElement | false {
@@ -1081,8 +1060,8 @@ function getControls(): HTMLElement | false {
// Mobile YouTube
".player-controls-top",
// Invidious/videojs video element's controls element
".vjs-control-bar"
]
".vjs-control-bar",
];
for (const controlsSelector of controlsSelectors) {
const controls = document.querySelectorAll(controlsSelector);
@@ -1095,53 +1074,75 @@ function getControls(): HTMLElement | false {
return false;
}
//adds all the player controls buttons
async function createButtons(): Promise<boolean> {
/** Creates any missing buttons on the YouTube player if possible. */
async function createButtons(): Promise<void> {
if (onMobileYouTube) return;
const result = await utils.wait(getControls).catch();
//set global controls variable
controls = result;
let createdButton = false;
controls = await utils.wait(getControls).catch();
// Add button if does not already exist in html
createdButton = createButton("startSponsor", "sponsorStart", startSponsorClicked, "PlayerStartIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("delete", "clearTimes", clearSponsorTimes, "PlayerDeleteIconSponsorBlocker256px.png") || createdButton;
createdButton = createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png") || createdButton;
return createdButton;
createButton("startSponsor", "sponsorStart", () => closeInfoMenuAnd(() => startOrEndTimingNewSegment()), "PlayerStartIconSponsorBlocker.svg");
createButton("cancelSponsor", "sponsorCancel", () => closeInfoMenuAnd(() => cancelCreatingSegment()), "PlayerCancelSegmentIconSponsorBlocker.svg");
createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker.svg");
createButton("delete", "clearTimes", () => closeInfoMenuAnd(() => clearSponsorTimes()), "PlayerDeleteIconSponsorBlocker.svg");
createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker.svg");
}
//adds or removes the player controls button to what it should be
async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
//not on a proper video yet
if (!sponsorVideoID) return false;
/** Creates any missing buttons on the player and updates their visiblity. */
async function updateVisibilityOfPlayerControlsButton(): Promise<void> {
// Not on a proper video yet
if (!sponsorVideoID) return;
const createdButtons = await createButtons();
if (!createdButtons) return;
await createButtons();
if (Config.config.hideVideoPlayerControls || onInvidious) {
document.getElementById("startSponsorButton").style.display = "none";
document.getElementById("submitButton").style.display = "none";
} else {
document.getElementById("startSponsorButton").style.removeProperty("display");
}
updateEditButtonsOnPlayer();
//don't show the info button on embeds
// Don't show the info button on embeds
if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious) {
document.getElementById("infoButton").style.display = "none";
playerButtons.info.button.style.display = "none";
} else {
document.getElementById("infoButton").style.removeProperty("display");
playerButtons.info.button.style.removeProperty("display");
}
if (Config.config.hideDeleteButtonPlayerControls || onInvidious) {
document.getElementById("deleteButton").style.display = "none";
}
/** Updates the visibility of buttons on the player related to creating segments. */
function updateEditButtonsOnPlayer(): void {
// Don't try to update the buttons if we aren't on a YouTube video page
if (!sponsorVideoID) return;
const buttonsEnabled = !Config.config.hideVideoPlayerControls && !onInvidious;
let creatingSegment = false;
let submitButtonVisible = false;
let deleteButtonVisible = false;
// Only check if buttons should be visible if they're enabled
if (buttonsEnabled) {
creatingSegment = isSegmentCreationInProgress();
// Show only if there are any segments to submit
submitButtonVisible = sponsorTimesSubmitting.length > 0;
// Show only if there are any segments to delete
deleteButtonVisible = sponsorTimesSubmitting.length > 1 || (sponsorTimesSubmitting.length > 0 && !creatingSegment);
}
return createdButtons;
// Update the elements
playerButtons.startSponsor.button.style.display = buttonsEnabled ? "unset" : "none";
playerButtons.cancelSponsor.button.style.display = buttonsEnabled && creatingSegment ? "unset" : "none";
if (buttonsEnabled) {
if (creatingSegment) {
playerButtons.startSponsor.image.src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker.svg");
playerButtons.startSponsor.button.setAttribute("title", chrome.i18n.getMessage("sponsorEnd"));
} else {
playerButtons.startSponsor.image.src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker.svg");
playerButtons.startSponsor.button.setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
}
}
playerButtons.submit.button.style.display = submitButtonVisible && !Config.config.hideUploadButtonPlayerControls ? "unset" : "none";
playerButtons.delete.button.style.display = deleteButtonVisible && !Config.config.hideDeleteButtonPlayerControls ? "unset" : "none";
}
/**
@@ -1162,30 +1163,48 @@ function getRealCurrentTime(): number {
}
}
function startSponsorClicked() {
//it can't update to this info yet
closeInfoMenu();
toggleStartSponsorButton();
//add to sponsorTimes
if (sponsorTimesSubmitting.length > 0 && sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.length < 2) {
//it is an end time
sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment[1] = getRealCurrentTime();
sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1].segment.sort((a, b) => a > b ? 1 : (a < b ? -1 : 0));
} else {
//it is a start time
function startOrEndTimingNewSegment() {
if (!isSegmentCreationInProgress()) {
sponsorTimesSubmitting.push({
segment: [getRealCurrentTime()],
UUID: null,
category: Config.config.defaultCategory
category: Config.config.defaultCategory,
});
} else {
// Finish creating the new segment
const existingSegment = getIncompleteSegment();
const existingTime = existingSegment.segment[0];
const currentTime = getRealCurrentTime();
// Swap timestamps if the user put the segment end before the start
existingSegment.segment = [Math.min(existingTime, currentTime), Math.max(existingTime, currentTime)];
}
//save this info
// Save the newly created segment
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
updateSponsorTimesSubmitting(false)
updateEditButtonsOnPlayer();
updateSponsorTimesSubmitting(false);
}
function getIncompleteSegment(): SponsorTime {
return sponsorTimesSubmitting[sponsorTimesSubmitting.length - 1];
}
/** Is the latest submitting segment incomplete */
function isSegmentCreationInProgress(): boolean {
const segment = getIncompleteSegment();
return segment && segment?.segment?.length !== 2;
}
function cancelCreatingSegment() {
if (isSegmentCreationInProgress()) {
sponsorTimesSubmitting.splice(sponsorTimesSubmitting.length - 1, 1);
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
}
updateEditButtonsOnPlayer();
updateSponsorTimesSubmitting(false);
}
function updateSponsorTimesSubmitting(getFromConfig = true) {
@@ -1214,31 +1233,6 @@ function updateSponsorTimesSubmitting(getFromConfig = true) {
}
}
async function changeStartSponsorButton(showStartSponsor: boolean, uploadButtonVisible: boolean): Promise<boolean> {
if(!sponsorVideoID || onMobileYouTube) return false;
//if it isn't visible, there is no data
const shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
document.getElementById("deleteButton").style.display = shouldHide;
if (!Config.config.hideUploadButtonPlayerControls && !onInvidious) {
document.getElementById("submitButton").style.display = shouldHide;
}
if (showStartSponsor) {
showingStartSponsor = true;
(<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
} else {
showingStartSponsor = false;
(<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorEND"));
}
}
function toggleStartSponsorButton() {
changeStartSponsorButton(!showingStartSponsor, true);
}
function openInfoMenu() {
if (document.getElementById("sponsorBlockPopupContainer") != null) {
//it's already added
@@ -1248,7 +1242,7 @@ function openInfoMenu() {
popupInitialised = false;
//hide info button
document.getElementById("infoButton").style.display = "none";
if (playerButtons.info) playerButtons.info.button.style.display = "none";
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
@@ -1310,20 +1304,28 @@ function openInfoMenu() {
function closeInfoMenu() {
const popup = document.getElementById("sponsorBlockPopupContainer");
if (popup != null) {
popup.remove();
if (popup === null) return;
//show info button if it's not an embed
if (!document.URL.includes("/embed/")) {
document.getElementById("infoButton").style.display = "unset";
}
popup.remove();
// Show info button if it's not an embed
if (!document.URL.includes("/embed/") && playerButtons.info) {
playerButtons.info.button.style.display = "unset";
}
}
function clearSponsorTimes() {
//it can't update to this info yet
/**
* The content script currently has no way to notify the info menu of changes. As a workaround we close it, thus making it query the new information when reopened.
*
* This function and all its uses should be removed when this issue is fixed.
* */
function closeInfoMenuAnd<T>(func: () => T): T {
closeInfoMenu();
return func();
}
function clearSponsorTimes() {
const currentVideoID = sponsorVideoID;
const sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
@@ -1341,8 +1343,7 @@ function clearSponsorTimes() {
updatePreviewBar();
//set buttons to be correct
changeStartSponsorButton(true, false);
updateEditButtonsOnPlayer();
}
}
@@ -1408,18 +1409,6 @@ function dontShowNoticeAgain() {
closeAllSkipNotices();
}
function sponsorMessageStarted(callback: (response: MessageResponse) => void) {
video = document.querySelector('video');
//send back current time
callback({
time: video.currentTime
})
//update button
toggleStartSponsorButton();
}
/**
* Helper method for the submission notice to clear itself when it closes
*/
@@ -1430,9 +1419,6 @@ function resetSponsorSubmissionNotice() {
function submitSponsorTimes() {
if (submissionNotice !== null) return;
//it can't update to this info yet
closeInfoMenu();
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
}
@@ -1441,10 +1427,10 @@ function submitSponsorTimes() {
//send the message to the background js
//called after all the checks have been made that it's okay to do so
async function sendSubmitMessage(): Promise<void> {
//add loading animation
(<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
async function sendSubmitMessage() {
// Add loading animation
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
playerButtons.submit.button.style.animation = "rotate 1s 0s infinite";
//check if a sponsor exceeds the duration of the video
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
@@ -1476,13 +1462,15 @@ async function sendSubmitMessage(): Promise<void> {
});
if (response.status === 200) {
//hide loading message
const submitButton = document.getElementById("submitButton");
// Handle submission success
const submitButton = playerButtons.submit.button;
// Make the animation finite
submitButton.style.animation = "rotate 1s";
//finish this animation
//when the animation is over, hide the button
const animationEndListener = function() {
changeStartSponsorButton(true, false);
// When the animation is over, hide the button
const animationEndListener = () => {
updateEditButtonsOnPlayer();
submitButton.style.animation = "none";
@@ -1491,13 +1479,11 @@ async function sendSubmitMessage(): Promise<void> {
submitButton.addEventListener("animationend", animationEndListener);
//clear the sponsor times
// Remove segments from storage since they've already been submitted
Config.config.segmentTimes.delete(sponsorVideoID);
//add submissions to current sponsors list
if (sponsorTimes === null) sponsorTimes = [];
sponsorTimes = sponsorTimes.concat(sponsorTimesSubmitting);
// Add submissions to current sponsors list
sponsorTimes = (sponsorTimes || []).concat(sponsorTimesSubmitting);
// Increase contribution count
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
@@ -1511,9 +1497,9 @@ async function sendSubmitMessage(): Promise<void> {
updatePreviewBar();
} else {
//show that the upload failed
document.getElementById("submitButton").style.animation = "unset";
(<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker256px.png");
// Show that the upload failed
playerButtons.submit.button.style.animation = "unset";
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
alert(utils.getErrorMessage(response.status, response.responseText));
}
@@ -1570,7 +1556,7 @@ function hotkeyListener(e: KeyboardEvent): void {
}
break;
case startSponsorKey:
startSponsorClicked();
startOrEndTimingNewSegment();
break;
case submitKey:
submitSponsorTimes();
@@ -1578,14 +1564,6 @@ function hotkeyListener(e: KeyboardEvent): void {
}
}
/**
* Is this an unlisted YouTube video.
* Assumes that the the privacy info is available.
*/
function isUnlisted(): boolean {
return videoInfo?.microformat?.playerMicroformatRenderer?.isUnlisted || videoInfo?.videoDetails?.isPrivate;
}
/**
* Adds the CSS to the page if needed. Required on optional sites with Chrome.
*/