Compare commits

..

17 Commits
3.4.1 ... 3.5.1

Author SHA1 Message Date
Ajay Ramachandran
e87efe2c67 bump version 2021-10-23 15:09:47 -04:00
Ajay Ramachandran
069299e968 Merge pull request #1021 from opl-/fix/wheel-edits
Fix mouse wheel can enter edit mode
2021-10-22 23:11:22 -04:00
opl-
4d09a603fe Fix mouse wheel can enter edit mode 2021-10-23 00:52:52 +02:00
Ajay Ramachandran
8b595af5e9 Fix skip to highlight not hiding when none 2021-10-22 01:50:17 -04:00
Ajay Ramachandran
41739a8799 Merge branch 'master' of https://github.com/ajayyy/SponsorBlock 2021-10-22 01:29:47 -04:00
Ajay Ramachandran
23bebbca7c Fix help page on mobile 2021-10-22 01:29:46 -04:00
Ajay Ramachandran
5f2af12150 bump version 2021-10-21 22:39:15 -04:00
Ajay Ramachandran
628abd03f0 Added swiping away for skip to highlight on mobile 2021-10-21 22:33:49 -04:00
Ajay Ramachandran
8e254c5807 Make bigger buttons on mobile skip button 2021-10-21 22:11:26 -04:00
Ajay Ramachandran
0647576d6f Add animation for skip to highlight on mobile 2021-10-21 21:58:43 -04:00
Ajay Ramachandran
c803ae9499 Add skip to highlight to mobile 2021-10-21 01:30:52 -04:00
Ajay Ramachandran
d1e6421e5b Fix type errors 2021-10-20 19:33:48 -04:00
Ajay Ramachandran
bd0a6aaaad Hide submissions on mobile 2021-10-20 19:28:00 -04:00
Ajay Ramachandran
1e8b176c69 Allow finer tuned previewing 2021-10-19 22:57:46 -04:00
Ajay Ramachandran
14018798f7 Add wiki links to options 2021-10-19 19:10:27 -04:00
Ajay Ramachandran
a0d06ca6e8 Fix start time in preview bar in wrong place 2021-10-19 19:00:30 -04:00
Ajay Ramachandran
38b1dda20b Add support for required segment parameter 2021-10-18 19:51:44 -04:00
15 changed files with 271 additions and 86 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "__MSG_fullName__",
"short_name": "SponsorBlock",
"version": "3.4.1",
"version": "3.5.1",
"default_locale": "en",
"description": "__MSG_Description__",
"homepage_url": "https://sponsor.ajay.app",

View File

@@ -1,3 +1,7 @@
.hidden {
display: none;
}
#previewbar {
overflow: visible;
padding: 0;
@@ -290,6 +294,10 @@
float: right;
}
.sponsorSkipNoticeCloseButton.biggerCloseButton {
padding: 20px;
}
.sponsorSkipMessage {
font-size: 14px;
font-weight: bold;
@@ -298,6 +306,7 @@
margin-top: auto;
display: inline-block;
margin-right: 10px;
margin-bottom: auto;
}
.sponsorSkipInfo {
@@ -526,12 +535,35 @@ input::-webkit-inner-spin-button {
.skipButtonControlBarContainer {
cursor: pointer;
display: flex;
color: white;
}
.skipButtonControlBarContainer.hidden {
display: none !important;
}
.skipButtonControlBarContainer.mobile {
bottom: 30%;
margin-left: 5px;
position: absolute;
height: 20px;
background-color: #00000030;
opacity: 0.5;
border-radius: 10px;
padding: 4px;
}
.skipButtonControlBarContainer.mobile.textDisabled {
padding: 0;
background-color: transparent;
}
.skipButtonControlBarContainer.mobile > div {
margin: auto;
margin-left: 5px;
}
#sbSkipIconControlBarImage {
height: 60%;
top: 0px;
@@ -540,6 +572,11 @@ input::-webkit-inner-spin-button {
margin: auto;
}
.mobile #sbSkipIconControlBarImage {
height: 100%;
width: 20px;
}
.sponsorBlockTooltip {
position: absolute;
background-color: rgba(28, 28, 28, 0.7);

View File

@@ -3,6 +3,7 @@
<head>
<title> SponsorBlock </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="styles.css" rel="stylesheet"/>
@@ -39,7 +40,7 @@
__MSG_helpPageFeatureDisclaimer__
</p>
<iframe src="../options/options.html#embed" width="100%" height="500px" style="border: none"></iframe>
<iframe class="optionsFrame" src="../options/options.html#embed" style="border: none"></iframe>
<h1>__MSG_helpPageHowSkippingWorks__</h1>
@@ -56,14 +57,12 @@
__MSG_helpPageHowSkippingWorks2__
</p>
<div class="center"><img height="120px" src="images/voting on notice.gif"></div>
<div class="center"><img src="images/voting on notice.gif"></div>
<h1>__MSG_Submitting__</h1>
<p class="projectPreview">
<span class="projectPreviewImageLargeRight">
<img src="https://i.imgur.com/A1ilk6x.gif">
</span>
<img class="projectPreviewImageLarge" src="https://i.imgur.com/A1ilk6x.gif">
__MSG_helpPageSubmitting1__

View File

@@ -40,14 +40,6 @@ body {
transform: translateY(-20%);
}
.projectPreviewImageLargeRight {
position: absolute;
right: -210px;
width: 200px;
top: 50%;
transform: translateY(-50%);
}
.createdBy {
font-size: 14px;
text-align: center;
@@ -142,18 +134,9 @@ p,li,code,a {
overflow-wrap: break-word;
}
@media screen and (orientation:portrait) {
p,li,code,a {
max-width: 100%;
}
.projectPreviewImage {
position: unset;
width: 130px;
display: block;
margin: auto;
transform: none;
}
.optionsFrame {
width: 100%;
height: 500px;
}
.previewImage {
@@ -187,4 +170,33 @@ svg {
#sbDonate {
font-size: 10px;
}
@media screen and (orientation:portrait) {
.projectPreviewImage {
position: unset;
width: 50%;
display: block;
margin: auto;
transform: none;
}
.projectPreviewImageLarge {
position: unset;
left: 0;
width: 50%;
display: block;
margin: auto;
transform: unset;
}
.container {
max-width: 100%;
margin: 5px;
text-align: center;
}
p,li,code,a {
text-align: center;
}
}

View File

@@ -1,6 +1,7 @@
import * as React from "react";
import Config from "../config"
import * as CompileConfig from "../../config.json";
import { Category, CategorySkipOption } from "../types";
import { getCategoryActionType } from "../utils/categoryUtils";
@@ -93,6 +94,10 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
<td
colSpan={2}>
{chrome.i18n.getMessage("category_" + this.props.category + "_description")}
{' '}
<a href={CompileConfig.wikiLinks[this.props.category]} target="_blank" rel="noreferrer">
{`${chrome.i18n.getMessage("LearnMore")}`}
</a>
</td>
</tr>

View File

@@ -33,6 +33,7 @@ export interface NoticeProps {
zIndex?: number,
style?: React.CSSProperties
biggerCloseButton?: boolean;
}
export interface NoticeState {
@@ -151,7 +152,8 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
{/* Close button */}
<img src={chrome.extension.getURL("icons/close.png")}
className="sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
+ (this.props.biggerCloseButton ? " biggerCloseButton" : "")}
onClick={() => this.close()}>
</img>
</td>

View File

@@ -188,6 +188,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
maxCountdownTime={this.state.maxCountdownTime}
videoSpeed={() => this.contentContainer().v?.playbackRate}
style={noticeStyle}
biggerCloseButton={this.contentContainer().onMobileYouTube}
ref={this.noticeRef}
closeListener={() => this.closeListener()}
smaller={this.state.smaller}
@@ -350,13 +351,21 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
if (this.state.showSkipButton && (this.segments.length > 1
|| getCategoryActionType(this.segments[0].category) !== CategoryActionType.POI
|| this.props.unskipTime)) {
const style: React.CSSProperties = {
marginLeft: "4px",
color: (this.state.actionState === SkipNoticeAction.Unskip) ? this.selectedColor : this.unselectedColor
};
if (this.contentContainer().onMobileYouTube) {
style.padding = "20px";
style.minWidth = "100px";
}
return (
<span className="sponsorSkipNoticeUnskipSection">
<button id={"sponsorSkipUnskipButton" + this.idSuffix}
className="sponsorSkipObject sponsorSkipNoticeButton"
style={{marginLeft: "4px",
color: (this.state.actionState === SkipNoticeAction.Unskip) ? this.selectedColor : this.unselectedColor
}}
style={style}
onClick={() => this.prepAction(SkipNoticeAction.Unskip)}>
{this.state.skipButtonText + (this.state.showKeybindHint ? " (" + Config.config.skipKeybind + ")" : "")}
</button>

View File

@@ -152,8 +152,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
<div id={"sponsorTimesContainer" + this.idSuffix}
className="sponsorTimeDisplay"
onClick={this.toggleEditTime.bind(this)}
onWheel={this.toggleEditTime.bind(this)}>
onClick={this.toggleEditTime.bind(this)}>
{utils.getFormattedTime(segment[0], true) +
((!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable)
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
@@ -212,7 +211,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
{(!isNaN(segment[1]) && getCategoryActionType(sponsorTime.category) === CategoryActionType.Skippable) ? (
<span id={"sponsorTimePreviewButton" + this.idSuffix}
className="sponsorTimeEditButton"
onClick={this.previewTime.bind(this)}>
onClick={(e) => this.previewTime(e.ctrlKey, e.shiftKey)}>
{chrome.i18n.getMessage("preview")}
</span>
): ""}
@@ -436,13 +435,17 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.props.contentContainer().updatePreviewBar();
}
previewTime(): void {
previewTime(ctrlPressed = false, shiftPressed = false): void {
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index;
const skipTime = sponsorTimes[index].segment[0];
this.props.contentContainer().previewTime(skipTime - (2 * this.props.contentContainer().v.playbackRate));
let seekTime = 2;
if (ctrlPressed) seekTime = 0.5;
if (shiftPressed) seekTime = 0.25;
this.props.contentContainer().previewTime(skipTime - (seekTime * this.props.contentContainer().v.playbackRate));
}
inspectTime(): void {

View File

@@ -17,6 +17,7 @@ import { getCategoryActionType } from "./utils/categoryUtils";
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
import { Tooltip } from "./render/Tooltip";
import { getStartTimeFromUrl } from "./utils/urlParser";
import { getControls } from "./utils/pageUtils";
// Hack to get the CSS loaded on permission-based sites (Invidious)
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
@@ -151,7 +152,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
//send the sponsor times along with if it's found
sendResponse({
found: sponsorDataFound,
sponsorTimes: sponsorTimes
sponsorTimes: sponsorTimes,
onMobileYouTube
});
if (!request.updating && popupInitialised && document.getElementById("sponsorBlockPopupContainer") != null) {
@@ -191,7 +193,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
case "refreshSegments":
sponsorsLookup(sponsorVideoID, false).then(() => sendResponse({
found: sponsorDataFound,
sponsorTimes: sponsorTimes
sponsorTimes: sponsorTimes,
onMobileYouTube
}));
return true;
@@ -336,6 +339,8 @@ async function videoIDChange(id) {
function handleMobileControlsMutations(): void {
updateVisibilityOfPlayerControlsButton();
skipButtonControlBar?.updateMobileControls();
if (previewBar !== null) {
if (document.body.contains(previewBar.container)) {
const progressBarBackground = document.querySelector<HTMLElement>(".progress-bar-background");
@@ -650,7 +655,8 @@ function setupSkipButtonControlBar() {
skippingSegments: [segment],
openNotice: true,
forceAutoSkip: true
})
}),
onMobileYouTube
});
}
@@ -676,12 +682,25 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
categories.push(categorySelection.name);
}
const extraRequestData: Record<string, unknown> = {};
const windowHash = window.location.hash.substr(1);
if (windowHash) {
const params: Record<string, unknown> = windowHash.split('&').reduce((acc, param) => {
const [key, value] = param.split('=');
acc[key] = value;
return acc;
}, {});
if (params.requiredSegment) extraRequestData.requiredSegment = params.requiredSegment;
}
// Check for hashPrefix setting
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
categories,
actionTypes: Config.config.muteSegments ? [ActionType.Skip, ActionType.Mute] : [ActionType.Skip],
userAgent: `${chrome.runtime.id}`
userAgent: `${chrome.runtime.id}`,
...extraRequestData
});
if (response?.ok) {
@@ -1234,6 +1253,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
&& skippingSegments.length === 1
&& getCategoryActionType(skippingSegments[0].category) === CategoryActionType.POI) {
skipButtonControlBar.enable(skippingSegments[0], !Config.config.highlightCategoryUpdate ? 15 : 0);
if (onMobileYouTube) skipButtonControlBar.setShowKeybindHint(false);
if (!Config.config.highlightCategoryUpdate) {
new Tooltip({
@@ -1254,6 +1274,7 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
//send out the message saying that a sponsor message was skipped
if (!Config.config.dontShowNotice || !autoSkip) {
const newSkipNotice = new SkipNotice(skippingSegments, autoSkip, skipNoticeContentContainer, unskipTime);
if (onMobileYouTube) newSkipNotice.setShowKeybindHint(false);
skipNotices.push(newSkipNotice);
activeSkipKeybindElement?.setShowKeybindHint(false);
@@ -1341,27 +1362,6 @@ function shouldSkip(segment: SponsorTime): boolean {
(Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic"));
}
function getControls(): HTMLElement | false {
const controlsSelectors = [
// YouTube
".ytp-right-controls",
// Mobile YouTube
".player-controls-top",
// Invidious/videojs video element's controls element
".vjs-control-bar",
];
for (const controlsSelector of controlsSelectors) {
const controls = document.querySelectorAll(controlsSelector);
if (controls && controls.length > 0) {
return <HTMLElement> controls[controls.length - 1];
}
}
return false;
}
/** Creates any missing buttons on the YouTube player if possible. */
async function createButtons(): Promise<void> {
if (onMobileYouTube) return;

View File

@@ -199,7 +199,9 @@ class PreviewBar {
bar.style.position = "absolute";
const duration = segment[1] - segment[0];
if (segment[1] - segment[0] > 0) bar.style.width = this.timeToPercentage(segment[1] - segment[0]);
bar.style.left = this.timeToPercentage(Math.min(this.videoDuration - Math.max(0, duration), segment[0]));
const time = segment[1] ? Math.min(this.videoDuration - Math.max(0, duration), segment[0]) : segment[0];
bar.style.left = this.timeToPercentage(time);
return bar;
}

View File

@@ -7,6 +7,7 @@ const utils = new Utils();
export interface SkipButtonControlBarProps {
skip: (segment: SponsorTime) => void;
onMobileYouTube: boolean;
}
export class SkipButtonControlBar {
@@ -18,18 +19,31 @@ export class SkipButtonControlBar {
segment: SponsorTime;
showKeybindHint = true;
onMobileYouTube: boolean;
enabled = false;
timeout: NodeJS.Timeout;
duration = 0;
skip: (segment: SponsorTime) => void;
// Used if on mobile page
hideButton: () => void;
showButton: () => void;
// Used by mobile only for swiping away
left = 0;
swipeStart = 0;
constructor(props: SkipButtonControlBarProps) {
this.skip = props.skip;
this.onMobileYouTube = props.onMobileYouTube;
this.container = document.createElement("div");
this.container.classList.add("skipButtonControlBarContainer");
this.container.classList.add("hidden");
if (this.onMobileYouTube) this.container.classList.add("mobile");
this.skipIcon = document.createElement("img");
this.skipIcon.src = chrome.runtime.getURL("icons/skipIcon.svg");
@@ -43,6 +57,11 @@ export class SkipButtonControlBar {
this.container.addEventListener("click", () => this.toggleSkip());
this.container.addEventListener("mouseenter", () => this.stopTimer());
this.container.addEventListener("mouseleave", () => this.startTimer());
if (this.onMobileYouTube) {
this.container.addEventListener("touchstart", (e) => this.handleTouchStart(e));
this.container.addEventListener("touchmove", (e) => this.handleTouchMove(e));
this.container.addEventListener("touchend", () => this.handleTouchEnd());
}
}
getElement(): HTMLElement {
@@ -50,21 +69,38 @@ export class SkipButtonControlBar {
}
attachToPage(): void {
const leftControlsContainer = document.querySelector(".ytp-left-controls");
const mountingContainer = this.getMountingContainer();
this.chapterText = document.querySelector(".ytp-chapter-container");
if (leftControlsContainer && !leftControlsContainer.contains(this.container)) {
leftControlsContainer.insertBefore(this.container, this.chapterText);
if (Config.config.autoHideInfoButton) {
utils.setupAutoHideAnimation(this.skipIcon, leftControlsContainer, false, false);
if (mountingContainer && !mountingContainer.contains(this.container)) {
if (this.onMobileYouTube) {
mountingContainer.appendChild(this.container);
} else {
mountingContainer.insertBefore(this.container, this.chapterText);
}
if (Config.config.autoHideInfoButton && !this.onMobileYouTube) {
utils.setupAutoHideAnimation(this.skipIcon, mountingContainer, false, false);
} else {
const { hide, show } = utils.setupCustomHideAnimation(this.skipIcon, mountingContainer, false, false);
this.hideButton = hide;
this.showButton = show;
}
}
}
private getMountingContainer(): HTMLElement {
if (!this.onMobileYouTube) {
return document.querySelector(".ytp-left-controls");
} else {
return document.getElementById("player-container-id");
}
}
enable(segment: SponsorTime, duration?: number): void {
if (duration) this.duration = duration;
this.segment = segment;
this.enabled = true;
this.refreshText();
this.textContainer?.classList?.remove("hidden");
@@ -103,6 +139,8 @@ export class SkipButtonControlBar {
this.chapterText?.classList?.remove("hidden");
this.getChapterPrefix()?.classList?.remove("hidden");
this.enabled = false;
}
toggleSkip(): void {
@@ -116,12 +154,28 @@ export class SkipButtonControlBar {
return;
}
this.container.classList.add("textDisabled");
this.textContainer?.classList?.add("hidden");
this.chapterText?.classList?.remove("hidden");
this.getChapterPrefix()?.classList?.add("hidden");
utils.enableAutoHideAnimation(this.skipIcon);
if (this.onMobileYouTube) {
this.hideButton();
}
}
updateMobileControls(): void {
const overlay = document.getElementById("player-control-overlay");
if (overlay && this.enabled) {
if (overlay?.classList?.contains("pointer-events-off")) {
this.hideButton();
} else {
this.showButton();
}
}
}
private getTitle(): string {
@@ -131,5 +185,37 @@ export class SkipButtonControlBar {
private getChapterPrefix(): HTMLElement {
return document.querySelector(".ytp-chapter-title-prefix");
}
// Swiping away on mobile
private handleTouchStart(event: TouchEvent): void {
this.swipeStart = event.touches[0].clientX;
}
// Swiping away on mobile
private handleTouchMove(event: TouchEvent): void {
const distanceMoved = this.swipeStart - event.touches[0].clientX;
this.left = Math.min(-distanceMoved, 0);
this.updateLeftStyle();
}
// Swiping away on mobile
private handleTouchEnd(): void {
if (this.left < -this.container.offsetWidth / 2) {
this.disableText();
// Don't let animation play
this.skipIcon.style.display = "none";
setTimeout(() => this.skipIcon.style.removeProperty("display"), 200);
}
this.left = 0;
this.updateLeftStyle();
}
// Swiping away on mobile
private updateLeftStyle() {
this.container.style.left = this.left + "px";
}
}

View File

@@ -31,9 +31,10 @@ interface IsInfoFoundMessage {
export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | IsInfoFoundMessage);
interface IsInfoFoundMessageResponse {
export interface IsInfoFoundMessageResponse {
found: boolean;
sponsorTimes: SponsorTime[];
onMobileYouTube: boolean;
}
interface GetVideoIdResponse {

View File

@@ -2,7 +2,7 @@ import Config from "./config";
import Utils from "./utils";
import { SponsorTime, SponsorHideType, CategoryActionType } from "./types";
import { Message, MessageResponse } from "./messageTypes";
import { Message, MessageResponse, IsInfoFoundMessageResponse } from "./messageTypes";
import { showDonationLink } from "./utils/configUtils";
import { getCategoryActionType } from "./utils/categoryUtils";
const utils = new Utils();
@@ -278,7 +278,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
}, (tabs) => onTabs(tabs, updating));
}
function infoFound(request: { found: boolean, sponsorTimes: SponsorTime[] }) {
function infoFound(request: IsInfoFoundMessageResponse) {
if (chrome.runtime.lastError) {
//This page doesn't have the injected content script, or at least not yet
displayNoVideo();
@@ -289,6 +289,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
if (request != undefined) {
//remove loading text
PageElements.mainControls.style.display = "flex";
if (request.onMobileYouTube) PageElements.mainControls.classList.add("hidden");
PageElements.whitelistButton.classList.remove("hidden");
PageElements.loadingIndicator.style.display = "none";

View File

@@ -183,7 +183,7 @@ export default class Utils {
}
}
setupAutoHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): void {
setupCustomHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): { hide: () => void, show: () => void } {
if (enabled) element.classList.add("autoHiding");
element.classList.add("hidden");
element.classList.add("animationDone");
@@ -191,22 +191,30 @@ export default class Utils {
let mouseEntered = false;
container.addEventListener("mouseenter", () => {
mouseEntered = true;
element.classList.remove("animationDone");
// Wait for next event loop
setTimeout(() => {
if (mouseEntered) element.classList.remove("hidden")
}, 10);
});
container.addEventListener("mouseleave", () => {
mouseEntered = false;
if (element.classList.contains("autoHiding")) {
element.classList.add("hidden");
return {
hide: () => {
mouseEntered = false;
if (element.classList.contains("autoHiding")) {
element.classList.add("hidden");
}
},
show: () => {
mouseEntered = true;
element.classList.remove("animationDone");
// Wait for next event loop
setTimeout(() => {
if (mouseEntered) element.classList.remove("hidden")
}, 10);
}
});
};
}
setupAutoHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): void {
const { hide, show } = this.setupCustomHideAnimation(element, container, enabled, rightSlide);
container.addEventListener("mouseleave", () => hide());
container.addEventListener("mouseenter", () => show());
}
enableAutoHideAnimation(element: Element): void {

20
src/utils/pageUtils.ts Normal file
View File

@@ -0,0 +1,20 @@
export function getControls(): HTMLElement | false {
const controlsSelectors = [
// YouTube
".ytp-right-controls",
// Mobile YouTube
".player-controls-top",
// Invidious/videojs video element's controls element
".vjs-control-bar",
];
for (const controlsSelector of controlsSelectors) {
const controls = document.querySelectorAll(controlsSelector);
if (controls && controls.length > 0) {
return <HTMLElement> controls[controls.length - 1];
}
}
return false;
}