mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 11:37:02 +03:00
Merge pull request #2187 from tech234a/master
Implement support for YouTube TV (tv.youtube.com)
This commit is contained in:
Submodule maze-utils updated: de2cda1e75...601df1ea1b
Submodule public/_locales updated: 2653a15038...e2794273bd
@@ -31,6 +31,15 @@
|
|||||||
transition: transform .1s cubic-bezier(0,0,0.2,1);
|
transition: transform .1s cubic-bezier(0,0,0.2,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prevent bar from covering highlights on YTTV */
|
||||||
|
#previewbar.sponsorblock-yttv-container {
|
||||||
|
z-index: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
ytu-time-bar.ytu-storyboard {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
/* May 2024 hover preview */
|
/* May 2024 hover preview */
|
||||||
.YtPlayerProgressBarProgressBar #previewbar {
|
.YtPlayerProgressBarProgressBar #previewbar {
|
||||||
transform: none;
|
transform: none;
|
||||||
@@ -67,6 +76,11 @@ div:hover > #previewbar.sbNotInvidious {
|
|||||||
min-width: 1px;
|
min-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.previewbar-yttv {
|
||||||
|
height: 10px;
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.previewbar.requiredSegment {
|
.previewbar.requiredSegment {
|
||||||
transform: scaleY(3);
|
transform: scaleY(3);
|
||||||
}
|
}
|
||||||
@@ -184,6 +198,16 @@ div:hover > .sponsorBlockChapterBar {
|
|||||||
padding-right: 3.6px;
|
padding-right: 3.6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sbButtonYTTV {
|
||||||
|
padding-left: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* YTTV only */
|
||||||
|
.ytu-player-controls > .skipButtonControlBarContainer > div {
|
||||||
|
padding-left: 5px;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.autoHiding {
|
.autoHiding {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ChapterVoteComponent extends React.Component<ChapterVoteProps, ChapterVote
|
|||||||
<>
|
<>
|
||||||
{/* Upvote Button */}
|
{/* Upvote Button */}
|
||||||
<button id={"sponsorTimesDownvoteButtonsContainerUpvoteChapter"}
|
<button id={"sponsorTimesDownvoteButtonsContainerUpvoteChapter"}
|
||||||
className={"playerButton sbPlayerUpvote ytp-button " + (!this.state.show ? "sbhidden" : "")}
|
className={"playerButton sbPlayerUpvote ytp-button " + (!this.state.show ? "sbhidden " : " ") + (document.location.host === "tv.youtube.com" ? "sbButtonYTTV" : "")}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
title={chrome.i18n.getMessage("upvoteButtonInfo")}
|
||||||
onClick={(e) => this.vote(e, 1)}>
|
onClick={(e) => this.vote(e, 1)}>
|
||||||
@@ -55,7 +55,7 @@ class ChapterVoteComponent extends React.Component<ChapterVoteProps, ChapterVote
|
|||||||
|
|
||||||
{/* Downvote Button */}
|
{/* Downvote Button */}
|
||||||
<button id={"sponsorTimesDownvoteButtonsContainerDownvoteChapter"}
|
<button id={"sponsorTimesDownvoteButtonsContainerDownvoteChapter"}
|
||||||
className={"playerButton sbPlayerDownvote ytp-button " + (!this.state.show ? "sbhidden" : "")}
|
className={"playerButton sbPlayerDownvote ytp-button " + (!this.state.show ? "sbhidden " : " ") + (document.location.host === "tv.youtube.com" ? "sbButtonYTTV" : "")}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
title={chrome.i18n.getMessage("reportButtonInfo")}
|
title={chrome.i18n.getMessage("reportButtonInfo")}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import { ChapterVote } from "./render/ChapterVote";
|
|||||||
import { openWarningDialog } from "./utils/warnings";
|
import { openWarningDialog } from "./utils/warnings";
|
||||||
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||||
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
||||||
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube, getLastNonInlineVideoID, triggerVideoIDChange, triggerVideoElementChange, getIsInline, getCurrentTime, setCurrentTime, getVideoDuration, verifyCurrentTime, waitForVideo } from "../maze-utils/src/video";
|
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube, isOnYTTV, getLastNonInlineVideoID, triggerVideoIDChange, triggerVideoElementChange, getIsInline, getCurrentTime, setCurrentTime, getVideoDuration, verifyCurrentTime, waitForVideo } from "../maze-utils/src/video";
|
||||||
import { Keybind, StorageChangesObject, isSafari, keybindEquals, keybindToString } from "../maze-utils/src/config";
|
import { Keybind, StorageChangesObject, isSafari, keybindEquals, keybindToString } from "../maze-utils/src/config";
|
||||||
import { findValidElement } from "../maze-utils/src/dom"
|
import { findValidElement } from "../maze-utils/src/dom"
|
||||||
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
@@ -240,7 +240,8 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
break;
|
break;
|
||||||
case "getChannelID":
|
case "getChannelID":
|
||||||
sendResponse({
|
sendResponse({
|
||||||
channelID: getChannelIDInfo().id
|
channelID: getChannelIDInfo().id,
|
||||||
|
isYTTV: (document.location.host === "tv.youtube.com")
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -553,6 +554,10 @@ function getPreviewBarAttachElement(): HTMLElement | null {
|
|||||||
}, {
|
}, {
|
||||||
// For Vorapis v3
|
// For Vorapis v3
|
||||||
selector: ".ytp-progress-bar-container > .html5-progress-bar > .ytp-progress-list"
|
selector: ".ytp-progress-bar-container > .html5-progress-bar > .ytp-progress-list"
|
||||||
|
}, {
|
||||||
|
// For YTTV
|
||||||
|
selector: ".yssi-slider > div.ytu-ss-timeline-container",
|
||||||
|
isVisibleCheck: false
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -578,7 +583,7 @@ function createPreviewBar(): void {
|
|||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const chapterVote = new ChapterVote(voteAsync);
|
const chapterVote = new ChapterVote(voteAsync);
|
||||||
previewBar = new PreviewBar(el, isOnMobileYouTube(), isOnInvidious(), chapterVote, () => importExistingChapters(true));
|
previewBar = new PreviewBar(el, isOnMobileYouTube(), isOnInvidious(), isOnYTTV(), chapterVote, () => importExistingChapters(true));
|
||||||
|
|
||||||
updatePreviewBar();
|
updatePreviewBar();
|
||||||
}
|
}
|
||||||
@@ -1147,7 +1152,8 @@ function setupSkipButtonControlBar() {
|
|||||||
forceAutoSkip: true
|
forceAutoSkip: true
|
||||||
}),
|
}),
|
||||||
selectSegment,
|
selectSegment,
|
||||||
onMobileYouTube: isOnMobileYouTube()
|
onMobileYouTube: isOnMobileYouTube(),
|
||||||
|
onYTTV: isOnYTTV(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1850,6 +1856,9 @@ function createButton(baseID: string, title: string, callback: () => void, image
|
|||||||
newButton.id = baseID + "Button";
|
newButton.id = baseID + "Button";
|
||||||
newButton.classList.add("playerButton");
|
newButton.classList.add("playerButton");
|
||||||
newButton.classList.add("ytp-button");
|
newButton.classList.add("ytp-button");
|
||||||
|
if (isOnYTTV()) {
|
||||||
|
newButton.setAttribute("style", "width: 40px; height: 40px");
|
||||||
|
}
|
||||||
newButton.setAttribute("title", chrome.i18n.getMessage(title));
|
newButton.setAttribute("title", chrome.i18n.getMessage(title));
|
||||||
newButton.addEventListener("click", () => {
|
newButton.addEventListener("click", () => {
|
||||||
callback();
|
callback();
|
||||||
@@ -1924,7 +1933,7 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<void> {
|
|||||||
updateEditButtonsOnPlayer();
|
updateEditButtonsOnPlayer();
|
||||||
|
|
||||||
// Don't show the info button on embeds
|
// Don't show the info button on embeds
|
||||||
if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || isOnInvidious()
|
if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || isOnInvidious() || isOnYTTV()
|
||||||
|| document.getElementById("sponsorBlockPopupContainer") != null) {
|
|| document.getElementById("sponsorBlockPopupContainer") != null) {
|
||||||
playerButtons.info.button.style.display = "none";
|
playerButtons.info.button.style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
@@ -1991,6 +2000,11 @@ function getRealCurrentTime(): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startOrEndTimingNewSegment() {
|
function startOrEndTimingNewSegment() {
|
||||||
|
if (isOnYTTV() && getIsLivePremiere()) {
|
||||||
|
alert(chrome.i18n.getMessage("yttvLiveContentWarning"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
verifyCurrentTime();
|
verifyCurrentTime();
|
||||||
const roundedTime = Math.round((getRealCurrentTime() + Number.EPSILON) * 1000) / 1000;
|
const roundedTime = Math.round((getRealCurrentTime() + Number.EPSILON) * 1000) / 1000;
|
||||||
if (!isSegmentCreationInProgress()) {
|
if (!isSegmentCreationInProgress()) {
|
||||||
@@ -2694,6 +2708,7 @@ function showTimeWithoutSkips(skippedDuration: number): void {
|
|||||||
// YouTube player time display
|
// YouTube player time display
|
||||||
const selector =
|
const selector =
|
||||||
isOnInvidious() ? ".vjs-duration" :
|
isOnInvidious() ? ".vjs-duration" :
|
||||||
|
isOnYTTV() ? ".ypl-full-controls .ypmcs-control .time-info-bar" :
|
||||||
isOnMobileYouTube() ? ".ytwPlayerTimeDisplayContent" :
|
isOnMobileYouTube() ? ".ytwPlayerTimeDisplayContent" :
|
||||||
".ytp-time-display.notranslate .ytp-time-wrapper";
|
".ytp-time-display.notranslate .ytp-time-wrapper";
|
||||||
const display = document.querySelector(selector);
|
const display = document.querySelector(selector);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { findValidElement } from "../../maze-utils/src/dom";
|
|||||||
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
||||||
import { hasAutogeneratedChapters, isVisible } from "../utils/pageUtils";
|
import { hasAutogeneratedChapters, isVisible } from "../utils/pageUtils";
|
||||||
import { isVorapisInstalled } from "../utils/compatibility";
|
import { isVorapisInstalled } from "../utils/compatibility";
|
||||||
|
import { isOnYTTV } from "../../maze-utils/src/video";
|
||||||
|
|
||||||
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
|
const TOOLTIP_VISIBLE_CLASS = 'sponsorCategoryTooltipVisible';
|
||||||
const MIN_CHAPTER_SIZE = 0.003;
|
const MIN_CHAPTER_SIZE = 0.003;
|
||||||
@@ -41,6 +42,12 @@ class PreviewBar {
|
|||||||
categoryTooltip?: HTMLDivElement;
|
categoryTooltip?: HTMLDivElement;
|
||||||
categoryTooltipContainer?: HTMLElement;
|
categoryTooltipContainer?: HTMLElement;
|
||||||
chapterTooltip?: HTMLDivElement;
|
chapterTooltip?: HTMLDivElement;
|
||||||
|
|
||||||
|
// ScrubTooltips for YTTV only
|
||||||
|
categoryScrubTooltip?: HTMLDivElement;
|
||||||
|
categoryScrubTooltipContainer?: HTMLElement;
|
||||||
|
chapterScrubTooltip?: HTMLDivElement;
|
||||||
|
|
||||||
lastSmallestSegment: Record<string, {
|
lastSmallestSegment: Record<string, {
|
||||||
index: number;
|
index: number;
|
||||||
segment: PreviewBarSegment;
|
segment: PreviewBarSegment;
|
||||||
@@ -49,6 +56,7 @@ class PreviewBar {
|
|||||||
parent: HTMLElement;
|
parent: HTMLElement;
|
||||||
onMobileYouTube: boolean;
|
onMobileYouTube: boolean;
|
||||||
onInvidious: boolean;
|
onInvidious: boolean;
|
||||||
|
onYTTV: boolean;
|
||||||
progressBar: HTMLElement;
|
progressBar: HTMLElement;
|
||||||
|
|
||||||
segments: PreviewBarSegment[] = [];
|
segments: PreviewBarSegment[] = [];
|
||||||
@@ -70,14 +78,19 @@ class PreviewBar {
|
|||||||
unfilteredChapterGroups: ChapterGroup[];
|
unfilteredChapterGroups: ChapterGroup[];
|
||||||
chapterGroups: ChapterGroup[];
|
chapterGroups: ChapterGroup[];
|
||||||
|
|
||||||
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, chapterVote: ChapterVote, updateExistingChapters: () => void, test=false) {
|
constructor(parent: HTMLElement, onMobileYouTube: boolean, onInvidious: boolean, onYTTV: boolean, chapterVote: ChapterVote, updateExistingChapters: () => void, test=false) {
|
||||||
if (test) return;
|
if (test) return;
|
||||||
this.container = document.createElement('ul');
|
this.container = document.createElement('ul');
|
||||||
this.container.id = 'previewbar';
|
this.container.id = 'previewbar';
|
||||||
|
|
||||||
|
if (onYTTV) {
|
||||||
|
this.container.classList.add("sponsorblock-yttv-container");
|
||||||
|
}
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.onMobileYouTube = onMobileYouTube;
|
this.onMobileYouTube = onMobileYouTube;
|
||||||
this.onInvidious = onInvidious;
|
this.onInvidious = onInvidious;
|
||||||
|
this.onYTTV = onYTTV;
|
||||||
this.chapterVote = chapterVote;
|
this.chapterVote = chapterVote;
|
||||||
this.updateExistingChapters = updateExistingChapters;
|
this.updateExistingChapters = updateExistingChapters;
|
||||||
|
|
||||||
@@ -97,26 +110,49 @@ class PreviewBar {
|
|||||||
|
|
||||||
// Create label placeholder
|
// Create label placeholder
|
||||||
this.categoryTooltip = document.createElement("div");
|
this.categoryTooltip = document.createElement("div");
|
||||||
this.categoryTooltip.className = "ytp-tooltip-title sponsorCategoryTooltip";
|
if (isOnYTTV()) {
|
||||||
|
this.categoryTooltip.className = "sponsorCategoryTooltip";
|
||||||
|
} else {
|
||||||
|
this.categoryTooltip.className = "ytp-tooltip-title sponsorCategoryTooltip";
|
||||||
|
}
|
||||||
this.chapterTooltip = document.createElement("div");
|
this.chapterTooltip = document.createElement("div");
|
||||||
this.chapterTooltip.className = "ytp-tooltip-title sponsorCategoryTooltip";
|
if (isOnYTTV()) {
|
||||||
|
this.chapterTooltip.className = "sponsorCategoryTooltip";
|
||||||
|
} else {
|
||||||
|
this.chapterTooltip.className = "ytp-tooltip-title sponsorCategoryTooltip";
|
||||||
|
}
|
||||||
|
|
||||||
// global chaper tooltip or duration tooltip
|
if (isOnYTTV()) {
|
||||||
// YT, Vorapis, unknown
|
this.categoryScrubTooltip = document.createElement("div");
|
||||||
const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper, .ytp-progress-tooltip-text-container") ?? document.querySelector("#progress-bar-container.ytk-player > #hover-time-info");
|
this.categoryScrubTooltip.className = "sponsorCategoryTooltip";
|
||||||
const originalTooltip = tooltipTextWrapper.querySelector(".ytp-tooltip-title:not(.sponsorCategoryTooltip), .ytp-progress-tooltip-text:not(.sponsorCategoryTooltip)") as HTMLElement;
|
this.chapterScrubTooltip = document.createElement("div");
|
||||||
|
this.chapterScrubTooltip.className = "sponsorCategoryTooltip";
|
||||||
|
}
|
||||||
|
|
||||||
|
// global chapter tooltip or duration tooltip
|
||||||
|
// YT, Vorapis, unknown, YTTV
|
||||||
|
const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper, .ytp-progress-tooltip-text-container, .yssi-slider .ys-seek-details .time-info-bar") ?? document.querySelector("#progress-bar-container.ytk-player > #hover-time-info");
|
||||||
|
const originalTooltip = tooltipTextWrapper.querySelector(".ytp-tooltip-title:not(.sponsorCategoryTooltip), .ytp-progress-tooltip-text:not(.sponsorCategoryTooltip), .current-time:not(.sponsorCategoryTooltip)") as HTMLElement;
|
||||||
if (!tooltipTextWrapper || !tooltipTextWrapper.parentElement) return;
|
if (!tooltipTextWrapper || !tooltipTextWrapper.parentElement) return;
|
||||||
|
|
||||||
// Grab the tooltip from the text wrapper as the tooltip doesn't have its classes on init
|
// Grab the tooltip from the text wrapper as the tooltip doesn't have its classes on init
|
||||||
this.categoryTooltipContainer = tooltipTextWrapper.parentElement;
|
this.categoryTooltipContainer = tooltipTextWrapper.parentElement;
|
||||||
// YT, Vorapis
|
// YT, Vorapis, YTTV
|
||||||
const titleTooltip = tooltipTextWrapper.querySelector(".ytp-tooltip-title, .ytp-progress-tooltip-text") as HTMLElement;
|
const titleTooltip = tooltipTextWrapper.querySelector(".ytp-tooltip-title, .ytp-progress-tooltip-text, .current-time") as HTMLElement;
|
||||||
if (!this.categoryTooltipContainer || !titleTooltip) return;
|
if (!this.categoryTooltipContainer || !titleTooltip) return;
|
||||||
|
|
||||||
tooltipTextWrapper.insertBefore(this.categoryTooltip, titleTooltip.nextSibling);
|
tooltipTextWrapper.insertBefore(this.categoryTooltip, titleTooltip.nextSibling);
|
||||||
tooltipTextWrapper.insertBefore(this.chapterTooltip, titleTooltip.nextSibling);
|
tooltipTextWrapper.insertBefore(this.chapterTooltip, titleTooltip.nextSibling);
|
||||||
|
|
||||||
const seekBar = document.querySelector(".ytp-progress-bar-container");
|
if (isOnYTTV()) {
|
||||||
|
const scrubTooltipTextWrapper = document.querySelector(".yssi-slider .ysl-filmstrip-lens .time-info-bar")
|
||||||
|
if (!this.categoryTooltipContainer) return;
|
||||||
|
|
||||||
|
scrubTooltipTextWrapper.appendChild(this.categoryScrubTooltip);
|
||||||
|
scrubTooltipTextWrapper.appendChild(this.chapterScrubTooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seekBar = (document.querySelector(".ytp-progress-bar-container, .ypcs-scrub-slider-slot.ytu-player-controls"));
|
||||||
if (!seekBar) return;
|
if (!seekBar) return;
|
||||||
|
|
||||||
let mouseOnSeekBar = false;
|
let mouseOnSeekBar = false;
|
||||||
@@ -163,6 +199,12 @@ class PreviewBar {
|
|||||||
this.categoryTooltipContainer.classList.remove(TOOLTIP_VISIBLE_CLASS);
|
this.categoryTooltipContainer.classList.remove(TOOLTIP_VISIBLE_CLASS);
|
||||||
originalTooltip.style.removeProperty("display");
|
originalTooltip.style.removeProperty("display");
|
||||||
}
|
}
|
||||||
|
if (this.onYTTV) {
|
||||||
|
this.setTooltipTitle(mainSegment, this.categoryTooltip);
|
||||||
|
this.setTooltipTitle(secondarySegment, this.chapterTooltip);
|
||||||
|
this.setTooltipTitle(mainSegment, this.categoryScrubTooltip);
|
||||||
|
this.setTooltipTitle(secondarySegment, this.chapterScrubTooltip);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.categoryTooltipContainer.classList.add(TOOLTIP_VISIBLE_CLASS);
|
this.categoryTooltipContainer.classList.add(TOOLTIP_VISIBLE_CLASS);
|
||||||
if (mainSegment !== null && secondarySegment !== null) {
|
if (mainSegment !== null && secondarySegment !== null) {
|
||||||
@@ -174,6 +216,10 @@ class PreviewBar {
|
|||||||
|
|
||||||
this.setTooltipTitle(mainSegment, this.categoryTooltip);
|
this.setTooltipTitle(mainSegment, this.categoryTooltip);
|
||||||
this.setTooltipTitle(secondarySegment, this.chapterTooltip);
|
this.setTooltipTitle(secondarySegment, this.chapterTooltip);
|
||||||
|
if (this.onYTTV) {
|
||||||
|
this.setTooltipTitle(mainSegment, this.categoryScrubTooltip);
|
||||||
|
this.setTooltipTitle(secondarySegment, this.chapterScrubTooltip);
|
||||||
|
}
|
||||||
|
|
||||||
if (isVorapisInstalled()) {
|
if (isVorapisInstalled()) {
|
||||||
const tooltipParent = tooltipTextWrapper.parentElement!;
|
const tooltipParent = tooltipTextWrapper.parentElement!;
|
||||||
@@ -226,7 +272,12 @@ class PreviewBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// On the seek bar
|
// On the seek bar
|
||||||
this.parent.prepend(this.container);
|
if (this.onYTTV) {
|
||||||
|
// order of sibling elements matters on YTTV
|
||||||
|
this.parent.insertBefore(this.container, this.parent.firstChild.nextSibling.nextSibling);
|
||||||
|
} else {
|
||||||
|
this.parent.prepend(this.container);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
@@ -362,6 +413,10 @@ class PreviewBar {
|
|||||||
bar.style.marginRight = `${this.chapterMargin}px`;
|
bar.style.marginRight = `${this.chapterMargin}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.onYTTV) {
|
||||||
|
bar.classList.add("previewbar-yttv");
|
||||||
|
}
|
||||||
|
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,8 +923,10 @@ class PreviewBar {
|
|||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
const chapterButton = this.getChapterButton(chaptersContainer);
|
const chapterButton = this.getChapterButton(chaptersContainer);
|
||||||
chapterButton.classList.remove("ytp-chapter-container-disabled");
|
if (chapterButton) {
|
||||||
chapterButton.disabled = false;
|
chapterButton.classList.remove("ytp-chapter-container-disabled");
|
||||||
|
chapterButton.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||||
chapterTitle.style.display = "none";
|
chapterTitle.style.display = "none";
|
||||||
@@ -878,6 +935,9 @@ class PreviewBar {
|
|||||||
const elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
chapterTitle.parentElement.insertBefore(elem, chapterTitle);
|
chapterTitle.parentElement.insertBefore(elem, chapterTitle);
|
||||||
elem.classList.add("sponsorChapterText");
|
elem.classList.add("sponsorChapterText");
|
||||||
|
if (document.location.host === "tv.youtube.com") {
|
||||||
|
elem.style.lineHeight = "initial";
|
||||||
|
}
|
||||||
return elem;
|
return elem;
|
||||||
})()) as HTMLDivElement;
|
})()) as HTMLDivElement;
|
||||||
chapterCustomText.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
|
chapterCustomText.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
|
||||||
@@ -890,7 +950,15 @@ class PreviewBar {
|
|||||||
|
|
||||||
if (chosenSegment.source === SponsorSourceType.Server) {
|
if (chosenSegment.source === SponsorSourceType.Server) {
|
||||||
const chapterVoteContainer = this.chapterVote.getContainer();
|
const chapterVoteContainer = this.chapterVote.getContainer();
|
||||||
if (!chapterButton.contains(chapterVoteContainer)) {
|
if (document.location.host === "tv.youtube.com") {
|
||||||
|
if (!chaptersContainer.contains(chapterVoteContainer)) {
|
||||||
|
const oldVoteContainers = document.querySelectorAll("#chapterVote");
|
||||||
|
if (oldVoteContainers.length > 0) {
|
||||||
|
oldVoteContainers.forEach((oldVoteContainer) => oldVoteContainer.remove());
|
||||||
|
}
|
||||||
|
chaptersContainer.appendChild(chapterVoteContainer);
|
||||||
|
}
|
||||||
|
} else if (!chapterButton.contains(chapterVoteContainer)) {
|
||||||
const oldVoteContainers = document.querySelectorAll("#chapterVote");
|
const oldVoteContainers = document.querySelectorAll("#chapterVote");
|
||||||
if (oldVoteContainers.length > 0) {
|
if (oldVoteContainers.length > 0) {
|
||||||
oldVoteContainers.forEach((oldVoteContainer) => oldVoteContainer.remove());
|
oldVoteContainers.forEach((oldVoteContainer) => oldVoteContainer.remove());
|
||||||
@@ -929,6 +997,18 @@ class PreviewBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getChaptersContainer(): HTMLElement {
|
private getChaptersContainer(): HTMLElement {
|
||||||
|
if (document.location.host === "tv.youtube.com") {
|
||||||
|
if (!document.querySelector(".ytp-chapter-container")) {
|
||||||
|
const dest = document.querySelector(".ypcs-control-buttons-left");
|
||||||
|
if (!dest) return null;
|
||||||
|
const sbChapterContainer = document.createElement("div");
|
||||||
|
sbChapterContainer.className = "ytp-chapter-container";
|
||||||
|
const sbChapterTitleContent = document.createElement("div");
|
||||||
|
sbChapterTitleContent.className = "ytp-chapter-title-content";
|
||||||
|
sbChapterContainer.appendChild(sbChapterTitleContent);
|
||||||
|
dest.appendChild(sbChapterContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
return document.querySelector(".ytp-chapter-container") as HTMLElement;
|
return document.querySelector(".ytp-chapter-container") as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface SkipButtonControlBarProps {
|
|||||||
skip: (segment: SponsorTime) => void;
|
skip: (segment: SponsorTime) => void;
|
||||||
selectSegment: (UUID: SegmentUUID) => void;
|
selectSegment: (UUID: SegmentUUID) => void;
|
||||||
onMobileYouTube: boolean;
|
onMobileYouTube: boolean;
|
||||||
|
onYTTV: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SkipButtonControlBar {
|
export class SkipButtonControlBar {
|
||||||
@@ -21,6 +22,7 @@ export class SkipButtonControlBar {
|
|||||||
|
|
||||||
showKeybindHint = true;
|
showKeybindHint = true;
|
||||||
onMobileYouTube: boolean;
|
onMobileYouTube: boolean;
|
||||||
|
onYTTV: boolean;
|
||||||
|
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ export class SkipButtonControlBar {
|
|||||||
constructor(props: SkipButtonControlBarProps) {
|
constructor(props: SkipButtonControlBarProps) {
|
||||||
this.skip = props.skip;
|
this.skip = props.skip;
|
||||||
this.onMobileYouTube = props.onMobileYouTube;
|
this.onMobileYouTube = props.onMobileYouTube;
|
||||||
|
this.onYTTV = props.onYTTV;
|
||||||
|
|
||||||
this.container = document.createElement("div");
|
this.container = document.createElement("div");
|
||||||
this.container.classList.add("skipButtonControlBarContainer");
|
this.container.classList.add("skipButtonControlBarContainer");
|
||||||
@@ -50,6 +53,10 @@ export class SkipButtonControlBar {
|
|||||||
this.skipIcon.src = chrome.runtime.getURL("icons/skipIcon.svg");
|
this.skipIcon.src = chrome.runtime.getURL("icons/skipIcon.svg");
|
||||||
this.skipIcon.classList.add("ytp-button");
|
this.skipIcon.classList.add("ytp-button");
|
||||||
this.skipIcon.id = "sbSkipIconControlBarImage";
|
this.skipIcon.id = "sbSkipIconControlBarImage";
|
||||||
|
if (this.onYTTV) {
|
||||||
|
this.skipIcon.style.width = "24px";
|
||||||
|
this.skipIcon.style.height = "24px";
|
||||||
|
}
|
||||||
|
|
||||||
this.textContainer = document.createElement("div");
|
this.textContainer = document.createElement("div");
|
||||||
|
|
||||||
@@ -84,7 +91,7 @@ export class SkipButtonControlBar {
|
|||||||
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 || this.onYTTV) {
|
||||||
mountingContainer.appendChild(this.container);
|
mountingContainer.appendChild(this.container);
|
||||||
} else {
|
} else {
|
||||||
mountingContainer.insertBefore(this.container, this.chapterText);
|
mountingContainer.insertBefore(this.container, this.chapterText);
|
||||||
@@ -101,8 +108,10 @@ export class SkipButtonControlBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getMountingContainer(): HTMLElement {
|
private getMountingContainer(): HTMLElement {
|
||||||
if (!this.onMobileYouTube) {
|
if (!this.onMobileYouTube && !this.onYTTV) {
|
||||||
return document.querySelector(".ytp-left-controls");
|
return document.querySelector(".ytp-left-controls");
|
||||||
|
} else if (this.onYTTV) {
|
||||||
|
return document.querySelector(".ypcs-control-buttons-left");
|
||||||
} else {
|
} else {
|
||||||
return document.getElementById("player-container-id");
|
return document.getElementById("player-container-id");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ interface GetVideoIdResponse {
|
|||||||
|
|
||||||
export interface GetChannelIDResponse {
|
export interface GetChannelIDResponse {
|
||||||
channelID: string;
|
channelID: string;
|
||||||
|
isYTTV: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SponsorStartResponse {
|
export interface SponsorStartResponse {
|
||||||
|
|||||||
@@ -930,7 +930,11 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
//get the channel url
|
//get the channel url
|
||||||
const response = await sendTabMessageAsync({ message: 'getChannelID' }) as GetChannelIDResponse;
|
const response = await sendTabMessageAsync({ message: 'getChannelID' }) as GetChannelIDResponse;
|
||||||
if (!response.channelID) {
|
if (!response.channelID) {
|
||||||
alert(chrome.i18n.getMessage("channelDataNotFound") + " https://github.com/ajayyy/SponsorBlock/issues/753");
|
if (response.isYTTV) {
|
||||||
|
alert(chrome.i18n.getMessage("yttvNoChannelWhitelist"));
|
||||||
|
} else {
|
||||||
|
alert(chrome.i18n.getMessage("channelDataNotFound") + " https://github.com/ajayyy/SponsorBlock/issues/753");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ export class ChapterVote {
|
|||||||
this.container.id = "chapterVote";
|
this.container.id = "chapterVote";
|
||||||
this.container.style.height = "100%";
|
this.container.style.height = "100%";
|
||||||
|
|
||||||
|
if (document.location.host === "tv.youtube.com") {
|
||||||
|
this.container.style.lineHeight = "initial";
|
||||||
|
}
|
||||||
|
|
||||||
this.root = createRoot(this.container);
|
this.root = createRoot(this.container);
|
||||||
this.root.render(<ChapterVoteComponent ref={this.ref} vote={vote} />);
|
this.root.render(<ChapterVoteComponent ref={this.ref} vote={vote} />);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ export default class Utils {
|
|||||||
".main-video-section > .video-container", // Cloudtube
|
".main-video-section > .video-container", // Cloudtube
|
||||||
".shaka-video-container", // Piped
|
".shaka-video-container", // Piped
|
||||||
"#player-container.ytk-player", // YT Kids
|
"#player-container.ytk-player", // YT Kids
|
||||||
|
"#id-tv-container" // YTTV
|
||||||
];
|
];
|
||||||
|
|
||||||
let referenceNode = findValidElementFromSelector(selectors)
|
let referenceNode = findValidElementFromSelector(selectors)
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ export function getControls(): HTMLElement {
|
|||||||
// Piped shaka player
|
// Piped shaka player
|
||||||
".shaka-bottom-controls",
|
".shaka-bottom-controls",
|
||||||
// Vorapis v3
|
// Vorapis v3
|
||||||
".html5-player-chrome"
|
".html5-player-chrome",
|
||||||
|
// tv.youtube.com
|
||||||
|
".ypcs-control-buttons-right"
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const controlsSelector of controlsSelectors) {
|
for (const controlsSelector of controlsSelectors) {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
import PreviewBar, { PreviewBarSegment } from "../src/js-components/previewBar";
|
import PreviewBar, { PreviewBarSegment } from "../src/js-components/previewBar";
|
||||||
|
|
||||||
describe("createChapterRenderGroups", () => {
|
describe("createChapterRenderGroups", () => {
|
||||||
let previewBar: PreviewBar;
|
let previewBar: PreviewBar;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
previewBar = new PreviewBar(null, null, null, null, null, true);
|
previewBar = new PreviewBar(null, null, null, null, null, null, true);
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Two unrelated times", () => {
|
it("Two unrelated times", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user