aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAjay Ramachandran <[email protected]>2021-01-17 14:25:45 -0500
committerAjay Ramachandran <[email protected]>2021-01-17 14:25:45 -0500
commite269b1aec605ae5afb55572cb9a0177529862209 (patch)
treee96855685ea6c0b433d7045e0ce2d1d49fa71f4f
parentff0dc6e5706ffae19502b192061189c6e0c2199f (diff)
downloadSponsorBlock-e269b1aec605ae5afb55572cb9a0177529862209.tar.gz
SponsorBlock-e269b1aec605ae5afb55572cb9a0177529862209.zip
Add skip keybind
https://github.com/ajayyy/SponsorBlock/issues/299
-rw-r--r--public/_locales/en/messages.json3
-rw-r--r--public/options/options.html21
-rw-r--r--src/components/NoticeComponent.tsx23
-rw-r--r--src/components/SkipNoticeComponent.tsx44
-rw-r--r--src/config.ts2
-rw-r--r--src/content.ts68
-rw-r--r--src/render/SkipNotice.tsx11
-rw-r--r--src/types.ts2
8 files changed, 123 insertions, 51 deletions
diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index 0a761cf5..d631d2cc 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -232,6 +232,9 @@
"message": "If you still don't like it, hit the never show button.",
"description": "The second line of the message displayed after the notice was upgraded."
},
+ "setSkipShortcut": {
+ "message": "Set key for skipping a segment"
+ },
"setStartSponsorShortcut": {
"message": "Set key for start segment keybind"
},
diff --git a/public/options/options.html b/public/options/options.html
index 68c28cbd..5907c07b 100644
--- a/public/options/options.html
+++ b/public/options/options.html
@@ -83,6 +83,27 @@
<br/>
<br/>
+
+ <div option-type="keybind-change" sync-option="skipKeybind">
+ <div class="option-button trigger-button">
+ __MSG_setSkipShortcut__
+ </div>
+
+ <div class="option-hidden-section hidden">
+ <br/>
+
+ <div class="medium-description keybind-status">
+ __MSG_keybindDescription__
+ </div>
+
+ <span class="medium-description bold keybind-status-key">
+
+ </span>
+ </div>
+ </div>
+
+ <br/>
+ <br/>
<div option-type="keybind-change" sync-option="startSponsorKeybind">
<div class="option-button trigger-button">
diff --git a/src/components/NoticeComponent.tsx b/src/components/NoticeComponent.tsx
index d699c204..da25aa65 100644
--- a/src/components/NoticeComponent.tsx
+++ b/src/components/NoticeComponent.tsx
@@ -177,12 +177,19 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
countdownTime
})
}
+
+ removeFadeAnimation(): void {
+ //remove the fade out class if it exists
+ const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
+ notice.classList.remove("sponsorSkipNoticeFadeOut");
+ notice.style.animation = "none";
+ }
pauseCountdown(): void {
if (!this.props.timed) return;
//remove setInterval
- clearInterval(this.countdownInterval);
+ if (this.countdownInterval) clearInterval(this.countdownInterval);
this.countdownInterval = null;
//reset countdown and inform the user
@@ -191,10 +198,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
countdownText: this.state.countdownManuallyPaused ? chrome.i18n.getMessage("manualPaused") : chrome.i18n.getMessage("paused")
});
- //remove the fade out class if it exists
- const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
- notice.classList.remove("sponsorSkipNoticeFadeOut");
- notice.style.animation = "none";
+ this.removeFadeAnimation();
}
startCountdown(): void {
@@ -208,16 +212,25 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
countdownText: null
});
+ this.setupInterval();
+ }
+
+ setupInterval(): void {
+ if (this.countdownInterval) clearInterval(this.countdownInterval);
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
}
resetCountdown(): void {
if (!this.props.timed) return;
+ this.setupInterval();
+
this.setState({
countdownTime: this.state.maxCountdownTime(),
countdownText: null
});
+
+ this.removeFadeAnimation();
}
/**
diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx
index 069472b2..f6b318e6 100644
--- a/src/components/SkipNoticeComponent.tsx
+++ b/src/components/SkipNoticeComponent.tsx
@@ -5,7 +5,7 @@ import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
-enum SkipNoticeAction {
+export enum SkipNoticeAction {
None,
Upvote,
Downvote,
@@ -24,23 +24,23 @@ export interface SkipNoticeProps {
}
export interface SkipNoticeState {
- noticeTitle: string;
+ noticeTitle?: string;
- messages: string[];
- messageOnClick: (event: React.MouseEvent) => unknown;
+ messages?: string[];
+ messageOnClick?: (event: React.MouseEvent) => unknown;
- countdownTime: number;
- maxCountdownTime: () => number;
- countdownText: string;
+ countdownTime?: number;
+ maxCountdownTime?: () => number;
+ countdownText?: string;
- unskipText: string;
- unskipCallback: (index: number) => void;
+ unskipText?: string;
+ unskipCallback?: (index: number) => void;
- downvoting: boolean;
- choosingCategory: boolean;
- thanksForVotingText: string; //null until the voting buttons should be hidden
+ downvoting?: boolean;
+ choosingCategory?: boolean;
+ thanksForVotingText?: string; //null until the voting buttons should be hidden
- actionState: SkipNoticeAction;
+ actionState?: SkipNoticeAction;
}
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
@@ -196,7 +196,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
style={{marginLeft: "4px"}}
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
- {this.state.unskipText}
+ {this.state.unskipText + " (" + Config.config.skipKeybind + ")"}
</button>
</td>
@@ -456,21 +456,23 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
reskip(index: number): void {
this.contentContainer().reskipSponsorTime(this.segments[index]);
- //reset countdown
- this.setState({
+ const newState: SkipNoticeState = {
unskipText: chrome.i18n.getMessage("unskip"),
unskipCallback: this.unskip.bind(this),
maxCountdownTime: () => 4,
countdownTime: 4
- });
+ };
// See if the title should be changed
if (!this.autoSkip) {
- this.setState({
- noticeTitle: chrome.i18n.getMessage("noticeTitle")
- });
- }
+ newState.noticeTitle = chrome.i18n.getMessage("noticeTitle");
+ }
+
+ //reset countdown
+ this.setState(newState, () => {
+ this.noticeRef.current.resetCountdown();
+ });
}
afterVote(segment: SponsorTime, type: number, category: string): void {
diff --git a/src/config.ts b/src/config.ts
index 5290fb61..5723db12 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -10,6 +10,7 @@ interface SBConfig {
defaultCategory: string,
whitelistedChannels: string[],
forceChannelCheck: boolean,
+ skipKeybind: string,
startSponsorKeybind: string,
submitKeybind: string,
minutesSaved: number,
@@ -142,6 +143,7 @@ const Config: SBObject = {
defaultCategory: "chooseACategory",
whitelistedChannels: [],
forceChannelCheck: false,
+ skipKeybind: "Enter",
startSponsorKeybind: ";",
submitKeybind: "'",
minutesSaved: 0,
diff --git a/src/content.ts b/src/content.ts
index ab3ece52..9e9332e6 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -23,6 +23,8 @@ let sponsorDataFound = false;
let sponsorTimes: SponsorTime[] = null;
//what video id are these sponsors for
let sponsorVideoID: VideoID = null;
+// List of open skip notices
+const skipNotices: SkipNotice[] = [];
// JSON video info
let videoInfo: VideoInfo = null;
@@ -35,11 +37,13 @@ let channelID: string;
let currentSkipSchedule: NodeJS.Timeout = null;
let seekListenerSetUp = false
-/** @type {Array[boolean]} Has the sponsor been skipped */
+/** Has the sponsor been skipped */
let sponsorSkipped: boolean[] = [];
//the video
let video: HTMLVideoElement;
+// List of videos that have had event listeners added to them
+const videoRootsWithEventListeners: HTMLDivElement[] = [];
let onInvidious;
let onMobileYouTube;
@@ -99,6 +103,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
unskipSponsorTime,
sponsorTimes,
sponsorTimesSubmitting,
+ skipNotices,
v: video,
sponsorVideoID,
reskipSponsorTime,
@@ -197,28 +202,6 @@ if (!Config.configListeners.includes(contentConfigUpdateListener)) {
Config.configListeners.push(contentConfigUpdateListener);
}
-//check for hotkey pressed
-document.onkeydown = function(e: KeyboardEvent){
- const key = e.key;
-
- const video = document.getElementById("movie_player");
-
- const startSponsorKey = Config.config.startSponsorKeybind;
-
- const submitKey = Config.config.submitKeybind;
-
- //is the video in focus, otherwise they could be typing a comment
- if (document.activeElement === video) {
- if(key == startSponsorKey){
- //semicolon
- startSponsorClicked();
- } else if (key == submitKey) {
- //single quote
- submitSponsorTimes();
- }
- }
-}
-
function resetValues() {
lastCheckTime = 0;
lastCheckVideoTime = -1;
@@ -512,6 +495,8 @@ async function sponsorsLookup(id: string) {
return;
}
+ addHotkeyListener();
+
if (!durationListenerSetUp) {
durationListenerSetUp = true;
@@ -996,7 +981,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
if (openNotice) {
//send out the message saying that a sponsor message was skipped
if (!Config.config.dontShowNotice || !autoSkip) {
- new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer);
+ skipNotices.push(new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer));
}
}
@@ -1542,6 +1527,41 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
return sponsorTimesMessage;
}
+function addHotkeyListener(): boolean {
+ const videoRoot = document.getElementById("movie_player") as HTMLDivElement;
+
+ if (!videoRootsWithEventListeners.includes(videoRoot)) {
+ videoRoot.addEventListener("keydown", hotkeyListener);
+ videoRootsWithEventListeners.push(videoRoot);
+ return true;
+ }
+
+ return false;
+}
+
+function hotkeyListener(e: KeyboardEvent): void {
+ const key = e.key;
+
+ const skipKey = Config.config.skipKeybind;
+ const startSponsorKey = Config.config.startSponsorKeybind;
+ const submitKey = Config.config.submitKeybind;
+
+ switch (key) {
+ case skipKey:
+ if (skipNotices.length > 0) {
+ const latestSkipNotice = skipNotices[skipNotices.length - 1];
+ latestSkipNotice.toggleSkip.call(latestSkipNotice);
+ }
+ break;
+ case startSponsorKey:
+ startSponsorClicked();
+ break;
+ case submitKey:
+ submitSponsorTimes();
+ break;
+ }
+}
+
/**
* Is this an unlisted YouTube video.
* Assumes that the the privacy info is available.
diff --git a/src/render/SkipNotice.tsx b/src/render/SkipNotice.tsx
index ac66cae4..526d2d1c 100644
--- a/src/render/SkipNotice.tsx
+++ b/src/render/SkipNotice.tsx
@@ -1,7 +1,7 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
-import SkipNoticeComponent from "../components/SkipNoticeComponent";
+import SkipNoticeComponent, { SkipNoticeAction } from "../components/SkipNoticeComponent";
import { SponsorTime, ContentContainer } from "../types";
class SkipNotice {
@@ -15,6 +15,8 @@ class SkipNotice {
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
+ this.skipNoticeRef = React.createRef();
+
this.segments = segments;
this.autoSkip = autoSkip;
this.contentContainer = contentContainer;
@@ -67,6 +69,13 @@ class SkipNotice {
ReactDOM.unmountComponentAtNode(this.noticeElement);
this.noticeElement.remove();
+
+ const skipNotices = this.contentContainer().skipNotices;
+ skipNotices.splice(skipNotices.indexOf(this), 1);
+ }
+
+ toggleSkip(): void {
+ this.skipNoticeRef.current.prepAction(SkipNoticeAction.Unskip);
}
}
diff --git a/src/types.ts b/src/types.ts
index 1d79e015..dd870f37 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,5 +1,6 @@
import SubmissionNotice from "./render/SubmissionNotice";
import SkipNoticeComponent from "./components/SkipNoticeComponent";
+import SkipNotice from "./render/SkipNotice";
export interface ContentContainer {
(): {
@@ -8,6 +9,7 @@ export interface ContentContainer {
unskipSponsorTime: (segment: SponsorTime) => void,
sponsorTimes: SponsorTime[],
sponsorTimesSubmitting: SponsorTime[],
+ skipNotices: SkipNotice[],
v: HTMLVideoElement,
sponsorVideoID,
reskipSponsorTime: (segment: SponsorTime) => void,