Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into improvements

This commit is contained in:
Ajay Ramachandran
2021-06-18 00:46:55 -04:00
23 changed files with 482 additions and 848 deletions

View File

@@ -35,15 +35,18 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
{chrome.i18n.getMessage("category")}
</td>
<td id={"CategorySkipOption"}>
<td id={"CategorySkipOption"}
className="skipOption">
{chrome.i18n.getMessage("skipOption")}
</td>
<td id={"CategoryColorOption"}>
<td id={"CategoryColorOption"}
className="colorOption">
{chrome.i18n.getMessage("seekBarColor")}
</td>
<td id={"CategoryPreviewColorOption"}>
<td id={"CategoryPreviewColorOption"}
className="previewColorOption">
{chrome.i18n.getMessage("previewColor")}
</td>
</tr>

View File

@@ -24,8 +24,8 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
// Setup state
this.state = {
color: props.defaultColor || Config.config.barTypes[this.props.category].color,
previewColor: props.defaultPreviewColor || Config.config.barTypes["preview-" + this.props.category].color,
color: props.defaultColor || Config.config.barTypes[this.props.category]?.color,
previewColor: props.defaultPreviewColor || Config.config.barTypes["preview-" + this.props.category]?.color,
}
}
@@ -59,7 +59,8 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
{chrome.i18n.getMessage("category_" + this.props.category)}
</td>
<td id={this.props.category + "SkipOption"}>
<td id={this.props.category + "SkipOption"}
className="skipOption">
<select
className="categoryOptionsSelector"
defaultValue={defaultOption}
@@ -68,7 +69,8 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
</select>
</td>
<td id={this.props.category + "ColorOption"}>
<td id={this.props.category + "ColorOption"}
className="colorOption">
<input
className="categoryColorTextBox option-text-box"
type="color"
@@ -76,7 +78,8 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
value={this.state.color} />
</td>
<td id={this.props.category + "PreviewColorOption"}>
<td id={this.props.category + "PreviewColorOption"}
className="previewColorOption">
<input
className="categoryColorTextBox option-text-box"
type="color"

View File

@@ -25,7 +25,8 @@ export interface NoticeProps {
// Callback for when this is closed
closeListener: () => void,
zIndex?: number
zIndex?: number,
style?: React.CSSProperties
}
export interface NoticeState {
@@ -81,7 +82,8 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
render(): React.ReactElement {
const noticeStyle: React.CSSProperties = {
zIndex: this.props.zIndex || (1000 + this.amountOfPreviousNotices)
zIndex: this.props.zIndex || (1000 + this.amountOfPreviousNotices),
...(this.props.style ?? {})
}
return (

View File

@@ -135,9 +135,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
render(): React.ReactElement {
const noticeStyle: React.CSSProperties = {
zIndex: 1500 + this.amountOfPreviousNotices
}
const noticeStyle: React.CSSProperties = { }
if (this.contentContainer().onMobileYouTube) {
noticeStyle.bottom = "4em";
noticeStyle.transform = "scale(0.8) translate(10%, 10%)";
@@ -155,6 +153,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
timed={true}
maxCountdownTime={this.state.maxCountdownTime}
videoSpeed={() => this.contentContainer().v?.playbackRate}
style={noticeStyle}
ref={this.noticeRef}
closeListener={() => this.closeListener()}
smaller={this.props.smaller}

View File

@@ -18,6 +18,7 @@ interface SBConfig {
showTimeWithSkips: boolean,
disableSkipping: boolean,
trackViewCount: boolean,
trackViewCountInPrivate: boolean,
dontShowNotice: boolean,
hideVideoPlayerControls: boolean,
hideInfoButtonPlayerControls: boolean,
@@ -155,6 +156,7 @@ const Config: SBObject = {
showTimeWithSkips: true,
disableSkipping: false,
trackViewCount: true,
trackViewCountInPrivate: true,
dontShowNotice: false,
hideVideoPlayerControls: false,
hideInfoButtonPlayerControls: false,
@@ -224,11 +226,11 @@ const Config: SBObject = {
opacity: "0.7"
},
"preview": {
color: "#ff1684",
color: "#0b9d65",
opacity: "0.7"
},
"preview-preview": {
color: "#9b044c",
color: "#065b3a",
opacity: "0.7"
},
"music_offtopic": {
@@ -352,6 +354,14 @@ function migrateOldFormats(config: SBConfig) {
config.categorySelections = config.categorySelections;
}
// Remove some old unused options
if (config["sponsorVideoID"] !== undefined) {
chrome.storage.sync.remove("sponsorVideoID");
}
if (config["previousVideoID"] !== undefined) {
chrome.storage.sync.remove("previousVideoID");
}
}
async function setupConfig() {

View File

@@ -33,15 +33,15 @@ let channelIDInfo: ChannelIDInfo;
// Skips are rescheduled every seeking event.
// Skips are canceled every seeking event
let currentSkipSchedule: NodeJS.Timeout = null;
let seekListenerSetUp = false
/** Has the sponsor been skipped */
let sponsorSkipped: boolean[] = [];
//the video
let video: HTMLVideoElement;
let videoMutationObserver: MutationObserver = null;
// List of videos that have had event listeners added to them
const videoRootsWithEventListeners: HTMLDivElement[] = [];
const videosWithEventListeners: HTMLVideoElement[] = [];
let onInvidious;
let onMobileYouTube;
@@ -49,9 +49,6 @@ let onMobileYouTube;
//the video id of the last preview bar update
let lastPreviewBarUpdate;
//whether the duration listener listening for the duration changes of the video has been setup yet
let durationListenerSetUp = false;
// Is the video currently being switched
let switchingVideos = null;
@@ -77,6 +74,7 @@ const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImage
// Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
addHotkeyListener();
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
@@ -475,48 +473,60 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
}
}
async function sponsorsLookup(id: string) {
video = document.querySelector('video') // Youtube video player
//there is no video here
if (video == null) {
setTimeout(() => sponsorsLookup(id), 100);
return;
function setupVideoMutationListener() {
const videoContainer = document.querySelector(".html5-video-container");
if (!videoContainer || videoMutationObserver !== null || onInvidious) return;
videoMutationObserver = new MutationObserver(refreshVideoAttachments);
videoMutationObserver.observe(videoContainer, {
attributes: true,
childList: true,
subtree: true
});
}
function refreshVideoAttachments() {
const newVideo = document.querySelector('video');
if (newVideo && newVideo !== video) {
video = newVideo;
if (!videosWithEventListeners.includes(video)) {
videosWithEventListeners.push(video);
setupVideoListeners();
}
}
}
addHotkeyListener();
function setupVideoListeners() {
//wait until it is loaded
video.addEventListener('durationchange', durationChangeListener);
if (!durationListenerSetUp) {
durationListenerSetUp = true;
//wait until it is loaded
video.addEventListener('durationchange', durationChangeListener);
}
if (!seekListenerSetUp && !Config.config.disableSkipping) {
seekListenerSetUp = true;
if (!Config.config.disableSkipping) {
switchingVideos = false;
video.addEventListener('play', () => {
switchingVideos = false;
// If it is not the first event, then the only way to get to 0 is if there is a seek event
// This check makes sure that changing the video resolution doesn't cause the extension to think it
// gone back to the begining
if (!firstEvent && video.currentTime === 0) return;
firstEvent = false;
// Check if an ad is playing
updateAdFlag();
// Make sure it doesn't get double called with the playing event
if (Math.abs(lastCheckVideoTime - video.currentTime) > 0.3
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)) {
lastCheckTime = Date.now();
lastCheckVideoTime = video.currentTime;
startSponsorSchedule();
}
});
video.addEventListener('playing', () => {
// Make sure it doesn't get double called with the play event
@@ -524,7 +534,7 @@ async function sponsorsLookup(id: string) {
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)) {
lastCheckTime = Date.now();
lastCheckVideoTime = video.currentTime;
startSponsorSchedule();
}
});
@@ -533,7 +543,7 @@ async function sponsorsLookup(id: string) {
// Reset lastCheckVideoTime
lastCheckTime = Date.now();
lastCheckVideoTime = video.currentTime;
startSponsorSchedule();
}
});
@@ -544,12 +554,23 @@ async function sponsorsLookup(id: string) {
// Reset lastCheckVideoTime
lastCheckVideoTime = -1;
lastCheckTime = 0;
cancelSponsorSchedule();
});
startSponsorSchedule();
}
}
async function sponsorsLookup(id: string) {
if (!video) refreshVideoAttachments();
//there is still no video here
if (!video) {
setTimeout(() => sponsorsLookup(id), 100);
return;
}
setupVideoMutationListener();
//check database for sponsor times
//made true once a setTimeout has been created to try again after a server error
@@ -566,22 +587,12 @@ async function sponsorsLookup(id: string) {
categories
}).then(async (response: FetchResponse) => {
if (response?.ok) {
let result = JSON.parse(response.responseText);
result = result.filter((video) => video.videoID === id);
if (result.length > 0) {
result = result[0].segments;
if (result.length === 0) { // return if no segments found
retryFetch(id);
return;
}
} else { // return if no video found
retryFetch(id);
return;
}
const recievedSegments: SponsorTime[] = result;
if (!recievedSegments.length) {
console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments));
const recievedSegments: SponsorTime[] = JSON.parse(response.responseText)
?.filter((video) => video.videoID === id)
?.map((video) => video.segments)[0];
if (!recievedSegments || !recievedSegments.length) {
// return if no video found
retryFetch();
return;
}
@@ -597,6 +608,7 @@ async function sponsorsLookup(id: string) {
}
}
const oldSegments = sponsorTimes || [];
sponsorTimes = recievedSegments;
// Hide all submissions smaller than the minimum duration
@@ -608,6 +620,15 @@ async function sponsorsLookup(id: string) {
}
}
for (const segment of oldSegments) {
const otherSegment = sponsorTimes.find((other) => segment.UUID === other.UUID);
if (otherSegment) {
// If they downvoted it, or changed the category, keep it
otherSegment.hidden = segment.hidden;
otherSegment.category = segment.category;
}
}
startSkipScheduleCheckingForStartSponsors();
// Reset skip save
@@ -623,20 +644,24 @@ async function sponsorsLookup(id: string) {
sponsorLookupRetries = 0;
} else if (response?.status === 404) {
retryFetch(id);
retryFetch();
} else if (sponsorLookupRetries < 15 && !recheckStarted) {
recheckStarted = true;
//TODO lower when server becomes better (back to 1 second)
//some error occurred, try again in a second
setTimeout(() => sponsorsLookup(id), 5000 + Math.random() * 15000 + 5000 * sponsorLookupRetries);
setTimeout(() => {
if (sponsorVideoID && sponsorTimes?.length === 0) {
sponsorsLookup(sponsorVideoID);
}
}, 5000 + Math.random() * 15000 + 5000 * sponsorLookupRetries);
sponsorLookupRetries++;
}
});
}
function retryFetch(id: string): void {
function retryFetch(): void {
if (!Config.config.refetchWhenNotFound) return;
sponsorDataFound = false;
@@ -647,7 +672,11 @@ function retryFetch(id: string): void {
//if less than 3 days old
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
setTimeout(() => sponsorsLookup(id), 30000 + Math.random() * 90000);
setTimeout(() => {
if (sponsorVideoID && sponsorTimes?.length === 0) {
sponsorsLookup(sponsorVideoID);
}
}, 10000 + Math.random() * 30000);
}
});
@@ -692,7 +721,7 @@ function startSkipScheduleCheckingForStartSponsors() {
* Get the video info for the current tab from YouTube
*/
async function getVideoInfo(): Promise<void> {
const result = await utils.asyncRequestToCustomServer("GET", "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID);
const result = await utils.asyncRequestToCustomServer("GET", "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID + "&html5=1");
if (result.ok) {
const decodedData = decodeURIComponent(result.responseText).match(/player_response=([^&]*)/)[1];
@@ -706,7 +735,7 @@ async function getVideoInfo(): Promise<void> {
}
}
function getYouTubeVideoID(url: string) {
function getYouTubeVideoID(url: string): string | boolean {
// For YouTube TV support
if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
@@ -753,7 +782,7 @@ function getYouTubeVideoID(url: string) {
*/
function updatePreviewBarPositionMobile(parent: HTMLElement) {
if (document.getElementById("previewbar") === null) {
previewBar.updatePosition(parent);
previewBar.createElement(parent);
}
}
@@ -805,16 +834,19 @@ function updatePreviewBar(): void {
async function whitelistCheck() {
const whitelistedChannels = Config.config.whitelistedChannels;
const channelID = document.querySelector(".ytd-channel-name a")?.getAttribute("href")?.replace(/\/.+\//, "") // YouTube
const getChannelID = () => videoInfo?.videoDetails?.channelId
?? document.querySelector(".ytd-channel-name a")?.getAttribute("href")?.replace(/\/.+\//, "") // YouTube
?? document.querySelector(".ytp-title-channel-logo")?.getAttribute("href")?.replace(/https:\/.+\//, "") // YouTube Embed
?? document.querySelector("a > .channel-profile")?.parentElement?.getAttribute("href")?.replace(/\/.+\//, ""); // Invidious
if (channelID) {
try {
await utils.wait(() => !!getChannelID(), 6000, 20);
channelIDInfo = {
status: ChannelIDStatus.Found,
id: channelID
id: getChannelID()
}
} else {
} catch (e) {
channelIDInfo = {
status: ChannelIDStatus.Failed,
id: null
@@ -824,7 +856,7 @@ async function whitelistCheck() {
}
//see if this is a whitelisted channel
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
if (whitelistedChannels != undefined && whitelistedChannels.includes(getChannelID())) {
channelWhitelisted = true;
}
@@ -956,8 +988,8 @@ function previewTime(time: number, unpause = true) {
//send telemetry and count skip
function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped: number, fullSkip: boolean) {
if (!Config.config.trackViewCount) return;
if (!Config.config.trackViewCount || (!Config.config.trackViewCountInPrivate && chrome.extension.inIncognitoContext)) return;
let counted = false;
for (const segment of skippingSegments) {
const index = sponsorTimes.indexOf(segment);
@@ -1091,7 +1123,7 @@ async function createButtons(): Promise<void> {
/** Creates any missing buttons on the player and updates their visiblity. */
async function updateVisibilityOfPlayerControlsButton(): Promise<void> {
// Not on a proper video yet
if (!sponsorVideoID) return;
if (!sponsorVideoID || onMobileYouTube) return;
await createButtons();
@@ -1108,7 +1140,7 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<void> {
/** Updates the visibility of buttons on the player related to creating segments. */
function updateEditButtonsOnPlayer(): void {
// Don't try to update the buttons if we aren't on a YouTube video page
if (!sponsorVideoID) return;
if (!sponsorVideoID || onMobileYouTube) return;
const buttonsEnabled = !Config.config.hideVideoPlayerControls && !onInvidious;
@@ -1183,6 +1215,9 @@ function startOrEndTimingNewSegment() {
// Save the newly created segment
Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting);
// Make sure they know if someone has already submitted something it while they were watching
sponsorsLookup(sponsorVideoID);
updateEditButtonsOnPlayer();
updateSponsorTimesSubmitting(false);
}
@@ -1531,21 +1566,14 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
return sponsorTimesMessage;
}
function addHotkeyListener(): boolean {
let videoRoot = document.getElementById("movie_player") as HTMLDivElement;
if (onInvidious) videoRoot = (document.getElementById("player-container") ?? document.getElementById("player")) as HTMLDivElement;
if (video.baseURI.startsWith("https://www.youtube.com/tv#/")) videoRoot = document.querySelector("ytlr-watch-page") as HTMLDivElement;
if (videoRoot && !videoRootsWithEventListeners.includes(videoRoot)) {
videoRoot.addEventListener("keydown", hotkeyListener);
videoRootsWithEventListeners.push(videoRoot);
return true;
}
return false;
function addHotkeyListener(): void {
document.addEventListener("keydown", hotkeyListener);
}
function hotkeyListener(e: KeyboardEvent): void {
if (["textarea", "input"].includes(document.activeElement?.tagName?.toLowerCase())
|| document.activeElement?.id?.toLowerCase()?.includes("editable")) return;
const key = e.key;
const skipKey = Config.config.skipKeybind;

View File

@@ -1,5 +1,5 @@
/*
This is based on code from VideoSegments.
Parts of this are inspired from code from VideoSegments, but rewritten and under the LGPLv3 license
https://github.com/videosegments/videosegments/commits/f1e111bdfe231947800c6efdd51f62a4e7fef4d4/segmentsbar/segmentsbar.js
*/
@@ -37,7 +37,7 @@ class PreviewBar {
this.onMobileYouTube = onMobileYouTube;
this.onInvidious = onInvidious;
this.updatePosition(parent);
this.createElement(parent);
this.setupHoverText();
}
@@ -135,7 +135,7 @@ class PreviewBar {
});
}
updatePosition(parent: HTMLElement): void {
createElement(parent: HTMLElement): void {
this.parent = parent;
if (this.onMobileYouTube) {
@@ -143,22 +143,19 @@ class PreviewBar {
parent.style.opacity = "1";
this.container.style.transform = "none";
} else if (!this.onInvidious) {
// Hover listener
this.parent.addEventListener("mouseenter", () => this.container.classList.add("hovered"));
this.parent.addEventListener("mouseleave", () => this.container.classList.remove("hovered"));
}
// On the seek bar
this.parent.prepend(this.container);
}
// TODO: call on config changes
updateColor(segmentType: string, color: string, opacity: number): void {
const bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segmentType + ']');
for (const bar of bars) {
bar.style.backgroundColor = color;
bar.style.opacity = String(opacity);
}
}
clear(): void {
this.videoDuration = 0;
this.segments = [];
@@ -170,7 +167,6 @@ class PreviewBar {
set(segments: PreviewBarSegment[], videoDuration: number): void {
this.clear();
if (!segments) return;
this.segments = segments;
@@ -191,12 +187,11 @@ class PreviewBar {
bar.classList.add('previewbar');
bar.innerHTML = '&nbsp;';
const barSegmentType = (unsubmitted ? 'preview-' : '') + category;
const fullCategoryName = (unsubmitted ? 'preview-' : '') + category;
bar.setAttribute('sponsorblock-category', fullCategoryName);
bar.setAttribute('data-vs-segment-type', barSegmentType);
bar.style.backgroundColor = Config.config.barTypes[barSegmentType].color;
if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[barSegmentType].opacity;
bar.style.backgroundColor = Config.config.barTypes[fullCategoryName]?.color;
if (!this.onMobileYouTube) bar.style.opacity = Config.config.barTypes[fullCategoryName]?.opacity;
bar.style.position = "absolute";
if (segment[1] - segment[0] > 0) bar.style.width = this.timeToPercentage(segment[1] - segment[0]);

View File

@@ -31,6 +31,11 @@ async function init() {
const optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) {
if (optionsElements[i].getAttribute("private-mode-only") === "true" && !(await isIncognitoAllowed())) {
optionsElements[i].classList.add("hidden");
continue;
}
switch (optionsElements[i].getAttribute("option-type")) {
case "toggle": {
const option = optionsElements[i].getAttribute("sync-option");
@@ -540,3 +545,7 @@ function copyDebugOutputToClipboard() {
alert(chrome.i18n.getMessage("copyDebugInformationFailed"));
});
}
function isIncognitoAllowed(): Promise<boolean> {
return new Promise((resolve) => chrome.extension.isAllowedIncognitoAccess(resolve));
}

View File

@@ -372,8 +372,12 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
const sponsorTimeButton = document.createElement("button");
sponsorTimeButton.className = "segmentTimeButton popupElement";
const prefix = utils.shortCategoryName(segmentTimes[i].category) + ": ";
const categoryColorCircle = document.createElement("span");
categoryColorCircle.id = "sponsorTimesCategoryColorCircle" + UUID;
categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category]?.color;
categoryColorCircle.classList.add("dot");
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
let extraInfo = "";
if (segmentTimes[i].hidden === SponsorHideType.Downvoted) {
//this one is downvoted
@@ -382,14 +386,15 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
//this one is too short
extraInfo = " (" + chrome.i18n.getMessage("hiddenDueToDuration") + ")";
}
const textNode = document.createTextNode(utils.shortCategoryName(segmentTimes[i].category) + extraInfo);
const segmentTimeFromToNode = document.createElement("div");
segmentTimeFromToNode.innerText = utils.getFormattedTime(segmentTimes[i].segment[0], true) + " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true);
segmentTimeFromToNode.style.margin = "5px";
sponsorTimeButton.innerText = prefix + utils.getFormattedTime(segmentTimes[i].segment[0], true) + " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true) + extraInfo;
const categoryColorCircle = document.createElement("span");
categoryColorCircle.id = "sponsorTimesCategoryColorCircle" + UUID;
categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category].color;
categoryColorCircle.classList.add("dot");
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
sponsorTimeButton.appendChild(categoryColorCircle);
sponsorTimeButton.appendChild(textNode);
sponsorTimeButton.appendChild(segmentTimeFromToNode);
const votingButtons = document.createElement("div");
votingButtons.classList.add("votingButtons");
@@ -398,7 +403,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
const voteButtonsContainer = document.createElement("div");
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
voteButtonsContainer.setAttribute("align", "center");
voteButtonsContainer.style.display = "none"
voteButtonsContainer.classList.add('voteButtonsContainer--hide');
const upvoteButton = document.createElement("img");
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
@@ -427,7 +432,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
//add click listener to open up vote panel
sponsorTimeButton.addEventListener("click", function() {
voteButtonsContainer.style.removeProperty("display");
voteButtonsContainer.classList.toggle("voteButtonsContainer--hide");
});
// Will contain request status
@@ -441,7 +446,6 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
thanksForVotingText.classList.add("sponsorTimesThanksForVotingText");
voteStatusContainer.appendChild(thanksForVotingText);
votingButtons.append(categoryColorCircle);
votingButtons.append(sponsorTimeButton);
votingButtons.append(voteButtonsContainer);
votingButtons.append(voteStatusContainer);

View File

@@ -22,10 +22,11 @@ class SkipNotice {
this.contentContainer = contentContainer;
//get reference node
let referenceNode = document.getElementById("player-container-id")
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
?? document.querySelector(".main-video-section > .video-container"); // Cloudtube
if (referenceNode == null) {
//for embeds
const player = document.getElementById("player");
@@ -33,16 +34,13 @@ class SkipNotice {
let index = 1;
//find the child that is the video player (sometimes it is not the first)
while (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed")) {
while (index < player.children.length && (!referenceNode.classList.contains("html5-video-player") || !referenceNode.classList.contains("ytp-embed"))) {
referenceNode = player.children[index] as HTMLElement;
index++;
}
}
// YouTube Music
if (new URL(document.URL).host === "music.youtube.com") {
referenceNode = document.querySelector("#main-panel.ytmusic-player-page");
}
const amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
//this is the suffix added at the end of every id