mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-14 07:27:05 +03:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4738c1897f | ||
|
|
cfbb194a61 | ||
|
|
9ad21e8ea1 | ||
|
|
941fc2985d | ||
|
|
e2da071761 | ||
|
|
c20c9ac64a | ||
|
|
2d59f3825c | ||
|
|
fb8b7a7d19 | ||
|
|
d992cc7a3c | ||
|
|
f8fecf5174 | ||
|
|
3007cddce9 | ||
|
|
2b3c812f8a | ||
|
|
61535fac95 | ||
|
|
4182595436 | ||
|
|
97e30e4001 | ||
|
|
6763fd3b4b | ||
|
|
a39ec76340 | ||
|
|
f68282decc | ||
|
|
160de56a71 | ||
|
|
463ce258bf | ||
|
|
a64deb2e18 |
@@ -21,6 +21,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: ["react", "@typescript-eslint"],
|
plugins: ["react", "@typescript-eslint"],
|
||||||
rules: {
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
// TODO: Remove warn rules when not needed anymore
|
// TODO: Remove warn rules when not needed anymore
|
||||||
"no-self-assign": "off",
|
"no-self-assign": "off",
|
||||||
"@typescript-eslint/no-empty-interface": "off",
|
"@typescript-eslint/no-empty-interface": "off",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "4.4.1",
|
"version": "4.4.4",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
@@ -64,7 +64,12 @@
|
|||||||
],
|
],
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"default_title": "SponsorBlock",
|
"default_title": "SponsorBlock",
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html",
|
||||||
|
"default_icon": {
|
||||||
|
"16": "icons/IconSponsorBlocker16px.png",
|
||||||
|
"32": "icons/IconSponsorBlocker32px.png",
|
||||||
|
"64": "icons/LogoSponsorBlocker64px.png"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"scripts":[
|
"scripts":[
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
"message": "It seems the server is down. Contact the dev immediately."
|
"message": "It seems the server is down. Contact the dev immediately."
|
||||||
},
|
},
|
||||||
"connectionError": {
|
"connectionError": {
|
||||||
"message": "A connection error has occured. Error code: "
|
"message": "A connection error has occurred. Error code: "
|
||||||
},
|
},
|
||||||
"clearTimes": {
|
"clearTimes": {
|
||||||
"message": "Clear Segments"
|
"message": "Clear Segments"
|
||||||
@@ -356,7 +356,7 @@
|
|||||||
"message": "Show Time With Skips Removed"
|
"message": "Show Time With Skips Removed"
|
||||||
},
|
},
|
||||||
"showTimeWithSkipsDescription": {
|
"showTimeWithSkipsDescription": {
|
||||||
"message": "This time appears in brackets next to the current time on below the seekbar. This shows the total video duration minus any segments. This includes segments marked as only \"Show In Seekbar\"."
|
"message": "This time appears in brackets next to the current time on below the Seek Bar. This shows the total video duration minus any segments. This includes segments marked as only \"Show In Seek Bar\"."
|
||||||
},
|
},
|
||||||
"youHaveSkipped": {
|
"youHaveSkipped": {
|
||||||
"message": "You've skipped "
|
"message": "You've skipped "
|
||||||
@@ -410,7 +410,7 @@
|
|||||||
"message": "Supported Sites: "
|
"message": "Supported Sites: "
|
||||||
},
|
},
|
||||||
"optionsInfo": {
|
"optionsInfo": {
|
||||||
"message": "Enable Invidious support, disable autoskip, hide buttons and more."
|
"message": "Enable Invidious support, disable auto skip, hide buttons and more."
|
||||||
},
|
},
|
||||||
"addInvidiousInstance": {
|
"addInvidiousInstance": {
|
||||||
"message": "Add 3rd-Party Client Instance"
|
"message": "Add 3rd-Party Client Instance"
|
||||||
@@ -499,7 +499,7 @@
|
|||||||
"incorrectlyFormattedOptions": {
|
"incorrectlyFormattedOptions": {
|
||||||
"message": "This JSON is not formatted correctly. Your options have not been changed."
|
"message": "This JSON is not formatted correctly. Your options have not been changed."
|
||||||
},
|
},
|
||||||
"confirmNoticeTitle" : {
|
"confirmNoticeTitle": {
|
||||||
"message": "Submit Segment"
|
"message": "Submit Segment"
|
||||||
},
|
},
|
||||||
"submit": {
|
"submit": {
|
||||||
@@ -947,5 +947,11 @@
|
|||||||
},
|
},
|
||||||
"openOptionsPage": {
|
"openOptionsPage": {
|
||||||
"message": "Open options page"
|
"message": "Open options page"
|
||||||
|
},
|
||||||
|
"resetToDefault": {
|
||||||
|
"message": "Reset settings to default"
|
||||||
|
},
|
||||||
|
"confirmResetToDefault": {
|
||||||
|
"message": "Are you sure you want to reset all settings to their default values? This cannot be undone."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@
|
|||||||
"message": "Detta döljer knapparna på YouTube-spelaren som du kan skicka in segment med som ska hoppas över."
|
"message": "Detta döljer knapparna på YouTube-spelaren som du kan skicka in segment med som ska hoppas över."
|
||||||
},
|
},
|
||||||
"showSkipButton": {
|
"showSkipButton": {
|
||||||
"message": "Behåll knappen hoppa till markerat på spelaren"
|
"message": "Behåll knappen hoppa till höjdpunkt på spelaren"
|
||||||
},
|
},
|
||||||
"showInfoButton": {
|
"showInfoButton": {
|
||||||
"message": "Visa Infoknapp På YouTube-spelaren"
|
"message": "Visa Infoknapp På YouTube-spelaren"
|
||||||
@@ -613,7 +613,7 @@
|
|||||||
"message": "Icke-musik"
|
"message": "Icke-musik"
|
||||||
},
|
},
|
||||||
"category_poi_highlight": {
|
"category_poi_highlight": {
|
||||||
"message": "Markera"
|
"message": "Höjdpunkt"
|
||||||
},
|
},
|
||||||
"category_poi_highlight_description": {
|
"category_poi_highlight_description": {
|
||||||
"message": "Den del av videon som de flesta letar efter. Liknande kommentarer \"Video börjar på x\"."
|
"message": "Den del av videon som de flesta letar efter. Liknande kommentarer \"Video börjar på x\"."
|
||||||
|
|||||||
@@ -368,6 +368,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-type="button-press" data-sync="resetToDefault" data-confirm-message="confirmResetToDefault">
|
||||||
|
<div class="option-button trigger-button">
|
||||||
|
__MSG_resetToDefault__
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="advanced" class="option-group hidden">
|
<div id="advanced" class="option-group hidden">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as CompileConfig from "../../config.json";
|
import * as CompileConfig from "../../config.json";
|
||||||
import Config from "../config"
|
import Config from "../config"
|
||||||
import { Category, ContentContainer, SponsorHideType, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
|
import { Category, ContentContainer, SponsorTime, NoticeVisbilityMode, ActionType, SponsorSourceType, SegmentUUID } from "../types";
|
||||||
import NoticeComponent from "./NoticeComponent";
|
import NoticeComponent from "./NoticeComponent";
|
||||||
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
|
||||||
import Utils from "../utils";
|
import Utils from "../utils";
|
||||||
@@ -72,7 +72,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|
|
||||||
amountOfPreviousNotices: number;
|
amountOfPreviousNotices: number;
|
||||||
showInSecondSlot: boolean;
|
showInSecondSlot: boolean;
|
||||||
|
|
||||||
idSuffix: string;
|
idSuffix: string;
|
||||||
|
|
||||||
noticeRef: React.MutableRefObject<NoticeComponent>;
|
noticeRef: React.MutableRefObject<NoticeComponent>;
|
||||||
@@ -105,7 +105,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
if (this.segments.length > 1) {
|
if (this.segments.length > 1) {
|
||||||
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
|
this.segments.sort((a, b) => a.segment[0] - b.segment[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the suffix added at the end of every id
|
// This is the suffix added at the end of every id
|
||||||
for (const segment of this.segments) {
|
for (const segment of this.segments) {
|
||||||
this.idSuffix += segment.UUID;
|
this.idSuffix += segment.UUID;
|
||||||
@@ -168,7 +168,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
|
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it started out as smaller, always keep the
|
// If it started out as smaller, always keep the
|
||||||
// skip button there
|
// skip button there
|
||||||
const showFirstSkipButton = this.props.smaller || this.segments[0].actionType === ActionType.Mute;
|
const showFirstSkipButton = this.props.smaller || this.segments[0].actionType === ActionType.Mute;
|
||||||
const firstColumn = showFirstSkipButton ? (
|
const firstColumn = showFirstSkipButton ? (
|
||||||
@@ -181,7 +181,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
showInSecondSlot={this.showInSecondSlot}
|
showInSecondSlot={this.showInSecondSlot}
|
||||||
idSuffix={this.idSuffix}
|
idSuffix={this.idSuffix}
|
||||||
fadeIn={true}
|
fadeIn={true}
|
||||||
startFaded={Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAll
|
startFaded={Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAll
|
||||||
|| (Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAutoSkip && this.autoSkip)}
|
|| (Config.config.noticeVisibilityMode >= NoticeVisbilityMode.FadedForAutoSkip && this.autoSkip)}
|
||||||
timed={true}
|
timed={true}
|
||||||
maxCountdownTime={this.state.maxCountdownTime}
|
maxCountdownTime={this.state.maxCountdownTime}
|
||||||
@@ -205,7 +205,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
key={0}>
|
key={0}>
|
||||||
|
|
||||||
{/* Vote Button Container */}
|
{/* Vote Button Container */}
|
||||||
{!this.state.thanksForVotingText ?
|
{!this.state.thanksForVotingText ?
|
||||||
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
|
<td id={"sponsorTimesVoteButtonsContainer" + this.idSuffix}
|
||||||
className="sponsorTimesVoteButtonsContainer">
|
className="sponsorTimesVoteButtonsContainer">
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
? this.getSkipButton(1) : null}
|
? this.getSkipButton(1) : null}
|
||||||
|
|
||||||
{/* Never show button */}
|
{/* Never show button */}
|
||||||
{!this.autoSkip || this.props.startReskip ? "" :
|
{!this.autoSkip || this.props.startReskip ? "" :
|
||||||
<td className="sponsorSkipNoticeRightSection"
|
<td className="sponsorSkipNoticeRightSection"
|
||||||
key={1}>
|
key={1}>
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeRightButton"
|
||||||
@@ -343,7 +343,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSkipButton(buttonIndex: number): JSX.Element {
|
getSkipButton(buttonIndex: number): JSX.Element {
|
||||||
if (this.state.showSkipButton[buttonIndex] && (this.segments.length > 1
|
if (this.state.showSkipButton[buttonIndex] && (this.segments.length > 1
|
||||||
|| this.segments[0].actionType !== ActionType.Poi
|
|| this.segments[0].actionType !== ActionType.Poi
|
||||||
|| this.props.unskipTime)) {
|
|| this.props.unskipTime)) {
|
||||||
|
|
||||||
@@ -365,8 +365,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
className="sponsorSkipObject sponsorSkipNoticeButton"
|
className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
style={style}
|
style={style}
|
||||||
onClick={() => this.prepAction(buttonIndex === 1 ? SkipNoticeAction.Unskip1 : SkipNoticeAction.Unskip0)}>
|
onClick={() => this.prepAction(buttonIndex === 1 ? SkipNoticeAction.Unskip1 : SkipNoticeAction.Unskip0)}>
|
||||||
{this.getSkipButtonText(buttonIndex, forceSeek ? ActionType.Skip : null)
|
{this.getSkipButtonText(buttonIndex, forceSeek ? ActionType.Skip : null)
|
||||||
+ (!forceSeek && this.state.showKeybindHint
|
+ (!forceSeek && this.state.showKeybindHint
|
||||||
? " (" + keybindToString(Config.config.skipKeybind) + ")" : "")}
|
? " (" + keybindToString(Config.config.skipKeybind) + ")" : "")}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
@@ -379,7 +379,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
for (let i = 0; i < this.segments.length; i++) {
|
for (let i = 0; i < this.segments.length; i++) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
<button className="sponsorSkipObject sponsorSkipNoticeButton"
|
||||||
style={{opacity: this.getSubmissionChooserOpacity(i),
|
style={{opacity: this.getSubmissionChooserOpacity(i),
|
||||||
color: this.getSubmissionChooserColor(i)}}
|
color: this.getSubmissionChooserColor(i)}}
|
||||||
onClick={() => this.performAction(i)}
|
onClick={() => this.performAction(i)}
|
||||||
key={"submission" + i + this.segments[i].category + this.idSuffix}>
|
key={"submission" + i + this.segments[i].category + this.idSuffix}>
|
||||||
@@ -404,7 +404,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
getSubmissionChooserColor(index: number): string {
|
getSubmissionChooserColor(index: number): string {
|
||||||
const isDownvote = this.state.actionState == SkipNoticeAction.Downvote;
|
const isDownvote = this.state.actionState == SkipNoticeAction.Downvote;
|
||||||
const isCopyDownvote = this.state.actionState == SkipNoticeAction.CopyDownvote;
|
const isCopyDownvote = this.state.actionState == SkipNoticeAction.CopyDownvote;
|
||||||
const shouldWarnUser = Config.config.isVip && (isDownvote || isCopyDownvote)
|
const shouldWarnUser = Config.config.isVip && (isDownvote || isCopyDownvote)
|
||||||
&& this.segments[index].locked === 1;
|
&& this.segments[index].locked === 1;
|
||||||
|
|
||||||
return shouldWarnUser ? this.lockedColor : this.unselectedColor;
|
return shouldWarnUser ? this.lockedColor : this.unselectedColor;
|
||||||
@@ -480,8 +480,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the action from the current state
|
* Performs the action from the current state
|
||||||
*
|
*
|
||||||
* @param index
|
* @param index
|
||||||
*/
|
*/
|
||||||
performAction(index: number, action?: SkipNoticeAction): void {
|
performAction(index: number, action?: SkipNoticeAction): void {
|
||||||
switch (action ?? this.state.actionState) {
|
switch (action ?? this.state.actionState) {
|
||||||
@@ -620,7 +620,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
// See if the title should be changed
|
// See if the title should be changed
|
||||||
if (!this.autoSkip) {
|
if (!this.autoSkip) {
|
||||||
newState.noticeTitle = chrome.i18n.getMessage("noticeTitle");
|
newState.noticeTitle = chrome.i18n.getMessage("noticeTitle");
|
||||||
}
|
}
|
||||||
|
|
||||||
//reset countdown
|
//reset countdown
|
||||||
this.setState(newState, () => {
|
this.setState(newState, () => {
|
||||||
@@ -723,7 +723,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
messages
|
messages
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addVoteButtonInfo(message: string): void {
|
addVoteButtonInfo(message: string): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
thanksForVotingText: message
|
thanksForVotingText: message
|
||||||
@@ -786,7 +786,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
case ActionType.Mute: {
|
case ActionType.Mute: {
|
||||||
return chrome.i18n.getMessage("unmute");
|
return chrome.i18n.getMessage("unmute");
|
||||||
}
|
}
|
||||||
case ActionType.Skip:
|
case ActionType.Skip:
|
||||||
default: {
|
default: {
|
||||||
return chrome.i18n.getMessage("unskip");
|
return chrome.i18n.getMessage("unskip");
|
||||||
}
|
}
|
||||||
@@ -799,7 +799,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
case ActionType.Mute: {
|
case ActionType.Mute: {
|
||||||
return chrome.i18n.getMessage("mute");
|
return chrome.i18n.getMessage("mute");
|
||||||
}
|
}
|
||||||
case ActionType.Skip:
|
case ActionType.Skip:
|
||||||
default: {
|
default: {
|
||||||
return chrome.i18n.getMessage("reskip");
|
return chrome.i18n.getMessage("reskip");
|
||||||
}
|
}
|
||||||
@@ -812,7 +812,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
|||||||
case ActionType.Mute: {
|
case ActionType.Mute: {
|
||||||
return chrome.i18n.getMessage("mute");
|
return chrome.i18n.getMessage("mute");
|
||||||
}
|
}
|
||||||
case ActionType.Skip:
|
case ActionType.Skip:
|
||||||
default: {
|
default: {
|
||||||
return chrome.i18n.getMessage("skip");
|
return chrome.i18n.getMessage("skip");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import Config from "../config";
|
|
||||||
import { Category, SegmentUUID, SponsorTime } from "../types";
|
|
||||||
|
|
||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
import * as invidiousList from "../ci/invidiouslist.json";
|
import * as invidiousList from "../ci/invidiouslist.json";
|
||||||
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes, Keybind, HashedValue, VideoID, SponsorHideType } from "./types";
|
import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, Keybind, HashedValue, VideoID, SponsorHideType } from "./types";
|
||||||
import { keybindEquals } from "./utils/configUtils";
|
import { keybindEquals } from "./utils/configUtils";
|
||||||
|
|
||||||
interface SBConfig {
|
interface SBConfig {
|
||||||
@@ -113,6 +113,7 @@ export interface SBObject {
|
|||||||
local: SBStorage;
|
local: SBStorage;
|
||||||
forceSyncUpdate(prop: string): void;
|
forceSyncUpdate(prop: string): void;
|
||||||
forceLocalUpdate(prop: string): void;
|
forceLocalUpdate(prop: string): void;
|
||||||
|
resetToDefault(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Config: SBObject = {
|
const Config: SBObject = {
|
||||||
@@ -289,7 +290,8 @@ const Config: SBObject = {
|
|||||||
config: null,
|
config: null,
|
||||||
local: null,
|
local: null,
|
||||||
forceSyncUpdate,
|
forceSyncUpdate,
|
||||||
forceLocalUpdate
|
forceLocalUpdate,
|
||||||
|
resetToDefault
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function setup
|
// Function setup
|
||||||
@@ -300,7 +302,7 @@ function configProxy(): { sync: SBConfig, local: SBStorage } {
|
|||||||
for (const key in changes) {
|
for (const key in changes) {
|
||||||
Config.cachedSyncConfig[key] = changes[key].newValue;
|
Config.cachedSyncConfig[key] = changes[key].newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const callback of Config.configSyncListeners) {
|
for (const callback of Config.configSyncListeners) {
|
||||||
callback(changes);
|
callback(changes);
|
||||||
}
|
}
|
||||||
@@ -310,7 +312,7 @@ function configProxy(): { sync: SBConfig, local: SBStorage } {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const syncHandler: ProxyHandler<SBConfig> = {
|
const syncHandler: ProxyHandler<SBConfig> = {
|
||||||
set<K extends keyof SBConfig>(obj: SBConfig, prop: K, value: SBConfig[K]) {
|
set<K extends keyof SBConfig>(obj: SBConfig, prop: K, value: SBConfig[K]) {
|
||||||
Config.cachedSyncConfig[prop] = value;
|
Config.cachedSyncConfig[prop] = value;
|
||||||
@@ -327,10 +329,10 @@ function configProxy(): { sync: SBConfig, local: SBStorage } {
|
|||||||
|
|
||||||
return obj[prop] || data;
|
return obj[prop] || data;
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteProperty(obj: SBConfig, prop: keyof SBConfig) {
|
deleteProperty(obj: SBConfig, prop: keyof SBConfig) {
|
||||||
chrome.storage.sync.remove(<string> prop);
|
chrome.storage.sync.remove(<string> prop);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,10 +354,10 @@ function configProxy(): { sync: SBConfig, local: SBStorage } {
|
|||||||
|
|
||||||
return obj[prop] || data;
|
return obj[prop] || data;
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteProperty(obj: SBStorage, prop: keyof SBStorage) {
|
deleteProperty(obj: SBStorage, prop: keyof SBStorage) {
|
||||||
chrome.storage.local.remove(<string> prop);
|
chrome.storage.local.remove(<string> prop);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +381,7 @@ function forceLocalUpdate(prop: string): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchConfig(): Promise<void> {
|
async function fetchConfig(): Promise<void> {
|
||||||
await Promise.all([new Promise<void>((resolve) => {
|
await Promise.all([new Promise<void>((resolve) => {
|
||||||
chrome.storage.sync.get(null, function(items) {
|
chrome.storage.sync.get(null, function(items) {
|
||||||
Config.cachedSyncConfig = <SBConfig> <unknown> items;
|
Config.cachedSyncConfig = <SBConfig> <unknown> items;
|
||||||
@@ -387,7 +389,7 @@ async function fetchConfig(): Promise<void> {
|
|||||||
});
|
});
|
||||||
}), new Promise<void>((resolve) => {
|
}), new Promise<void>((resolve) => {
|
||||||
chrome.storage.local.get(null, function(items) {
|
chrome.storage.local.get(null, function(items) {
|
||||||
Config.cachedLocalStorage = <SBStorage> <unknown> items;
|
Config.cachedLocalStorage = <SBStorage> <unknown> items;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
})]);
|
})]);
|
||||||
@@ -431,9 +433,9 @@ function migrateOldSyncFormats(config: SBConfig) {
|
|||||||
if (!config["autoSkipOnMusicVideosUpdate"]) {
|
if (!config["autoSkipOnMusicVideosUpdate"]) {
|
||||||
config["autoSkipOnMusicVideosUpdate"] = true;
|
config["autoSkipOnMusicVideosUpdate"] = true;
|
||||||
for (const selection of config.categorySelections) {
|
for (const selection of config.categorySelections) {
|
||||||
if (selection.name === "music_offtopic"
|
if (selection.name === "music_offtopic"
|
||||||
&& selection.option === CategorySkipOption.AutoSkip) {
|
&& selection.option === CategorySkipOption.AutoSkip) {
|
||||||
|
|
||||||
config.autoSkipOnMusicVideos = true;
|
config.autoSkipOnMusicVideos = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -522,6 +524,16 @@ function addDefaults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetToDefault() {
|
||||||
|
chrome.storage.sync.set({
|
||||||
|
...Config.syncDefaults,
|
||||||
|
userID: Config.config.userID,
|
||||||
|
minutesSaved: Config.config.minutesSaved,
|
||||||
|
skipCount: Config.config.skipCount,
|
||||||
|
sponsorTimesContributed: Config.config.sponsorTimesContributed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Sync config
|
// Sync config
|
||||||
setupConfig();
|
setupConfig();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { isSafari, keybindEquals } from "./utils/configUtils";
|
|||||||
import { CategoryPill } from "./render/CategoryPill";
|
import { CategoryPill } from "./render/CategoryPill";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "./utils/animationUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
|
import { logDebug } from "./utils/logger";
|
||||||
|
|
||||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||||
@@ -275,6 +276,7 @@ function resetValues() {
|
|||||||
switchingVideos = false;
|
switchingVideos = false;
|
||||||
} else {
|
} else {
|
||||||
switchingVideos = true;
|
switchingVideos = true;
|
||||||
|
logDebug("Setting switching videos to true (reset data)");
|
||||||
}
|
}
|
||||||
|
|
||||||
firstEvent = true;
|
firstEvent = true;
|
||||||
@@ -319,9 +321,6 @@ async function videoIDChange(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get new video info
|
|
||||||
// getVideoInfo(); // Seems to have been replaced
|
|
||||||
|
|
||||||
// Update whitelist data when the video data is loaded
|
// Update whitelist data when the video data is loaded
|
||||||
whitelistCheck();
|
whitelistCheck();
|
||||||
|
|
||||||
@@ -448,6 +447,8 @@ function videoOnReadyListener(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cancelSponsorSchedule(): void {
|
function cancelSponsorSchedule(): void {
|
||||||
|
logDebug("Pausing skipping");
|
||||||
|
|
||||||
if (currentSkipSchedule !== null) {
|
if (currentSkipSchedule !== null) {
|
||||||
clearTimeout(currentSkipSchedule);
|
clearTimeout(currentSkipSchedule);
|
||||||
currentSkipSchedule = null;
|
currentSkipSchedule = null;
|
||||||
@@ -470,11 +471,13 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
// Reset lastCheckVideoTime
|
// Reset lastCheckVideoTime
|
||||||
lastCheckVideoTime = -1;
|
lastCheckVideoTime = -1;
|
||||||
lastCheckTime = 0;
|
lastCheckTime = 0;
|
||||||
console.warn("[SB] Ad playing, pausing skipping");
|
logDebug("[SB] Ad playing, pausing skipping");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDebug(`Considering to start skipping: ${!video}, ${video?.paused}`);
|
||||||
|
|
||||||
if (!video || video.paused) return;
|
if (!video || video.paused) return;
|
||||||
if (currentTime === undefined || currentTime === null) {
|
if (currentTime === undefined || currentTime === null) {
|
||||||
const virtualTime = lastTimeFromWaitingEvent ?? (lastKnownVideoTime.videoTime ?
|
const virtualTime = lastTimeFromWaitingEvent ?? (lastKnownVideoTime.videoTime ?
|
||||||
@@ -506,6 +509,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
|
|
||||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||||
|
|
||||||
|
logDebug(`Ready to start skipping: ${skipInfo.index} at ${currentTime}`);
|
||||||
if (skipInfo.index === -1) return;
|
if (skipInfo.index === -1) return;
|
||||||
|
|
||||||
const currentSkip = skipInfo.array[skipInfo.index];
|
const currentSkip = skipInfo.array[skipInfo.index];
|
||||||
@@ -526,6 +530,8 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDebug(`Next step in starting skipping: ${!shouldSkip(currentSkip)}, ${!sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)}`);
|
||||||
|
|
||||||
// Don't skip if this category should not be skipped
|
// Don't skip if this category should not be skipped
|
||||||
if (!shouldSkip(currentSkip) && !sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)) return;
|
if (!shouldSkip(currentSkip) && !sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)) return;
|
||||||
|
|
||||||
@@ -566,6 +572,8 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
// Use interval instead of timeout near the end to combat imprecise video time
|
// Use interval instead of timeout near the end to combat imprecise video time
|
||||||
const startIntervalTime = performance.now();
|
const startIntervalTime = performance.now();
|
||||||
const startVideoTime = Math.max(currentTime, video.currentTime);
|
const startVideoTime = Math.max(currentTime, video.currentTime);
|
||||||
|
logDebug(`Starting setInterval skipping ${video.currentTime} to skip at ${skipTime[0]}`);
|
||||||
|
|
||||||
currentSkipInterval = setInterval(() => {
|
currentSkipInterval = setInterval(() => {
|
||||||
const intervalDuration = performance.now() - startIntervalTime;
|
const intervalDuration = performance.now() - startIntervalTime;
|
||||||
if (intervalDuration >= delayTime || video.currentTime >= skipTime[0]) {
|
if (intervalDuration >= delayTime || video.currentTime >= skipTime[0]) {
|
||||||
@@ -580,6 +588,8 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
}
|
}
|
||||||
}, 1);
|
}, 1);
|
||||||
} else {
|
} else {
|
||||||
|
logDebug(`Starting timeout to skip ${video.currentTime} to skip at ${skipTime[0]}`);
|
||||||
|
|
||||||
// Schedule for right before to be more precise than normal timeout
|
// Schedule for right before to be more precise than normal timeout
|
||||||
currentSkipSchedule = setTimeout(skippingFunction, Math.max(0, delayTime - 100));
|
currentSkipSchedule = setTimeout(skippingFunction, Math.max(0, delayTime - 100));
|
||||||
}
|
}
|
||||||
@@ -669,6 +679,8 @@ function setupVideoListeners() {
|
|||||||
|
|
||||||
if (switchingVideos) {
|
if (switchingVideos) {
|
||||||
switchingVideos = false;
|
switchingVideos = false;
|
||||||
|
logDebug("Setting switching videos to false");
|
||||||
|
|
||||||
// If already segments loaded before video, retry to skip starting segments
|
// If already segments loaded before video, retry to skip starting segments
|
||||||
if (sponsorTimes) startSkipScheduleCheckingForStartSponsors();
|
if (sponsorTimes) startSkipScheduleCheckingForStartSponsors();
|
||||||
}
|
}
|
||||||
@@ -691,7 +703,7 @@ function setupVideoListeners() {
|
|||||||
|
|
||||||
if (startedWaiting) {
|
if (startedWaiting) {
|
||||||
startedWaiting = false;
|
startedWaiting = false;
|
||||||
console.warn(`[SB] Starting schedule after buffering: ${Math.abs(lastCheckVideoTime - video.currentTime) > 0.3
|
logDebug(`[SB] Playing event after buffering: ${Math.abs(lastCheckVideoTime - video.currentTime) > 0.3
|
||||||
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)}`);
|
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,7 +746,7 @@ function setupVideoListeners() {
|
|||||||
};
|
};
|
||||||
video.addEventListener('pause', () => paused());
|
video.addEventListener('pause', () => paused());
|
||||||
video.addEventListener('waiting', () => {
|
video.addEventListener('waiting', () => {
|
||||||
console.warn("[SB] Not skipping due to buffering");
|
logDebug("[SB] Not skipping due to buffering");
|
||||||
startedWaiting = true;
|
startedWaiting = true;
|
||||||
|
|
||||||
paused();
|
paused();
|
||||||
@@ -927,12 +939,10 @@ function startSkipScheduleCheckingForStartSponsors() {
|
|||||||
// See if there are any starting sponsors
|
// See if there are any starting sponsors
|
||||||
let startingSegmentTime = getStartTimeFromUrl(document.URL) || -1;
|
let startingSegmentTime = getStartTimeFromUrl(document.URL) || -1;
|
||||||
let found = false;
|
let found = false;
|
||||||
let startingSegment: SponsorTime = null;
|
|
||||||
for (const time of sponsorTimes) {
|
for (const time of sponsorTimes) {
|
||||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||||
&& time.actionType !== ActionType.Poi) {
|
&& time.actionType !== ActionType.Poi) {
|
||||||
startingSegmentTime = time.segment[0];
|
startingSegmentTime = time.segment[0];
|
||||||
startingSegment = time;
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -942,7 +952,6 @@ function startSkipScheduleCheckingForStartSponsors() {
|
|||||||
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSegmentTime && time.segment[1] > video.currentTime
|
||||||
&& time.actionType !== ActionType.Poi) {
|
&& time.actionType !== ActionType.Poi) {
|
||||||
startingSegmentTime = time.segment[0];
|
startingSegmentTime = time.segment[0];
|
||||||
startingSegment = time;
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -980,26 +989,6 @@ function startSkipScheduleCheckingForStartSponsors() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the video info for the current tab from YouTube
|
|
||||||
*
|
|
||||||
* TODO: Replace
|
|
||||||
*/
|
|
||||||
async function getVideoInfo(): Promise<void> {
|
|
||||||
const result = await utils.asyncRequestToCustomServer("GET", "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID + "&html5=1&c=TVHTML5&cver=7.20190319");
|
|
||||||
|
|
||||||
if (result.ok) {
|
|
||||||
const decodedData = decodeURIComponent(result.responseText).match(/player_response=([^&]*)/)[1];
|
|
||||||
if (!decodedData) {
|
|
||||||
console.error("[SB] Failed at getting video info from YouTube.");
|
|
||||||
console.error("[SB] Data returned from YouTube: " + result.responseText);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
videoInfo = JSON.parse(decodedData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getYouTubeVideoID(document: Document): string | boolean {
|
function getYouTubeVideoID(document: Document): string | boolean {
|
||||||
const url = document.URL;
|
const url = document.URL;
|
||||||
// clips should never skip, going from clip to full video has no indications.
|
// clips should never skip, going from clip to full video has no indications.
|
||||||
@@ -1134,7 +1123,7 @@ async function whitelistCheck() {
|
|||||||
?? document.querySelector("a.ytp-title-channel-logo") // YouTube Embed
|
?? document.querySelector("a.ytp-title-channel-logo") // YouTube Embed
|
||||||
?? document.querySelector(".channel-profile #channel-name")?.parentElement.parentElement // Invidious
|
?? document.querySelector(".channel-profile #channel-name")?.parentElement.parentElement // Invidious
|
||||||
?? document.querySelector("a.slim-owner-icon-and-title")) // Mobile YouTube
|
?? document.querySelector("a.slim-owner-icon-and-title")) // Mobile YouTube
|
||||||
?.getAttribute("href")?.match(/\/channel\/(UC[a-zA-Z0-9_-]{22})/)?.[1];
|
?.getAttribute("href")?.match(/(?:\/c\/|\/channel\/)(UC[a-zA-Z0-9_-]{22}|[a-zA-Z0-9_-]+)/)?.[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await utils.wait(() => !!getChannelID(), 6000, 20);
|
await utils.wait(() => !!getChannelID(), 6000, 20);
|
||||||
@@ -1142,14 +1131,12 @@ async function whitelistCheck() {
|
|||||||
channelIDInfo = {
|
channelIDInfo = {
|
||||||
status: ChannelIDStatus.Found,
|
status: ChannelIDStatus.Found,
|
||||||
id: getChannelID().match(/^\/?([^\s/]+)/)[0]
|
id: getChannelID().match(/^\/?([^\s/]+)/)[0]
|
||||||
}
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
channelIDInfo = {
|
channelIDInfo = {
|
||||||
status: ChannelIDStatus.Failed,
|
status: ChannelIDStatus.Failed,
|
||||||
id: null
|
id: null
|
||||||
}
|
};
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//see if this is a whitelisted channel
|
//see if this is a whitelisted channel
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import Config from "../config";
|
|||||||
import { SponsorTime } from "../types";
|
import { SponsorTime } from "../types";
|
||||||
import { getSkippingText } from "../utils/categoryUtils";
|
import { getSkippingText } from "../utils/categoryUtils";
|
||||||
import { keybindToString } from "../utils/configUtils";
|
import { keybindToString } from "../utils/configUtils";
|
||||||
|
|
||||||
import Utils from "../utils";
|
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../utils/animationUtils";
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
export interface SkipButtonControlBarProps {
|
export interface SkipButtonControlBarProps {
|
||||||
skip: (segment: SponsorTime) => void;
|
skip: (segment: SponsorTime) => void;
|
||||||
@@ -53,7 +50,7 @@ export class SkipButtonControlBar {
|
|||||||
this.skipIcon.id = "sbSkipIconControlBarImage";
|
this.skipIcon.id = "sbSkipIconControlBarImage";
|
||||||
|
|
||||||
this.textContainer = document.createElement("div");
|
this.textContainer = document.createElement("div");
|
||||||
|
|
||||||
this.container.appendChild(this.skipIcon);
|
this.container.appendChild(this.skipIcon);
|
||||||
this.container.appendChild(this.textContainer);
|
this.container.appendChild(this.textContainer);
|
||||||
this.container.addEventListener("click", () => this.toggleSkip());
|
this.container.addEventListener("click", () => this.toggleSkip());
|
||||||
@@ -73,7 +70,7 @@ export class SkipButtonControlBar {
|
|||||||
attachToPage(): void {
|
attachToPage(): void {
|
||||||
const mountingContainer = this.getMountingContainer();
|
const mountingContainer = this.getMountingContainer();
|
||||||
this.chapterText = document.querySelector(".ytp-chapter-container");
|
this.chapterText = document.querySelector(".ytp-chapter-container");
|
||||||
|
|
||||||
if (mountingContainer && !mountingContainer.contains(this.container)) {
|
if (mountingContainer && !mountingContainer.contains(this.container)) {
|
||||||
if (this.onMobileYouTube) {
|
if (this.onMobileYouTube) {
|
||||||
mountingContainer.appendChild(this.container);
|
mountingContainer.appendChild(this.container);
|
||||||
@@ -172,10 +169,10 @@ export class SkipButtonControlBar {
|
|||||||
const overlay = document.getElementById("player-control-overlay");
|
const overlay = document.getElementById("player-control-overlay");
|
||||||
|
|
||||||
if (overlay && this.enabled) {
|
if (overlay && this.enabled) {
|
||||||
if (overlay?.classList?.contains("pointer-events-off")) {
|
if (overlay?.classList?.contains("fadein")) {
|
||||||
this.hideButton();
|
|
||||||
} else {
|
|
||||||
this.showButton();
|
this.showButton();
|
||||||
|
} else {
|
||||||
|
this.hideButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,4 +217,3 @@ export class SkipButtonControlBar {
|
|||||||
this.container.style.left = this.left + "px";
|
this.container.style.left = this.left + "px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -232,12 +232,22 @@ async function init() {
|
|||||||
}
|
}
|
||||||
case "button-press": {
|
case "button-press": {
|
||||||
const actionButton = optionsElements[i].querySelector(".trigger-button");
|
const actionButton = optionsElements[i].querySelector(".trigger-button");
|
||||||
|
const confirmMessage = optionsElements[i].getAttribute("data-confirm-message");
|
||||||
|
|
||||||
switch(optionsElements[i].getAttribute("data-sync")) {
|
actionButton.addEventListener("click", () => {
|
||||||
case "copyDebugInformation":
|
if (confirmMessage !== null && !confirm(chrome.i18n.getMessage(confirmMessage))) {
|
||||||
actionButton.addEventListener("click", copyDebugOutputToClipboard);
|
return;
|
||||||
break;
|
}
|
||||||
}
|
switch (optionsElements[i].getAttribute("data-sync")) {
|
||||||
|
case "copyDebugInformation":
|
||||||
|
copyDebugOutputToClipboard();
|
||||||
|
break;
|
||||||
|
case "resetToDefault":
|
||||||
|
Config.resetToDefault();
|
||||||
|
window.location.reload();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/utils/logger.ts
Normal file
12
src/utils/logger.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
window["SBLogs"] = {
|
||||||
|
debug: [],
|
||||||
|
warn: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export function logDebug(message: string) {
|
||||||
|
window["SBLogs"].debug.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logWarn(message: string) {
|
||||||
|
window["SBLogs"].warn.push(message);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user