mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-08 20:47:11 +03:00
Fix hover previews on load and channel trailers
This commit is contained in:
@@ -60,6 +60,7 @@ let sponsorSkipped: boolean[] = [];
|
|||||||
let video: HTMLVideoElement;
|
let video: HTMLVideoElement;
|
||||||
let videoMuted = false; // Has it been attempted to be muted
|
let videoMuted = false; // Has it been attempted to be muted
|
||||||
let videoMutationObserver: MutationObserver = null;
|
let videoMutationObserver: MutationObserver = null;
|
||||||
|
let waitingForNewVideo = false;
|
||||||
// List of videos that have had event listeners added to them
|
// List of videos that have had event listeners added to them
|
||||||
const videosWithEventListeners: HTMLVideoElement[] = [];
|
const videosWithEventListeners: HTMLVideoElement[] = [];
|
||||||
const controlsWithEventListeners: HTMLElement[] = []
|
const controlsWithEventListeners: HTMLElement[] = []
|
||||||
@@ -97,10 +98,8 @@ const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImage
|
|||||||
// Direct Links after the config is loaded
|
// Direct Links after the config is loaded
|
||||||
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document)));
|
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document)));
|
||||||
// wait for hover preview to appear, and refresh attachments if ever found
|
// wait for hover preview to appear, and refresh attachments if ever found
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
utils.waitForElement(".ytp-inline-preview-ui").then(() => refreshVideoAttachments())
|
utils.waitForElement(".ytp-inline-preview-ui").then(() => refreshVideoAttachments())
|
||||||
utils.waitForElement("[data-sessionlink='feature=player-title']").then(() => videoIDChange(getYouTubeVideoID(document)))
|
utils.waitForElement("[data-sessionlink='feature=player-title']").then(() => videoIDChange(getYouTubeVideoID(document)))
|
||||||
});
|
|
||||||
addPageListeners();
|
addPageListeners();
|
||||||
addHotkeyListener();
|
addHotkeyListener();
|
||||||
|
|
||||||
@@ -316,7 +315,7 @@ function resetValues() {
|
|||||||
categoryPill?.setVisibility(false);
|
categoryPill?.setVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function videoIDChange(id) {
|
async function videoIDChange(id): Promise<void> {
|
||||||
//if the id has not changed return unless the video element has changed
|
//if the id has not changed return unless the video element has changed
|
||||||
if (sponsorVideoID === id && (isVisible(video) || !video)) return;
|
if (sponsorVideoID === id && (isVisible(video) || !video)) return;
|
||||||
|
|
||||||
@@ -500,6 +499,13 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure we are on the correct video
|
||||||
|
const newVideoID = getYouTubeVideoID(document);
|
||||||
|
if (newVideoID !== sponsorVideoID) {
|
||||||
|
videoIDChange(newVideoID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logDebug(`Considering to start skipping: ${!video}, ${video?.paused}`);
|
logDebug(`Considering to start skipping: ${!video}, ${video?.paused}`);
|
||||||
|
|
||||||
if (!video || video.paused) return;
|
if (!video || video.paused) return;
|
||||||
@@ -681,11 +687,14 @@ function setupVideoMutationListener() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshVideoAttachments() {
|
async function refreshVideoAttachments(): Promise<void> {
|
||||||
const newVideo = findValidElement(document.querySelectorAll('video')) as HTMLVideoElement;
|
if (waitingForNewVideo) return;
|
||||||
if (newVideo && newVideo !== video) {
|
|
||||||
video = newVideo;
|
|
||||||
|
|
||||||
|
waitingForNewVideo = true;
|
||||||
|
const newVideo = await utils.waitForElement("video", true) as HTMLVideoElement;
|
||||||
|
waitingForNewVideo = false;
|
||||||
|
|
||||||
|
video = newVideo;
|
||||||
if (!videosWithEventListeners.includes(video)) {
|
if (!videosWithEventListeners.includes(video)) {
|
||||||
videosWithEventListeners.push(video);
|
videosWithEventListeners.push(video);
|
||||||
|
|
||||||
@@ -694,14 +703,14 @@ function refreshVideoAttachments() {
|
|||||||
setupCategoryPill();
|
setupCategoryPill();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new bar in the new video element
|
|
||||||
if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) {
|
if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) {
|
||||||
previewBar.remove();
|
previewBar.remove();
|
||||||
previewBar = null;
|
previewBar = null;
|
||||||
|
|
||||||
createPreviewBar();
|
createPreviewBar();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
videoIDChange(getYouTubeVideoID(document));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupVideoListeners() {
|
function setupVideoListeners() {
|
||||||
@@ -1057,23 +1066,24 @@ function getYouTubeVideoID(document: Document, url?: string): string | boolean {
|
|||||||
// 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.
|
||||||
if (url.includes("youtube.com/clip/")) return false;
|
if (url.includes("youtube.com/clip/")) return false;
|
||||||
// skip to document and don't hide if on /embed/
|
// skip to document and don't hide if on /embed/
|
||||||
if (url.includes("/embed/") && url.includes("youtube.com")) return getYouTubeVideoIDFromDocument(document, false);
|
if (url.includes("/embed/") && url.includes("youtube.com")) return getYouTubeVideoIDFromDocument(false);
|
||||||
// skip to URL if matches youtube watch or invidious or matches youtube pattern
|
// skip to URL if matches youtube watch or invidious or matches youtube pattern
|
||||||
if ((!url.includes("youtube.com")) || url.includes("/watch") || url.includes("/shorts/") || url.includes("playlist")) return getYouTubeVideoIDFromURL(url);
|
if ((!url.includes("youtube.com")) || url.includes("/watch") || url.includes("/shorts/") || url.includes("playlist")) return getYouTubeVideoIDFromURL(url);
|
||||||
// skip to document if matches pattern
|
// skip to document if matches pattern
|
||||||
if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document);
|
if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument();
|
||||||
// not sure, try URL then document
|
// not sure, try URL then document
|
||||||
return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document, false);
|
return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getYouTubeVideoIDFromDocument(document: Document, hideIcon = true): string | boolean {
|
function getYouTubeVideoIDFromDocument(hideIcon = true): string | boolean {
|
||||||
// get ID from document (channel trailer / embedded playlist)
|
// get ID from document (channel trailer / embedded playlist)
|
||||||
const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href");
|
const element = video?.parentElement?.parentElement?.querySelector("[data-sessionlink='feature=player-title']");
|
||||||
|
const videoURL = element?.getAttribute("href");
|
||||||
if (videoURL) {
|
if (videoURL) {
|
||||||
onInvidious = hideIcon;
|
onInvidious = hideIcon;
|
||||||
return getYouTubeVideoIDFromURL(videoURL);
|
return getYouTubeVideoIDFromURL(videoURL);
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
src/utils.ts
39
src/utils.ts
@@ -2,7 +2,7 @@ import Config, { VideoDownvotes } from "./config";
|
|||||||
import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration, HashedValue, VideoID, SponsorHideType } from "./types";
|
import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration, HashedValue, VideoID, SponsorHideType } from "./types";
|
||||||
|
|
||||||
import * as CompileConfig from "../config.json";
|
import * as CompileConfig from "../config.json";
|
||||||
import { findValidElementFromSelector } from "./utils/pageUtils";
|
import { findValidElement, findValidElementFromSelector } from "./utils/pageUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
@@ -22,8 +22,9 @@ export default class Utils {
|
|||||||
];
|
];
|
||||||
|
|
||||||
/* Used for waitForElement */
|
/* Used for waitForElement */
|
||||||
|
creatingWaitingMutationObserver = false;
|
||||||
waitingMutationObserver: MutationObserver = null;
|
waitingMutationObserver: MutationObserver = null;
|
||||||
waitingElements: { selector: string, callback: (element: Element) => void }[] = [];
|
waitingElements: { selector: string, visibleCheck: boolean, callback: (element: Element) => void }[] = [];
|
||||||
|
|
||||||
constructor(backgroundScriptContainer: BackgroundScriptContainer = null) {
|
constructor(backgroundScriptContainer: BackgroundScriptContainer = null) {
|
||||||
this.backgroundScriptContainer = backgroundScriptContainer;
|
this.backgroundScriptContainer = backgroundScriptContainer;
|
||||||
@@ -34,18 +35,40 @@ export default class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Uses a mutation observer to wait asynchronously */
|
/* Uses a mutation observer to wait asynchronously */
|
||||||
async waitForElement(selector: string): Promise<Element> {
|
async waitForElement(selector: string, visibleCheck = false): Promise<Element> {
|
||||||
return await new Promise((resolve) => {
|
return await new Promise((resolve) => {
|
||||||
|
const initialElement = this.getElement(selector, visibleCheck);
|
||||||
|
if (initialElement) {
|
||||||
|
resolve(initialElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.waitingElements.push({
|
this.waitingElements.push({
|
||||||
selector,
|
selector,
|
||||||
|
visibleCheck,
|
||||||
callback: resolve
|
callback: resolve
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!this.creatingWaitingMutationObserver) {
|
||||||
|
this.creatingWaitingMutationObserver = true;
|
||||||
|
|
||||||
|
if (document.body) {
|
||||||
|
this.setupWaitingMutationListener();
|
||||||
|
} else {
|
||||||
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
this.setupWaitingMutationListener();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupWaitingMutationListener(): void {
|
||||||
if (!this.waitingMutationObserver) {
|
if (!this.waitingMutationObserver) {
|
||||||
this.waitingMutationObserver = new MutationObserver(() => {
|
this.waitingMutationObserver = new MutationObserver(() => {
|
||||||
const foundSelectors = [];
|
const foundSelectors = [];
|
||||||
for (const { selector, callback } of this.waitingElements) {
|
for (const { selector, visibleCheck, callback } of this.waitingElements) {
|
||||||
const element = document.querySelector(selector);
|
const element = this.getElement(selector, visibleCheck);
|
||||||
if (element) {
|
if (element) {
|
||||||
callback(element);
|
callback(element);
|
||||||
foundSelectors.push(selector);
|
foundSelectors.push(selector);
|
||||||
@@ -57,6 +80,7 @@ export default class Utils {
|
|||||||
if (this.waitingElements.length === 0) {
|
if (this.waitingElements.length === 0) {
|
||||||
this.waitingMutationObserver.disconnect();
|
this.waitingMutationObserver.disconnect();
|
||||||
this.waitingMutationObserver = null;
|
this.waitingMutationObserver = null;
|
||||||
|
this.creatingWaitingMutationObserver = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,7 +89,10 @@ export default class Utils {
|
|||||||
subtree: true
|
subtree: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
private getElement(selector: string, visibleCheck: boolean) {
|
||||||
|
return visibleCheck ? findValidElement(document.querySelectorAll(selector)) : document.querySelector(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
containsPermission(permissions: chrome.permissions.Permissions): Promise<boolean> {
|
containsPermission(permissions: chrome.permissions.Permissions): Promise<boolean> {
|
||||||
|
|||||||
Reference in New Issue
Block a user