aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAjay Ramachandran <[email protected]>2022-09-02 15:20:40 -0400
committerGitHub <[email protected]>2022-09-02 15:20:40 -0400
commit4f0f8655f4fccb71034490d44b3d409e5f01e224 (patch)
tree44020c8209972eaaf08b6030a185197e8ad5b312
parent668f6856d1c7a4ffcb83fa1ae1cdd888996d19af (diff)
parent29c6151fe3cde9fa5d4ad26f36613f12eba660a8 (diff)
downloadSponsorBlock-5.0.tar.gz
SponsorBlock-5.0.zip
Merge pull request #1425 from mchangrh/contentScriptRebase5.0
rebase document script out of videoInfo
-rw-r--r--manifest/manifest.json3
-rw-r--r--src/content.ts94
-rw-r--r--src/document.ts80
-rw-r--r--src/types.ts8
-rw-r--r--webpack/webpack.common.js7
5 files changed, 167 insertions, 25 deletions
diff --git a/manifest/manifest.json b/manifest/manifest.json
index a1b15423..5276e5eb 100644
--- a/manifest/manifest.json
+++ b/manifest/manifest.json
@@ -69,7 +69,8 @@
"icons/PlayerInfoIconSponsorBlocker.svg",
"icons/PlayerDeleteIconSponsorBlocker.svg",
"popup.html",
- "content.css"
+ "content.css",
+ "js/document.js"
],
"permissions": [
"storage",
diff --git a/src/content.ts b/src/content.ts
index cb2d3964..0aa6e64d 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -18,6 +18,7 @@ import {
ToggleSkippable,
VideoID,
VideoInfo,
+ PageType
} from "./types";
import Utils from "./utils";
import PreviewBar, { PreviewBarSegment } from "./js-components/previewBar";
@@ -55,6 +56,10 @@ let activeSkipKeybindElement: ToggleSkippable = null;
// JSON video info
let videoInfo: VideoInfo = null;
+// Page Type - browse/watch etc...
+let pageType: PageType;
+// if video is live or premiere
+let isLivePremiere: boolean
// The channel this video is about
let channelIDInfo: ChannelIDInfo;
// Locked Categories in this tab, like: ["sponsor","intro","outro"]
@@ -90,7 +95,7 @@ let onInvidious: boolean;
let onMobileYouTube: boolean;
//the video id of the last preview bar update
-let lastPreviewBarUpdate;
+let lastPreviewBarUpdate: VideoID;
// Is the video currently being switched
let switchingVideos = null;
@@ -334,12 +339,14 @@ function resetValues() {
sponsorSkipped = [];
videoInfo = null;
+ pageType = null;
channelWhitelisted = false;
channelIDInfo = {
status: ChannelIDStatus.Fetching,
id: null
};
lockedCategories = [];
+ isLivePremiere = false;
//empty the preview bar
if (previewBar !== null) {
@@ -1157,6 +1164,8 @@ function startSkipScheduleCheckingForStartSponsors() {
function getYouTubeVideoID(document: Document, url?: string): string | boolean {
url ||= document.URL;
+ // pageType shortcut
+ if (pageType === PageType.Channel) return getYouTubeVideoIDFromDocument()
// clips should never skip, going from clip to full video has no indications.
if (url.includes("youtube.com/clip/")) return false;
// skip to document and don't hide if on /embed/
@@ -1164,17 +1173,19 @@ function getYouTubeVideoID(document: Document, url?: string): string | boolean {
// 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 if matches pattern
- if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument();
+ if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(true, PageType.Channel);
// not sure, try URL then document
return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(false);
}
-function getYouTubeVideoIDFromDocument(hideIcon = true): string | boolean {
+function getYouTubeVideoIDFromDocument(hideIcon = true, pageHint = PageType.Watch): string | boolean {
// get ID from document (channel trailer / embedded playlist)
const element = video?.parentElement?.parentElement?.querySelector("a.ytp-title-link[data-sessionlink='feature=player-title']");
const videoURL = element?.getAttribute("href");
if (videoURL) {
onInvidious = hideIcon;
+ // if href found, hint was correct
+ pageType = pageHint;
return getYouTubeVideoIDFromURL(videoURL);
} else {
return false;
@@ -1294,25 +1305,29 @@ function updatePreviewBar(): void {
async function whitelistCheck() {
const whitelistedChannels = Config.config.whitelistedChannels;
- const getChannelID = () =>
- (document.querySelector("a.ytd-video-owner-renderer") // YouTube
- ?? document.querySelector("a.ytp-title-channel-logo") // YouTube Embed
- ?? document.querySelector(".channel-profile #channel-name")?.parentElement.parentElement // Invidious
- ?? document.querySelector("a.slim-owner-icon-and-title")) // Mobile YouTube
- ?.getAttribute("href")?.match(/\/(?:channel|c|user)\/(UC[a-zA-Z0-9_-]{22}|[a-zA-Z0-9_-]+)/)?.[1];
-
try {
- await utils.wait(() => !!getChannelID(), 6000, 20);
+ await utils.wait(() => channelIDInfo.status === ChannelIDStatus.Found, 6000, 20);
- channelIDInfo = {
- status: ChannelIDStatus.Found,
- id: getChannelID().match(/^\/?([^\s/]+)/)[0]
- };
+ // If found, continue on, it was set by the listener
} catch (e) {
- channelIDInfo = {
- status: ChannelIDStatus.Failed,
- id: null
- };
+ // Try fallback
+ const channelIDFallback = (document.querySelector("a.ytd-video-owner-renderer") // YouTube
+ ?? document.querySelector("a.ytp-title-channel-logo") // YouTube Embed
+ ?? document.querySelector(".channel-profile #channel-name")?.parentElement.parentElement // Invidious
+ ?? document.querySelector("a.slim-owner-icon-and-title")) // Mobile YouTube
+ ?.getAttribute("href")?.match(/\/(?:channel|c|user)\/(UC[a-zA-Z0-9_-]{22}|[a-zA-Z0-9_-]+)/)?.[1];
+
+ if (channelIDFallback) {
+ channelIDInfo = {
+ status: ChannelIDStatus.Found,
+ id: channelIDFallback
+ };
+ } else {
+ channelIDInfo = {
+ status: ChannelIDStatus.Failed,
+ id: null
+ };
+ }
}
//see if this is a whitelisted channel
@@ -1731,7 +1746,7 @@ function updateEditButtonsOnPlayer(): void {
// Don't try to update the buttons if we aren't on a YouTube video page
if (!sponsorVideoID || onMobileYouTube) return;
- const buttonsEnabled = !Config.config.hideVideoPlayerControls && !onInvidious;
+ const buttonsEnabled = !(Config.config.hideVideoPlayerControls || onInvidious);
let creatingSegment = false;
let submitButtonVisible = false;
@@ -2069,7 +2084,7 @@ function submitSponsorTimes() {
//called after all the checks have been made that it's okay to do so
async function sendSubmitMessage() {
// Block if submitting on a running livestream or premiere
- if (isVisible(document.querySelector(".ytp-live-badge"))) {
+ if (isLivePremiere || isVisible(document.querySelector(".ytp-live-badge"))) {
alert(chrome.i18n.getMessage("liveOrPremiere"));
return;
}
@@ -2182,6 +2197,36 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
return sponsorTimesMessage;
}
+function windowListenerHandler(event: MessageEvent): void {
+ const data = event.data;
+ const dataType = data.type;
+ if (data.source !== "sponsorblock") return;
+
+ if (dataType === "navigation") {
+ sponsorVideoID = data.videoID;
+ pageType = data.pageType;
+
+ if (data.channelID) {
+ channelIDInfo = {
+ id: data.channelID,
+ status: ChannelIDStatus.Found
+ };
+ }
+ } else if (dataType === "ad") {
+ if (isAdPlaying != data.playing) {
+ isAdPlaying = data.playing
+ updatePreviewBar();
+ updateVisibilityOfPlayerControlsButton();
+ }
+ } else if (dataType === "data") {
+ if (data.video !== sponsorVideoID) {
+ sponsorVideoID = data.videoID;
+ videoIDChange(sponsorVideoID);
+ }
+ isLivePremiere = data.isLive || data.isPremiere
+ }
+}
+
function updateActiveSegment(currentTime: number): void {
previewBar?.updateChapterText(sponsorTimes, sponsorTimesSubmitting, currentTime);
chrome.runtime.sendMessage({
@@ -2227,7 +2272,14 @@ function addPageListeners(): void {
}
};
+ // inject into document
+ const docScript = document.createElement("script");
+ docScript.src = chrome.runtime.getURL("js/document.js");
+ (document.head || document.documentElement).appendChild(docScript);
+
+ document.addEventListener("yt-navigate-start", resetValues);
document.addEventListener("yt-navigate-finish", refreshListners);
+ window.addEventListener("message", windowListenerHandler);
}
function addHotkeyListener(): void {
diff --git a/src/document.ts b/src/document.ts
new file mode 100644
index 00000000..d537dd0e
--- /dev/null
+++ b/src/document.ts
@@ -0,0 +1,80 @@
+/*
+ Content script are run in an isolated DOM so it is not possible to access some key details that are sanitized when passed cross-dom
+ This script is used to get the details from the page and make them available for the content script by being injected directly into the page
+*/
+
+import { PageType } from "./types";
+
+interface StartMessage {
+ type: "navigation",
+ pageType: PageType
+ videoID: string | null,
+}
+
+interface FinishMessage extends StartMessage {
+ channelID: string,
+ channelTitle: string
+}
+
+interface AdMessage {
+ type: "ad",
+ playing: boolean
+}
+
+interface VideoData {
+ type: "data",
+ videoID: string,
+ isLive: boolean,
+ isPremiere: boolean
+}
+
+type WindowMessage = StartMessage | FinishMessage | AdMessage | VideoData;
+
+// global playerClient - too difficult to type
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+let playerClient: any;
+
+const sendMessage = (message: WindowMessage): void => {
+ window.postMessage({ source: "sponsorblock", ...message }, "/");
+}
+
+function setupPlayerClient(e: CustomEvent): void {
+ if (playerClient) return; // early exit if already defined
+
+ playerClient = e.detail;
+ sendVideoData(); // send playerData after setup
+
+ e.detail.addEventListener('onAdStart', () => sendMessage({ type: "ad", playing: true } as AdMessage));
+ e.detail.addEventListener('onAdFinish', () => sendMessage({ type: "ad", playing: false } as AdMessage));
+}
+
+document.addEventListener("yt-player-updated", setupPlayerClient);
+document.addEventListener("yt-navigate-start", navigationStartSend);
+document.addEventListener("yt-navigate-finish", navigateFinishSend);
+
+function navigationParser(event: CustomEvent): StartMessage {
+ const pageType: PageType = event.detail.pageType;
+ const result: StartMessage = { type: "navigation", pageType, videoID: null };
+ if (pageType === "shorts" || pageType === "watch") {
+ const endpoint = event.detail.endpoint
+ result.videoID = (pageType === "shorts" ? endpoint.reelWatchEndpoint : endpoint.watchEndpoint).videoId;
+ }
+
+ return result;
+}
+
+function navigationStartSend(event: CustomEvent): void {
+ sendMessage(navigationParser(event) as StartMessage);
+}
+
+function navigateFinishSend(event: CustomEvent): void {
+ sendVideoData(); // arrived at new video, send video data
+ const videoDetails = event.detail?.response?.playerResponse?.videoDetails;
+ sendMessage({ channelID: videoDetails.channelId, channelTitle: videoDetails.author, ...navigationParser(event) } as FinishMessage);
+}
+
+function sendVideoData(): void {
+ if (!playerClient) return;
+ const { video_id, isLive, isPremiere } = playerClient.getVideoData();
+ sendMessage({ type: "data", videoID: video_id, isLive, isPremiere } as VideoData);
+} \ No newline at end of file
diff --git a/src/types.ts b/src/types.ts
index 6b8f9969..f90d199d 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -239,6 +239,14 @@ export type Keybind = {
shift?: boolean
}
+export enum PageType {
+ Shorts = "shorts",
+ Watch = "watch",
+ Search = "search",
+ Browse = "browse",
+ Channel = "channel"
+}
+
export interface ButtonListener {
name: string,
listener: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js
index 73c17807..d05dea03 100644
--- a/webpack/webpack.common.js
+++ b/webpack/webpack.common.js
@@ -29,9 +29,10 @@ module.exports = env => ({
popup: path.join(__dirname, srcDir + 'popup.ts'),
background: path.join(__dirname, srcDir + 'background.ts'),
content: path.join(__dirname, srcDir + 'content.ts'),
- options: path.join(__dirname, srcDir + 'options.ts'),
- help: path.join(__dirname, srcDir + 'help.ts'),
- permissions: path.join(__dirname, srcDir + 'permissions.ts'),
+ options: path.join(__dirname, srcDir + 'options.ts'),
+ help: path.join(__dirname, srcDir + 'help.ts'),
+ permissions: path.join(__dirname, srcDir + 'permissions.ts'),
+ document: path.join(__dirname, srcDir + 'document.ts'),
upsell: path.join(__dirname, srcDir + 'upsell.ts')
},
output: {