mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-10 21:47:06 +03:00
@@ -232,6 +232,9 @@
|
|||||||
"message": "If you still don't like it, hit the never show button.",
|
"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."
|
"description": "The second line of the message displayed after the notice was upgraded."
|
||||||
},
|
},
|
||||||
|
"setSkipShortcut": {
|
||||||
|
"message": "Set key for skipping a segment"
|
||||||
|
},
|
||||||
"setStartSponsorShortcut": {
|
"setStartSponsorShortcut": {
|
||||||
"message": "Set key for start segment keybind"
|
"message": "Set key for start segment keybind"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -84,6 +84,27 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<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 option-type="keybind-change" sync-option="startSponsorKeybind">
|
||||||
<div class="option-button trigger-button">
|
<div class="option-button trigger-button">
|
||||||
__MSG_setStartSponsorShortcut__
|
__MSG_setStartSponsorShortcut__
|
||||||
|
|||||||
@@ -178,11 +178,18 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pauseCountdown(): void {
|
||||||
if (!this.props.timed) return;
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
//remove setInterval
|
//remove setInterval
|
||||||
clearInterval(this.countdownInterval);
|
if (this.countdownInterval) clearInterval(this.countdownInterval);
|
||||||
this.countdownInterval = null;
|
this.countdownInterval = null;
|
||||||
|
|
||||||
//reset countdown and inform the user
|
//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")
|
countdownText: this.state.countdownManuallyPaused ? chrome.i18n.getMessage("manualPaused") : chrome.i18n.getMessage("paused")
|
||||||
});
|
});
|
||||||
|
|
||||||
//remove the fade out class if it exists
|
this.removeFadeAnimation();
|
||||||
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
|
|
||||||
notice.classList.remove("sponsorSkipNoticeFadeOut");
|
|
||||||
notice.style.animation = "none";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startCountdown(): void {
|
startCountdown(): void {
|
||||||
@@ -208,16 +212,25 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
|||||||
countdownText: null
|
countdownText: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setupInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInterval(): void {
|
||||||
|
if (this.countdownInterval) clearInterval(this.countdownInterval);
|
||||||
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
|
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetCountdown(): void {
|
resetCountdown(): void {
|
||||||
if (!this.props.timed) return;
|
if (!this.props.timed) return;
|
||||||
|
|
||||||
|
this.setupInterval();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
countdownTime: this.state.maxCountdownTime(),
|
countdownTime: this.state.maxCountdownTime(),
|
||||||
countdownText: null
|
countdownText: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.removeFadeAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
|
|||||||
import NoticeComponent from "./NoticeComponent";
|
import NoticeComponent from "./NoticeComponent";
|
||||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
|
|
||||||
enum SkipNoticeAction {
|
export enum SkipNoticeAction {
|
||||||
None,
|
None,
|
||||||
Upvote,
|
Upvote,
|
||||||
Downvote,
|
Downvote,
|
||||||
@@ -24,23 +24,23 @@ export interface SkipNoticeProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SkipNoticeState {
|
export interface SkipNoticeState {
|
||||||
noticeTitle: string;
|
noticeTitle?: string;
|
||||||
|
|
||||||
messages: string[];
|
messages?: string[];
|
||||||
messageOnClick: (event: React.MouseEvent) => unknown;
|
messageOnClick?: (event: React.MouseEvent) => unknown;
|
||||||
|
|
||||||
countdownTime: number;
|
countdownTime?: number;
|
||||||
maxCountdownTime: () => number;
|
maxCountdownTime?: () => number;
|
||||||
countdownText: string;
|
countdownText?: string;
|
||||||
|
|
||||||
unskipText: string;
|
unskipText?: string;
|
||||||
unskipCallback: (index: number) => void;
|
unskipCallback?: (index: number) => void;
|
||||||
|
|
||||||
downvoting: boolean;
|
downvoting?: boolean;
|
||||||
choosingCategory: boolean;
|
choosingCategory?: boolean;
|
||||||
thanksForVotingText: string; //null until the voting buttons should be hidden
|
thanksForVotingText?: string; //null until the voting buttons should be hidden
|
||||||
|
|
||||||
actionState: SkipNoticeAction;
|
actionState?: SkipNoticeAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeState> {
|
||||||
@@ -196,7 +196,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
style={{marginLeft: "4px"}}
|
style={{marginLeft: "4px"}}
|
||||||
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
|
||||||
|
|
||||||
{this.state.unskipText}
|
{this.state.unskipText + " (" + Config.config.skipKeybind + ")"}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@@ -456,21 +456,23 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
reskip(index: number): void {
|
reskip(index: number): void {
|
||||||
this.contentContainer().reskipSponsorTime(this.segments[index]);
|
this.contentContainer().reskipSponsorTime(this.segments[index]);
|
||||||
|
|
||||||
//reset countdown
|
const newState: SkipNoticeState = {
|
||||||
this.setState({
|
|
||||||
unskipText: chrome.i18n.getMessage("unskip"),
|
unskipText: chrome.i18n.getMessage("unskip"),
|
||||||
unskipCallback: this.unskip.bind(this),
|
unskipCallback: this.unskip.bind(this),
|
||||||
|
|
||||||
maxCountdownTime: () => 4,
|
maxCountdownTime: () => 4,
|
||||||
countdownTime: 4
|
countdownTime: 4
|
||||||
});
|
};
|
||||||
|
|
||||||
// See if the title should be changed
|
// See if the title should be changed
|
||||||
if (!this.autoSkip) {
|
if (!this.autoSkip) {
|
||||||
this.setState({
|
newState.noticeTitle = chrome.i18n.getMessage("noticeTitle");
|
||||||
noticeTitle: chrome.i18n.getMessage("noticeTitle")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reset countdown
|
||||||
|
this.setState(newState, () => {
|
||||||
|
this.noticeRef.current.resetCountdown();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
afterVote(segment: SponsorTime, type: number, category: string): void {
|
afterVote(segment: SponsorTime, type: number, category: string): void {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ interface SBConfig {
|
|||||||
defaultCategory: string,
|
defaultCategory: string,
|
||||||
whitelistedChannels: string[],
|
whitelistedChannels: string[],
|
||||||
forceChannelCheck: boolean,
|
forceChannelCheck: boolean,
|
||||||
|
skipKeybind: string,
|
||||||
startSponsorKeybind: string,
|
startSponsorKeybind: string,
|
||||||
submitKeybind: string,
|
submitKeybind: string,
|
||||||
minutesSaved: number,
|
minutesSaved: number,
|
||||||
@@ -142,6 +143,7 @@ const Config: SBObject = {
|
|||||||
defaultCategory: "chooseACategory",
|
defaultCategory: "chooseACategory",
|
||||||
whitelistedChannels: [],
|
whitelistedChannels: [],
|
||||||
forceChannelCheck: false,
|
forceChannelCheck: false,
|
||||||
|
skipKeybind: "Enter",
|
||||||
startSponsorKeybind: ";",
|
startSponsorKeybind: ";",
|
||||||
submitKeybind: "'",
|
submitKeybind: "'",
|
||||||
minutesSaved: 0,
|
minutesSaved: 0,
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ let sponsorDataFound = false;
|
|||||||
let sponsorTimes: SponsorTime[] = null;
|
let sponsorTimes: SponsorTime[] = null;
|
||||||
//what video id are these sponsors for
|
//what video id are these sponsors for
|
||||||
let sponsorVideoID: VideoID = null;
|
let sponsorVideoID: VideoID = null;
|
||||||
|
// List of open skip notices
|
||||||
|
const skipNotices: SkipNotice[] = [];
|
||||||
|
|
||||||
// JSON video info
|
// JSON video info
|
||||||
let videoInfo: VideoInfo = null;
|
let videoInfo: VideoInfo = null;
|
||||||
@@ -35,11 +37,13 @@ let channelID: string;
|
|||||||
let currentSkipSchedule: NodeJS.Timeout = null;
|
let currentSkipSchedule: NodeJS.Timeout = null;
|
||||||
let seekListenerSetUp = false
|
let seekListenerSetUp = false
|
||||||
|
|
||||||
/** @type {Array[boolean]} Has the sponsor been skipped */
|
/** Has the sponsor been skipped */
|
||||||
let sponsorSkipped: boolean[] = [];
|
let sponsorSkipped: boolean[] = [];
|
||||||
|
|
||||||
//the video
|
//the video
|
||||||
let video: HTMLVideoElement;
|
let video: HTMLVideoElement;
|
||||||
|
// List of videos that have had event listeners added to them
|
||||||
|
const videoRootsWithEventListeners: HTMLDivElement[] = [];
|
||||||
|
|
||||||
let onInvidious;
|
let onInvidious;
|
||||||
let onMobileYouTube;
|
let onMobileYouTube;
|
||||||
@@ -99,6 +103,7 @@ const skipNoticeContentContainer: ContentContainer = () => ({
|
|||||||
unskipSponsorTime,
|
unskipSponsorTime,
|
||||||
sponsorTimes,
|
sponsorTimes,
|
||||||
sponsorTimesSubmitting,
|
sponsorTimesSubmitting,
|
||||||
|
skipNotices,
|
||||||
v: video,
|
v: video,
|
||||||
sponsorVideoID,
|
sponsorVideoID,
|
||||||
reskipSponsorTime,
|
reskipSponsorTime,
|
||||||
@@ -197,28 +202,6 @@ if (!Config.configListeners.includes(contentConfigUpdateListener)) {
|
|||||||
Config.configListeners.push(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() {
|
function resetValues() {
|
||||||
lastCheckTime = 0;
|
lastCheckTime = 0;
|
||||||
lastCheckVideoTime = -1;
|
lastCheckVideoTime = -1;
|
||||||
@@ -512,6 +495,8 @@ async function sponsorsLookup(id: string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addHotkeyListener();
|
||||||
|
|
||||||
if (!durationListenerSetUp) {
|
if (!durationListenerSetUp) {
|
||||||
durationListenerSetUp = true;
|
durationListenerSetUp = true;
|
||||||
|
|
||||||
@@ -996,7 +981,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
|
|||||||
if (openNotice) {
|
if (openNotice) {
|
||||||
//send out the message saying that a sponsor message was skipped
|
//send out the message saying that a sponsor message was skipped
|
||||||
if (!Config.config.dontShowNotice || !autoSkip) {
|
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;
|
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.
|
* Is this an unlisted YouTube video.
|
||||||
* Assumes that the the privacy info is available.
|
* Assumes that the the privacy info is available.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
|
|
||||||
import SkipNoticeComponent from "../components/SkipNoticeComponent";
|
import SkipNoticeComponent, { SkipNoticeAction } from "../components/SkipNoticeComponent";
|
||||||
import { SponsorTime, ContentContainer } from "../types";
|
import { SponsorTime, ContentContainer } from "../types";
|
||||||
|
|
||||||
class SkipNotice {
|
class SkipNotice {
|
||||||
@@ -15,6 +15,8 @@ class SkipNotice {
|
|||||||
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
|
||||||
|
|
||||||
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
|
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
|
||||||
|
this.skipNoticeRef = React.createRef();
|
||||||
|
|
||||||
this.segments = segments;
|
this.segments = segments;
|
||||||
this.autoSkip = autoSkip;
|
this.autoSkip = autoSkip;
|
||||||
this.contentContainer = contentContainer;
|
this.contentContainer = contentContainer;
|
||||||
@@ -67,6 +69,13 @@ class SkipNotice {
|
|||||||
ReactDOM.unmountComponentAtNode(this.noticeElement);
|
ReactDOM.unmountComponentAtNode(this.noticeElement);
|
||||||
|
|
||||||
this.noticeElement.remove();
|
this.noticeElement.remove();
|
||||||
|
|
||||||
|
const skipNotices = this.contentContainer().skipNotices;
|
||||||
|
skipNotices.splice(skipNotices.indexOf(this), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSkip(): void {
|
||||||
|
this.skipNoticeRef.current.prepAction(SkipNoticeAction.Unskip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SubmissionNotice from "./render/SubmissionNotice";
|
import SubmissionNotice from "./render/SubmissionNotice";
|
||||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||||
|
import SkipNotice from "./render/SkipNotice";
|
||||||
|
|
||||||
export interface ContentContainer {
|
export interface ContentContainer {
|
||||||
(): {
|
(): {
|
||||||
@@ -8,6 +9,7 @@ export interface ContentContainer {
|
|||||||
unskipSponsorTime: (segment: SponsorTime) => void,
|
unskipSponsorTime: (segment: SponsorTime) => void,
|
||||||
sponsorTimes: SponsorTime[],
|
sponsorTimes: SponsorTime[],
|
||||||
sponsorTimesSubmitting: SponsorTime[],
|
sponsorTimesSubmitting: SponsorTime[],
|
||||||
|
skipNotices: SkipNotice[],
|
||||||
v: HTMLVideoElement,
|
v: HTMLVideoElement,
|
||||||
sponsorVideoID,
|
sponsorVideoID,
|
||||||
reskipSponsorTime: (segment: SponsorTime) => void,
|
reskipSponsorTime: (segment: SponsorTime) => void,
|
||||||
|
|||||||
Reference in New Issue
Block a user