mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2024-11-10 01:01:55 +01:00
Clean up segment creation code
Noteworthy changes: - Adds ability to cancel creating a segment - Makes segment creation fully the responsibility of the content script, with the popup script buttons simply doing RPC - Adds types to the Utils.wait function - Fixes segment timestamps backwards if user marks segment end earlier than the start - Makes the info menu (in-page popup) workaround clearer and easier to remove in the future.
This commit is contained in:
parent
318d00a9c4
commit
3879cc6de3
10 changed files with 223 additions and 264 deletions
|
@ -79,6 +79,9 @@
|
|||
"sponsorEnd": {
|
||||
"message": "Segment Ends Now"
|
||||
},
|
||||
"sponsorCancel": {
|
||||
"message": "Cancel Creating Segment"
|
||||
},
|
||||
"noVideoID": {
|
||||
"message": "No YouTube video found.\nIf this is incorrect, refresh the tab."
|
||||
},
|
||||
|
|
|
@ -79,6 +79,9 @@
|
|||
"sponsorEnd": {
|
||||
"message": "Koniec segmentu"
|
||||
},
|
||||
"sponsorCancel": {
|
||||
"message": "Anuluj tworzenie segmentu"
|
||||
},
|
||||
"noVideoID": {
|
||||
"message": "Nie znaleziono filmu YouTube.\nJeżeli to błąd, odśwież stronę."
|
||||
},
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
</div>
|
||||
<div id="submissionSection" style="display: none">
|
||||
<b style="display: block; margin-top: 12px;">__MSG_submissionEditHint__</b>
|
||||
<div id="submitTimesContainer" style="display: none; margin-top: 12px;">
|
||||
<div id="submitTimesContainer" style="margin-top: 12px;">
|
||||
<button id="submitTimes" class="mediumButton">__MSG_submitTimesButton__</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -344,7 +344,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
//if it is not a complete sponsor time
|
||||
if (sponsorTimes[index].segment.length < 2) {
|
||||
//update video player
|
||||
this.props.contentContainer().changeStartSponsorButton(true, false);
|
||||
this.props.contentContainer().updateEditButtonsOnPlayer();
|
||||
}
|
||||
|
||||
sponsorTimes.splice(index, 1);
|
||||
|
@ -359,7 +359,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
|||
this.props.submissionNotice.cancel();
|
||||
|
||||
//update video player
|
||||
this.props.contentContainer().changeStartSponsorButton(true, false);
|
||||
this.props.contentContainer().updateEditButtonsOnPlayer();
|
||||
} else {
|
||||
//update display
|
||||
this.props.submissionNotice.forceUpdate();
|
||||
|
|
|
@ -6,6 +6,7 @@ const utils = new Utils();
|
|||
|
||||
interface SBConfig {
|
||||
userID: string,
|
||||
/** Contains unsubmitted segments that the user has created. */
|
||||
segmentTimes: SBMap<string, SponsorTime[]>,
|
||||
defaultCategory: string,
|
||||
whitelistedChannels: string[],
|
||||
|
|
331
src/content.ts
331
src/content.ts
|
@ -1,6 +1,6 @@
|
|||
import Config from "./config";
|
||||
|
||||
import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject } from "./types";
|
||||
import { SponsorTime, IncompleteSponsorTime, CategorySkipOption, VideoID, SponsorHideType, FetchResponse, VideoInfo, StorageChangesObject } from "./types";
|
||||
|
||||
import { ContentContainer } from "./types";
|
||||
import Utils from "./utils";
|
||||
|
@ -71,8 +71,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 +84,10 @@ 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;
|
||||
/** Currently timed segment, which will be added to the unsubmitted segments when ready. */
|
||||
let currentlyTimedSegment: IncompleteSponsorTime | null = null;
|
||||
|
||||
//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,7 +114,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
|
|||
onMobileYouTube,
|
||||
sponsorSubmissionNotice: submissionNotice,
|
||||
resetSponsorSubmissionNotice,
|
||||
changeStartSponsorButton,
|
||||
updateEditButtonsOnPlayer,
|
||||
previewTime,
|
||||
videoInfo,
|
||||
getRealCurrentTime: getRealCurrentTime
|
||||
|
@ -127,11 +130,11 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||
videoIDChange(getYouTubeVideoID(document.URL));
|
||||
break;
|
||||
case "sponsorStart":
|
||||
sponsorMessageStarted(sendResponse);
|
||||
closeInfoMenuAnd(() => startOrEndTimingNewSegment());
|
||||
|
||||
break;
|
||||
case "sponsorDataChanged":
|
||||
updateSponsorTimesSubmitting();
|
||||
sendResponse({
|
||||
creatingSegment: currentlyTimedSegment !== null,
|
||||
});
|
||||
|
||||
break;
|
||||
case "isInfoFound":
|
||||
|
@ -150,7 +153,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||
break;
|
||||
case "getVideoID":
|
||||
sendResponse({
|
||||
videoID: sponsorVideoID
|
||||
videoID: sponsorVideoID,
|
||||
creatingSegment: currentlyTimedSegment !== null,
|
||||
});
|
||||
|
||||
break;
|
||||
|
@ -170,13 +174,9 @@ 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();
|
||||
closeInfoMenuAnd(() => submitSponsorTimes());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -294,33 +294,21 @@ 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 = [];
|
||||
currentlyTimedSegment = null;
|
||||
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");
|
||||
|
@ -1024,8 +1012,9 @@ function reskipSponsorTime(segment: SponsorTime) {
|
|||
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");
|
||||
|
@ -1049,9 +1038,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 {
|
||||
|
@ -1061,8 +1056,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);
|
||||
|
@ -1075,53 +1070,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", startOrEndTimingNewSegment, "PlayerStartIconSponsorBlocker256px.png");
|
||||
createButton("cancelSponsor", "sponsorCancel", cancelCreatingSegment, "PlayerUploadFailedIconSponsorBlocker256px.png");
|
||||
createButton("info", "openPopup", openInfoMenu, "PlayerInfoIconSponsorBlocker256px.png");
|
||||
createButton("delete", "clearTimes", () => closeInfoMenuAnd(() => clearSponsorTimes()), "PlayerDeleteIconSponsorBlocker256px.png");
|
||||
createButton("submit", "SubmitTimes", submitSponsorTimes, "PlayerUploadIconSponsorBlocker256px.png");
|
||||
}
|
||||
|
||||
//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 || onInvidious) return;
|
||||
|
||||
const buttonsEnabled = !Config.config.hideVideoPlayerControls;
|
||||
|
||||
let creatingSegment = false;
|
||||
let submitButtonVisible = false;
|
||||
let deleteButtonVisible = false;
|
||||
|
||||
// Only check if buttons should be visible if they're enabled
|
||||
if (buttonsEnabled) {
|
||||
creatingSegment = currentlyTimedSegment !== null;
|
||||
|
||||
// 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 > 0;
|
||||
}
|
||||
|
||||
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/PlayerStopIconSponsorBlocker256px.png");
|
||||
playerButtons.startSponsor.button.setAttribute("title", chrome.i18n.getMessage("sponsorEnd"));
|
||||
} else {
|
||||
playerButtons.startSponsor.image.src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
|
||||
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";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1142,30 +1159,40 @@ 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
|
||||
sponsorTimesSubmitting.push({
|
||||
function startOrEndTimingNewSegment() {
|
||||
if (!currentlyTimedSegment) {
|
||||
// Start a new segment
|
||||
currentlyTimedSegment = {
|
||||
segment: [getRealCurrentTime()],
|
||||
UUID: null,
|
||||
category: Config.config.defaultCategory
|
||||
category: Config.config.defaultCategory,
|
||||
};
|
||||
} else {
|
||||
// Finish creating the new segment
|
||||
const existingTime = currentlyTimedSegment.segment[0];
|
||||
const currentTime = getRealCurrentTime();
|
||||
|
||||
sponsorTimesSubmitting.push({
|
||||
...currentlyTimedSegment,
|
||||
// Swap timestamps if the user put the segment end before the start
|
||||
segment: [Math.min(existingTime, currentTime), Math.max(existingTime, currentTime)],
|
||||
});
|
||||
|
||||
currentlyTimedSegment = null;
|
||||
|
||||
// Save the newly created segment
|
||||
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
|
||||
}
|
||||
|
||||
//save this info
|
||||
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
|
||||
updateEditButtonsOnPlayer();
|
||||
|
||||
updateSponsorTimesSubmitting(false)
|
||||
updateSponsorTimesSubmitting(false);
|
||||
}
|
||||
|
||||
function cancelCreatingSegment() {
|
||||
currentlyTimedSegment = null;
|
||||
|
||||
updateEditButtonsOnPlayer();
|
||||
}
|
||||
|
||||
function updateSponsorTimesSubmitting(getFromConfig = true) {
|
||||
|
@ -1194,38 +1221,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 (showStartSponsor) {
|
||||
showingStartSponsor = true;
|
||||
(<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker256px.png");
|
||||
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
|
||||
|
||||
if (document.getElementById("startSponsorImage").style.display != "none" && uploadButtonVisible && !Config.config.hideUploadButtonPlayerControls && !onInvidious) {
|
||||
document.getElementById("submitButton").style.display = "unset";
|
||||
} else if (!uploadButtonVisible || onInvidious) {
|
||||
//disable submit button
|
||||
document.getElementById("submitButton").style.display = "none";
|
||||
}
|
||||
} else {
|
||||
showingStartSponsor = false;
|
||||
(<HTMLImageElement> document.getElementById("startSponsorImage")).src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker256px.png");
|
||||
document.getElementById("startSponsorButton").setAttribute("title", chrome.i18n.getMessage("sponsorEND"));
|
||||
|
||||
//disable submit button
|
||||
document.getElementById("submitButton").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function toggleStartSponsorButton() {
|
||||
changeStartSponsorButton(!showingStartSponsor, true);
|
||||
}
|
||||
|
||||
function openInfoMenu() {
|
||||
if (document.getElementById("sponsorBlockPopupContainer") != null) {
|
||||
//it's already added
|
||||
|
@ -1235,7 +1230,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) {
|
||||
|
@ -1297,20 +1292,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);
|
||||
|
@ -1328,8 +1331,7 @@ function clearSponsorTimes() {
|
|||
|
||||
updatePreviewBar();
|
||||
|
||||
//set buttons to be correct
|
||||
changeStartSponsorButton(true, false);
|
||||
updateEditButtonsOnPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1395,18 +1397,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
|
||||
*/
|
||||
|
@ -1417,9 +1407,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);
|
||||
}
|
||||
|
@ -1428,10 +1415,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/PlayerUploadIconSponsorBlocker256px.png");
|
||||
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++) {
|
||||
|
@ -1458,17 +1445,19 @@ async function sendSubmitMessage(): Promise<void> {
|
|||
const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
|
||||
videoID: sponsorVideoID,
|
||||
userID: Config.config.userID,
|
||||
segments: sponsorTimesSubmitting
|
||||
segments: sponsorTimesSubmitting,
|
||||
});
|
||||
|
||||
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";
|
||||
|
||||
|
@ -1477,13 +1466,12 @@ 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
|
||||
// FIXME: segments from sponsorTimesSubmitting do not contain UUIDs .-.
|
||||
sponsorTimes = (sponsorTimes || []).concat(sponsorTimesSubmitting);
|
||||
|
||||
// Increase contribution count
|
||||
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
|
||||
|
@ -1493,13 +1481,14 @@ async function sendSubmitMessage(): Promise<void> {
|
|||
Config.config.submissionCountSinceCategories = Config.config.submissionCountSinceCategories + 1;
|
||||
|
||||
// Empty the submitting times
|
||||
currentlyTimedSegment = null;
|
||||
sponsorTimesSubmitting = [];
|
||||
|
||||
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/PlayerUploadFailedIconSponsorBlocker256px.png");
|
||||
|
||||
alert(utils.getErrorMessage(response.status, response.responseText));
|
||||
}
|
||||
|
@ -1554,7 +1543,7 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||
}
|
||||
break;
|
||||
case startSponsorKey:
|
||||
startSponsorClicked();
|
||||
startOrEndTimingNewSegment();
|
||||
break;
|
||||
case submitKey:
|
||||
submitSponsorTimes();
|
||||
|
|
|
@ -12,7 +12,6 @@ interface DefaultMessage {
|
|||
message:
|
||||
"update"
|
||||
| "sponsorStart"
|
||||
| "sponsorDataChanged"
|
||||
| "isInfoFound"
|
||||
| "getVideoID"
|
||||
| "getChannelID"
|
||||
|
@ -25,13 +24,7 @@ interface BoolValueMessage {
|
|||
value: boolean;
|
||||
}
|
||||
|
||||
interface ChangeStartSponsorButtonMessage {
|
||||
message: "changeStartSponsorButton";
|
||||
showStartSponsor: boolean;
|
||||
uploadButtonVisible: boolean;
|
||||
}
|
||||
|
||||
export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | ChangeStartSponsorButtonMessage);
|
||||
export type Message = BaseMessage & (DefaultMessage | BoolValueMessage);
|
||||
|
||||
interface IsInfoFoundMessageResponse {
|
||||
found: boolean;
|
||||
|
@ -47,7 +40,7 @@ interface GetChannelIDResponse {
|
|||
}
|
||||
|
||||
interface SponsorStartResponse {
|
||||
time: number;
|
||||
creatingSegment: boolean;
|
||||
}
|
||||
|
||||
interface IsChannelWhitelistedResponse {
|
||||
|
|
120
src/popup.ts
120
src/popup.ts
|
@ -126,8 +126,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||
PageElements.optionsButton.addEventListener("click", openOptions);
|
||||
PageElements.helpButton.addEventListener("click", openHelp);
|
||||
|
||||
//if true, the button now selects the end time
|
||||
let startTimeChosen = false;
|
||||
/** If true, the content script is in the process of creating a new segment. */
|
||||
let creatingSegment = false;
|
||||
|
||||
//the start and end time pairs (2d)
|
||||
let sponsorTimes: SponsorTime[] = [];
|
||||
|
@ -233,11 +233,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||
|
||||
function onTabs(tabs) {
|
||||
messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
|
||||
if (result != undefined && result.videoID) {
|
||||
if (result !== undefined && result.videoID) {
|
||||
currentVideoID = result.videoID;
|
||||
creatingSegment = result.creatingSegment;
|
||||
|
||||
loadTabData(tabs);
|
||||
} else if (result == undefined && chrome.runtime.lastError) {
|
||||
// this isn't a YouTube video then, or at least the content script is not loaded
|
||||
} else if (result === undefined && chrome.runtime.lastError) {
|
||||
//this isn't a YouTube video then, or at least the content script is not loaded
|
||||
displayNoVideo();
|
||||
}
|
||||
});
|
||||
|
@ -253,19 +255,11 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||
//load video times for this video
|
||||
const sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
|
||||
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
|
||||
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
|
||||
startTimeChosen = true;
|
||||
PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
|
||||
}
|
||||
|
||||
sponsorTimes = sponsorTimesStorage;
|
||||
|
||||
//show submission section
|
||||
PageElements.submissionSection.style.display = "unset";
|
||||
|
||||
showSubmitTimesIfNecessary();
|
||||
}
|
||||
|
||||
updateSegmentEditingUI();
|
||||
|
||||
//check if this video's sponsors are known
|
||||
messageHandler.sendMessage(
|
||||
tabs[0].id,
|
||||
|
@ -321,51 +315,44 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||
//the content script will get the message if a YouTube page is open
|
||||
messageHandler.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, tabs => {
|
||||
currentWindow: true,
|
||||
}, (tabs) => {
|
||||
messageHandler.sendMessage(
|
||||
tabs[0].id,
|
||||
{from: 'popup', message: 'sponsorStart'},
|
||||
startSponsorCallback
|
||||
async (response) => {
|
||||
startSponsorCallback(response);
|
||||
|
||||
// Perform a second update after the config changes take effect as a workaround for a race condition
|
||||
const removeListener = (listener: typeof lateUpdate) => {
|
||||
const index = Config.configListeners.indexOf(listener);
|
||||
if (index !== -1) Config.configListeners.splice(index, 1);
|
||||
};
|
||||
|
||||
const lateUpdate = () => {
|
||||
startSponsorCallback(response);
|
||||
removeListener(lateUpdate);
|
||||
};
|
||||
|
||||
Config.configListeners.push(lateUpdate);
|
||||
|
||||
// Remove the listener after 200ms in case the changes were propagated by the time we got the response
|
||||
setTimeout(() => removeListener(lateUpdate), 200);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function startSponsorCallback(response) {
|
||||
const sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
|
||||
function startSponsorCallback(response: {creatingSegment: boolean}) {
|
||||
creatingSegment = response.creatingSegment;
|
||||
|
||||
if (sponsorTimes[sponsorTimesIndex] == undefined) {
|
||||
sponsorTimes[sponsorTimesIndex] = {
|
||||
segment: [],
|
||||
category: Config.config.defaultCategory,
|
||||
UUID: null
|
||||
};
|
||||
// Only update the segments after a segment was created
|
||||
if (!creatingSegment) {
|
||||
sponsorTimes = Config.config.segmentTimes.get(currentVideoID) || [];
|
||||
}
|
||||
|
||||
sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
|
||||
|
||||
const localStartTimeChosen = startTimeChosen;
|
||||
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
|
||||
|
||||
//send a message to the client script
|
||||
if (localStartTimeChosen) {
|
||||
messageHandler.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, tabs => {
|
||||
messageHandler.sendMessage(
|
||||
tabs[0].id,
|
||||
{message: "sponsorDataChanged"}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
updateStartTimeChosen();
|
||||
|
||||
//show submission section
|
||||
PageElements.submissionSection.style.display = "unset";
|
||||
|
||||
showSubmitTimesIfNecessary();
|
||||
// Update the UI
|
||||
updateSegmentEditingUI();
|
||||
}
|
||||
|
||||
//display the video times from the array at the top, in a different section
|
||||
|
@ -475,34 +462,13 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||
PageElements.showNoticeAgain.style.display = "none";
|
||||
}
|
||||
|
||||
function updateStartTimeChosen() {
|
||||
//update startTimeChosen letiable
|
||||
if (!startTimeChosen) {
|
||||
startTimeChosen = true;
|
||||
PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd");
|
||||
} else {
|
||||
resetStartTimeChosen();
|
||||
}
|
||||
/** Updates any UI related to segment editing and submission according to the current state. */
|
||||
function updateSegmentEditingUI() {
|
||||
PageElements.sponsorStart.innerText = chrome.i18n.getMessage(creatingSegment ? "sponsorEnd" : "sponsorStart");
|
||||
|
||||
PageElements.submissionSection.style.display = sponsorTimes && sponsorTimes.length > 0 ? "unset" : "none";
|
||||
}
|
||||
|
||||
//set it to false
|
||||
function resetStartTimeChosen() {
|
||||
startTimeChosen = false;
|
||||
PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorStart");
|
||||
}
|
||||
|
||||
//hides and shows the submit times button when needed
|
||||
function showSubmitTimesIfNecessary() {
|
||||
//check if an end time has been specified for the latest sponsor time
|
||||
if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].segment.length > 1) {
|
||||
//show submit times button
|
||||
document.getElementById("submitTimesContainer").style.display = "flex";
|
||||
} else {
|
||||
//hide submit times button
|
||||
document.getElementById("submitTimesContainer").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//make the options div visible
|
||||
function openOptions() {
|
||||
chrome.runtime.sendMessage({"message": "openConfig"});
|
||||
|
|
|
@ -17,7 +17,7 @@ export interface ContentContainer {
|
|||
onMobileYouTube: boolean,
|
||||
sponsorSubmissionNotice: SubmissionNotice,
|
||||
resetSponsorSubmissionNotice: () => void,
|
||||
changeStartSponsorButton: (showStartSponsor: boolean, uploadButtonVisible: boolean) => Promise<boolean>,
|
||||
updateEditButtonsOnPlayer: () => void,
|
||||
previewTime: (time: number, unpause?: boolean) => void,
|
||||
videoInfo: VideoInfo,
|
||||
getRealCurrentTime: () => number
|
||||
|
@ -60,6 +60,10 @@ export interface SponsorTime {
|
|||
hidden?: SponsorHideType;
|
||||
}
|
||||
|
||||
export type IncompleteSponsorTime = Omit<SponsorTime, 'segment'> & {
|
||||
segment: [number];
|
||||
};
|
||||
|
||||
export interface PreviewBarOption {
|
||||
color: string,
|
||||
opacity: string
|
||||
|
@ -160,4 +164,4 @@ export type VideoID = string;
|
|||
|
||||
export type StorageChangesObject = { [key: string]: chrome.storage.StorageChange };
|
||||
|
||||
export type UnEncodedSegmentTimes = [string, SponsorTime[]][];
|
||||
export type UnEncodedSegmentTimes = [string, SponsorTime[]][];
|
||||
|
|
|
@ -23,8 +23,8 @@ class Utils {
|
|||
this.backgroundScriptContainer = backgroundScriptContainer;
|
||||
}
|
||||
|
||||
// Function that can be used to wait for a condition before returning
|
||||
async wait(condition: () => HTMLElement | boolean, timeout = 5000, check = 100): Promise<HTMLElement | boolean> {
|
||||
/** Function that can be used to wait for a condition before returning. */
|
||||
async wait<T>(condition: () => T | false, timeout = 5000, check = 100): Promise<T> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject("TIMEOUT"), timeout);
|
||||
|
||||
|
|
Loading…
Reference in a new issue