From 5af483376311beedfc70c6548640a5ccdaaadde6 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 30 Nov 2021 02:58:57 -0500 Subject: [PATCH 1/7] change parser to use document if applicable --- src/content.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/content.ts b/src/content.ts index 681ef10c..11ffba65 100644 --- a/src/content.ts +++ b/src/content.ts @@ -86,7 +86,7 @@ let controls: HTMLElement | null = null; const playerButtons: Record = {}; // Direct Links after the config is loaded -utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL))); +utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document))); addHotkeyListener(); //the amount of times the sponsor lookup has retried @@ -137,7 +137,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo //messages from popup script switch(request.message){ case "update": - videoIDChange(getYouTubeVideoID(document.URL)); + videoIDChange(getYouTubeVideoID(document)); break; case "sponsorStart": startOrEndTimingNewSegment() @@ -509,7 +509,7 @@ function inMuteSegment(currentTime: number): boolean { * This makes sure the videoID is still correct and if the sponsorTime is included */ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean { - const currentVideoID = getYouTubeVideoID(document.URL); + const currentVideoID = getYouTubeVideoID(document); if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime && (!sponsorTimes || !sponsorTimes?.some((time) => time.segment === sponsorTime.segment)) && !sponsorTimesSubmitting.some((time) => time.segment === sponsorTime.segment))) { @@ -892,8 +892,23 @@ async function getVideoInfo(): Promise { } } -function getYouTubeVideoID(url: string): string | boolean { - // For YouTube TV support +function getYouTubeVideoID(document: Document): string | boolean { + const url = document.URL; + // skip to URL if matches youtube watch or invidious or matches youtube pattern + if ((!url.includes("youtube.com")) || url.includes("/watch") || url.includes("/embed/") || url.includes("/shorts/") || url.includes("playlist")) return getYouTubeVideoIDFromURL(url); + // skip to document if matches pattern + if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document); + // not sure, try URL then document + return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document); +} + +function getYouTubeVideoIDFromDocument(document: Document): string | boolean { + // get ID from document (channel trailer) + const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href"); + return getYouTubeVideoIDFromURL(videoURL); +} + +function getYouTubeVideoIDFromURL(url: string): string | boolean { if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", ""); //Attempt to parse url @@ -913,7 +928,7 @@ function getYouTubeVideoID(url: string): string | boolean { } else if (!["m.youtube.com", "www.youtube.com", "www.youtube-nocookie.com", "music.youtube.com"].includes(urlObject.host)) { if (!Config.config) { // Call this later, in case this is an Invidious tab - utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoID(url))); + utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoIDFromURL(url))); } return false @@ -931,7 +946,7 @@ function getYouTubeVideoID(url: string): string | boolean { console.error("[SB] Video ID not valid for " + url); return false; } - } + } return false; } From 6930980a4df864754228be02b1008139857d5ca4 Mon Sep 17 00:00:00 2001 From: Michael C Date: Tue, 30 Nov 2021 19:13:08 -0500 Subject: [PATCH 2/7] set isInvidious to bypass UI bugs --- src/content.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/content.ts b/src/content.ts index 11ffba65..019b3c8f 100644 --- a/src/content.ts +++ b/src/content.ts @@ -905,7 +905,12 @@ function getYouTubeVideoID(document: Document): string | boolean { function getYouTubeVideoIDFromDocument(document: Document): string | boolean { // get ID from document (channel trailer) const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href"); - return getYouTubeVideoIDFromURL(videoURL); + if (videoURL) { + onInvidious = true; + return getYouTubeVideoIDFromURL(videoURL); + } else { + return false + } } function getYouTubeVideoIDFromURL(url: string): string | boolean { From aca52abefc23c7c590843180cec3b9cf17f83b40 Mon Sep 17 00:00:00 2001 From: Ajay Date: Tue, 28 Dec 2021 11:58:05 -0500 Subject: [PATCH 3/7] Make skip notice work on channel trailer --- src/utils.ts | 15 ++++++++++----- src/utils/pageUtils.ts | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index bdb441e7..c5299b40 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import Config from "./config"; import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types"; import * as CompileConfig from "../config.json"; +import { findValidElement } from "./utils/pageUtils"; export default class Utils { @@ -436,11 +437,15 @@ export default class Utils { } findReferenceNode(): HTMLElement { - let referenceNode = document.getElementById("player-container-id") - ?? document.getElementById("movie_player") - ?? document.querySelector("#main-panel.ytmusic-player-page") // YouTube music - ?? document.querySelector("#player-container .video-js") // Invidious - ?? document.querySelector(".main-video-section > .video-container"); // Cloudtube + const selectors = [ + "#player-container-id", + "#movie_player", + "#c4-player", // Channel Trailer + "#main-panel.ytmusic-player-page", // YouTube music + "#player-container .video-js", // Invidious + ".main-video-section > .video-container" // Cloudtube + ] + let referenceNode = findValidElement(selectors) if (referenceNode == null) { //for embeds const player = document.getElementById("player"); diff --git a/src/utils/pageUtils.ts b/src/utils/pageUtils.ts index e88f7cc9..d94b36d5 100644 --- a/src/utils/pageUtils.ts +++ b/src/utils/pageUtils.ts @@ -17,4 +17,19 @@ export function getControls(): HTMLElement | false { } return false; +} + +export function isVisible(element: HTMLElement): boolean { + return element.offsetWidth > 0 && element.offsetHeight > 0; +} + +export function findValidElement(selectors: string[]): HTMLElement { + for (const selector of selectors) { + const element = document.querySelector(selector) as HTMLElement; + if (element && isVisible(element)) { + return element; + } + } + + return null; } \ No newline at end of file From 7f374f0f860d898f3ceddc7134c21eb7f35fd156 Mon Sep 17 00:00:00 2001 From: Ajay Date: Tue, 28 Dec 2021 12:17:08 -0500 Subject: [PATCH 4/7] Trigger changes even if videoid doesn't change if video element changes --- src/content.ts | 10 +++++----- src/utils.ts | 4 ++-- src/utils/pageUtils.ts | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/content.ts b/src/content.ts index 019b3c8f..c8dd4ac6 100644 --- a/src/content.ts +++ b/src/content.ts @@ -17,7 +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"; +import { findValidElement, getControls, isVisible } from "./utils/pageUtils"; // Hack to get the CSS loaded on permission-based sites (Invidious) utils.wait(() => Config.config !== null, 5000, 10).then(addCSS); @@ -266,8 +266,8 @@ function resetValues() { } async function videoIDChange(id) { - //if the id has not changed return - if (sponsorVideoID === id) return; + //if the id has not changed return unless the video element has changed + if (sponsorVideoID === id && isVisible(video)) return; //set the global videoID sponsorVideoID = id; @@ -540,7 +540,7 @@ function setupVideoMutationListener() { } function refreshVideoAttachments() { - const newVideo = document.querySelector('video'); + const newVideo = findValidElement(document.querySelectorAll('video')) as HTMLVideoElement; if (newVideo && newVideo !== video) { video = newVideo; @@ -638,7 +638,7 @@ function setupSkipButtonControlBar() { } async function sponsorsLookup(id: string, keepOldSubmissions = true) { - if (!video) refreshVideoAttachments(); + if (!video || !isVisible(video)) refreshVideoAttachments(); //there is still no video here if (!video) { setTimeout(() => sponsorsLookup(id), 100); diff --git a/src/utils.ts b/src/utils.ts index c5299b40..81be9488 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,7 +2,7 @@ import Config from "./config"; import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types"; import * as CompileConfig from "../config.json"; -import { findValidElement } from "./utils/pageUtils"; +import { findValidElementFromSelector } from "./utils/pageUtils"; export default class Utils { @@ -445,7 +445,7 @@ export default class Utils { "#player-container .video-js", // Invidious ".main-video-section > .video-container" // Cloudtube ] - let referenceNode = findValidElement(selectors) + let referenceNode = findValidElementFromSelector(selectors) if (referenceNode == null) { //for embeds const player = document.getElementById("player"); diff --git a/src/utils/pageUtils.ts b/src/utils/pageUtils.ts index d94b36d5..d31ead47 100644 --- a/src/utils/pageUtils.ts +++ b/src/utils/pageUtils.ts @@ -20,12 +20,20 @@ export function getControls(): HTMLElement | false { } export function isVisible(element: HTMLElement): boolean { - return element.offsetWidth > 0 && element.offsetHeight > 0; + return element && element.offsetWidth > 0 && element.offsetHeight > 0; } -export function findValidElement(selectors: string[]): HTMLElement { - for (const selector of selectors) { - const element = document.querySelector(selector) as HTMLElement; +export function findValidElementFromSelector(selectors: string[]): HTMLElement { + return findValidElementFromGenerator(selectors, (selector) => document.querySelector(selector)); +} + +export function findValidElement(elements: HTMLElement[] | NodeListOf): HTMLElement { + return findValidElementFromGenerator(elements); +} + +function findValidElementFromGenerator(objects: T[] | NodeListOf, generator?: (obj: T) => HTMLElement): HTMLElement { + for (const obj of objects) { + const element = generator ? generator(obj as T) : obj as HTMLElement; if (element && isVisible(element)) { return element; } From 9b152a552505b561f4a68ad33e431c0b2c2d1ab8 Mon Sep 17 00:00:00 2001 From: Ajay Date: Tue, 28 Dec 2021 12:52:48 -0500 Subject: [PATCH 5/7] Fix preview bar not being recreated --- src/content.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/content.ts b/src/content.ts index c8dd4ac6..881765e7 100644 --- a/src/content.ts +++ b/src/content.ts @@ -87,6 +87,7 @@ const playerButtons: Record Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document))); +addPageListeners(); addHotkeyListener(); //the amount of times the sponsor lookup has retried @@ -377,7 +378,7 @@ function createPreviewBar(): void { ]; for (const selector of progressElementSelectors) { - const el = document.querySelector(selector); + const el = findValidElement(document.querySelectorAll(selector)); if (el) { previewBar = new PreviewBar(el, onMobileYouTube, onInvidious); @@ -550,6 +551,14 @@ function refreshVideoAttachments() { setupVideoListeners(); setupSkipButtonControlBar(); } + + // Create a new bar in the new video element + if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) { + previewBar.remove(); + previewBar = null; + + createPreviewBar(); + } } } @@ -1843,6 +1852,16 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string { return sponsorTimesMessage; } +function addPageListeners(): void { + const refreshListners = () => { + if (!isVisible(video)) { + refreshVideoAttachments(); + } + }; + + document.addEventListener("yt-navigate-finish", refreshListners); +} + function addHotkeyListener(): void { document.addEventListener("keydown", hotkeyListener); } From 7a7b21cd873479e4e9f1129287269b3e5a23f9c5 Mon Sep 17 00:00:00 2001 From: Michael C Date: Fri, 31 Dec 2021 18:17:15 -0500 Subject: [PATCH 6/7] add path for embedded videos and playlists --- src/content.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content.ts b/src/content.ts index 881765e7..0730d59a 100644 --- a/src/content.ts +++ b/src/content.ts @@ -904,15 +904,15 @@ async function getVideoInfo(): Promise { function getYouTubeVideoID(document: Document): string | boolean { const url = document.URL; // skip to URL if matches youtube watch or invidious or matches youtube pattern - if ((!url.includes("youtube.com")) || url.includes("/watch") || url.includes("/embed/") || 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 - if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document); + if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/") || url.includes("/embed/")) return getYouTubeVideoIDFromDocument(document); // not sure, try URL then document return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document); } function getYouTubeVideoIDFromDocument(document: Document): string | boolean { - // get ID from document (channel trailer) + // get ID from document (channel trailer / embedded playlist) const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href"); if (videoURL) { onInvidious = true; From 44bc8741ef1e85fd38da5f56a77512ed4243c660 Mon Sep 17 00:00:00 2001 From: Michael C Date: Fri, 31 Dec 2021 22:56:46 -0500 Subject: [PATCH 7/7] fix UI issues with embeds - add loadStart trigger to create & update preview and buttons - show info button on /embed/ but not /channel/ --- src/content.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/content.ts b/src/content.ts index 0730d59a..b72979ff 100644 --- a/src/content.ts +++ b/src/content.ts @@ -399,6 +399,16 @@ function durationChangeListener(): void { updatePreviewBar(); } +/** + * Triggered once the video is ready. + * This is mainly to attach to embedded players who don't have a video element visible. + */ +function videoOnReadyListener(): void { + createPreviewBar(); + updatePreviewBar(); + createButtons(); +} + function cancelSponsorSchedule(): void { if (currentSkipSchedule !== null) { clearTimeout(currentSkipSchedule); @@ -564,6 +574,7 @@ function refreshVideoAttachments() { function setupVideoListeners() { //wait until it is loaded + video.addEventListener('loadstart', videoOnReadyListener) video.addEventListener('durationchange', durationChangeListener); if (!Config.config.disableSkipping) { @@ -905,17 +916,19 @@ function getYouTubeVideoID(document: Document): string | boolean { const url = document.URL; // 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); + // skip to document and don't hide if on /embed/ + if (url.includes("/embed/")) return getYouTubeVideoIDFromDocument(document, false); // skip to document if matches pattern - if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/") || url.includes("/embed/")) return getYouTubeVideoIDFromDocument(document); + if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document); // not sure, try URL then document return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document); } -function getYouTubeVideoIDFromDocument(document: Document): string | boolean { +function getYouTubeVideoIDFromDocument(document: Document, hideIcon = true): string | boolean { // get ID from document (channel trailer / embedded playlist) const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href"); if (videoURL) { - onInvidious = true; + onInvidious = hideIcon; return getYouTubeVideoIDFromURL(videoURL); } else { return false