mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-11 05:57:07 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into chapters
This commit is contained in:
221
src/content.ts
221
src/content.ts
@@ -10,7 +10,6 @@ import SkipNotice from "./render/SkipNotice";
|
||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||
import SubmissionNotice from "./render/SubmissionNotice";
|
||||
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
||||
import * as Chat from "./js-components/chat";
|
||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||
import { findValidElement, getControls, getExistingChapters, getHashParams, isVisible } from "./utils/pageUtils";
|
||||
@@ -20,6 +19,7 @@ import { AnimationUtils } from "./utils/animationUtils";
|
||||
import { GenericUtils } from "./utils/genericUtils";
|
||||
import { logDebug } from "./utils/logger";
|
||||
import { importTimes } from "./utils/exporter";
|
||||
import { openWarningDialog } from "./utils/warnings";
|
||||
|
||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||
@@ -62,13 +62,14 @@ let sponsorSkipped: boolean[] = [];
|
||||
let video: HTMLVideoElement;
|
||||
let videoMuted = false; // Has it been attempted to be muted
|
||||
let videoMutationObserver: MutationObserver = null;
|
||||
let waitingForNewVideo = false;
|
||||
// List of videos that have had event listeners added to them
|
||||
const videosWithEventListeners: HTMLVideoElement[] = [];
|
||||
const controlsWithEventListeners: HTMLElement[] = []
|
||||
|
||||
// This misleading variable name will be fixed soon
|
||||
let onInvidious;
|
||||
let onMobileYouTube;
|
||||
let onInvidious: boolean;
|
||||
let onMobileYouTube: boolean;
|
||||
|
||||
//the video id of the last preview bar update
|
||||
let lastPreviewBarUpdate;
|
||||
@@ -76,9 +77,6 @@ let lastPreviewBarUpdate;
|
||||
// Is the video currently being switched
|
||||
let switchingVideos = null;
|
||||
|
||||
// Made true every videoID change
|
||||
let firstEvent = false;
|
||||
|
||||
// Used by the play and playing listeners to make sure two aren't
|
||||
// called at the same time
|
||||
let lastCheckTime = 0;
|
||||
@@ -102,7 +100,8 @@ 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)));
|
||||
// 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("a.ytp-title-link[data-sessionlink='feature=player-title']").then(() => videoIDChange(getYouTubeVideoID(document)).then())
|
||||
addPageListeners();
|
||||
addHotkeyListener();
|
||||
|
||||
@@ -119,6 +118,9 @@ let submissionNotice: SubmissionNotice = null;
|
||||
// If there is an advert playing (or about to be played), this is true
|
||||
let isAdPlaying = false;
|
||||
|
||||
let lastResponseStatus: number;
|
||||
let retryCount = 0;
|
||||
|
||||
// Contains all of the functions and variables needed by the skip notice
|
||||
const skipNoticeContentContainer: ContentContainer = () => ({
|
||||
vote,
|
||||
@@ -166,6 +168,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
||||
//send the sponsor times along with if it's found
|
||||
sendResponse({
|
||||
found: sponsorDataFound,
|
||||
status: lastResponseStatus,
|
||||
sponsorTimes: sponsorTimes,
|
||||
time: video.currentTime,
|
||||
onMobileYouTube
|
||||
@@ -206,8 +209,12 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
||||
submitSponsorTimes();
|
||||
break;
|
||||
case "refreshSegments":
|
||||
// update video on refresh if videoID invalid
|
||||
if (!sponsorVideoID) videoIDChange(getYouTubeVideoID(document));
|
||||
// fetch segments
|
||||
sponsorsLookup(false).then(() => sendResponse({
|
||||
found: sponsorDataFound,
|
||||
status: lastResponseStatus,
|
||||
sponsorTimes: sponsorTimes,
|
||||
time: video.currentTime,
|
||||
onMobileYouTube
|
||||
@@ -301,6 +308,7 @@ if (!Config.configSyncListeners.includes(contentConfigUpdateListener)) {
|
||||
function resetValues() {
|
||||
lastCheckTime = 0;
|
||||
lastCheckVideoTime = -1;
|
||||
retryCount = 0;
|
||||
|
||||
sponsorTimes = [];
|
||||
existingChaptersImported = false;
|
||||
@@ -330,8 +338,6 @@ function resetValues() {
|
||||
logDebug("Setting switching videos to true (reset data)");
|
||||
}
|
||||
|
||||
firstEvent = true;
|
||||
|
||||
// Reset advert playing flag
|
||||
isAdPlaying = false;
|
||||
|
||||
@@ -343,7 +349,7 @@ function resetValues() {
|
||||
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 (sponsorVideoID === id && (isVisible(video) || !video)) return;
|
||||
|
||||
@@ -447,7 +453,7 @@ function createPreviewBar(): void {
|
||||
isVisibleCheck: true
|
||||
}, {
|
||||
// For new mobile YouTube (#1287)
|
||||
selector: ".ytm-progress-bar",
|
||||
selector: ".progress-bar-line",
|
||||
isVisibleCheck: true
|
||||
}, {
|
||||
// For Desktop YouTube
|
||||
@@ -527,6 +533,13 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
||||
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}`);
|
||||
if (!video) return;
|
||||
if (currentTime === undefined || currentTime === null) {
|
||||
@@ -537,7 +550,16 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
||||
updateActiveSegment(currentTime);
|
||||
|
||||
if (video.paused) return;
|
||||
if (videoMuted && !inMuteSegment(currentTime)) {
|
||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
|
||||
const currentSkip = skipInfo.array[skipInfo.index];
|
||||
const skipTime: number[] = [currentSkip?.scheduledTime, skipInfo.array[skipInfo.endIndex]?.segment[1]];
|
||||
const timeUntilSponsor = skipTime?.[0] - currentTime;
|
||||
const videoID = sponsorVideoID;
|
||||
const skipBuffer = 0.003;
|
||||
|
||||
if (videoMuted && !inMuteSegment(currentTime, skipInfo.index !== -1
|
||||
&& timeUntilSponsor < skipBuffer && shouldAutoSkip(currentSkip))) {
|
||||
video.muted = false;
|
||||
videoMuted = false;
|
||||
|
||||
@@ -547,22 +569,15 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
||||
}
|
||||
}
|
||||
|
||||
logDebug(`Ready to start skipping: ${skipInfo.index} at ${currentTime}`);
|
||||
if (skipInfo.index === -1) return;
|
||||
|
||||
if (Config.config.disableSkipping || channelWhitelisted || (channelIDInfo.status === ChannelIDStatus.Fetching && Config.config.forceChannelCheck)){
|
||||
return;
|
||||
}
|
||||
|
||||
if (incorrectVideoCheck()) return;
|
||||
|
||||
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
|
||||
logDebug(`Ready to start skipping: ${skipInfo.index} at ${currentTime}`);
|
||||
if (skipInfo.index === -1) return;
|
||||
|
||||
const currentSkip = skipInfo.array[skipInfo.index];
|
||||
const skipTime: number[] = [currentSkip.scheduledTime, skipInfo.array[skipInfo.endIndex].segment[1]];
|
||||
const timeUntilSponsor = skipTime[0] - currentTime;
|
||||
const videoID = sponsorVideoID;
|
||||
|
||||
// Find all indexes in between the start and end
|
||||
let skippingSegments = [skipInfo.array[skipInfo.index]];
|
||||
if (skipInfo.index !== skipInfo.endIndex) {
|
||||
@@ -576,7 +591,11 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
||||
}
|
||||
}
|
||||
|
||||
const skipBuffer = 0.003;
|
||||
logDebug(`Next step in starting skipping: ${!shouldSkip(currentSkip)}, ${!sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)}`);
|
||||
|
||||
// Don't skip if this category should not be skipped
|
||||
if (!shouldSkip(currentSkip) && !sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment)) return;
|
||||
|
||||
const skippingFunction = (forceVideoTime?: number) => {
|
||||
let forcedSkipTime: number = null;
|
||||
let forcedIncludeIntersectingSegments = false;
|
||||
@@ -593,6 +612,19 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
|
||||
skippingSegments,
|
||||
openNotice: skipInfo.openNotice
|
||||
});
|
||||
|
||||
// These are segments that start at the exact same time but need seperate notices
|
||||
for (const extra of skipInfo.extraIndexes) {
|
||||
const extraSkip = skipInfo.array[extra];
|
||||
if (shouldSkip(extraSkip)) {
|
||||
skipToTime({
|
||||
v: video,
|
||||
skipTime: [extraSkip.scheduledTime, extraSkip.segment[1]],
|
||||
skippingSegments: [extraSkip],
|
||||
openNotice: skipInfo.openNotice
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ManualSkip
|
||||
|| currentSkip.actionType === ActionType.Mute) {
|
||||
@@ -649,15 +681,17 @@ function getVirtualTime(): number {
|
||||
(performance.now() - lastKnownVideoTime.preciseTime) / 1000 + lastKnownVideoTime.videoTime : null);
|
||||
|
||||
if ((lastTimeFromWaitingEvent || !utils.isFirefox())
|
||||
&& !isSafari() && virtualTime && Math.abs(virtualTime - video.currentTime) < 0.6) {
|
||||
&& !isSafari() && virtualTime && Math.abs(virtualTime - video.currentTime) < 0.6 && video.currentTime !== 0) {
|
||||
return virtualTime;
|
||||
} else {
|
||||
return video.currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
function inMuteSegment(currentTime: number): boolean {
|
||||
const checkFunction = (segment) => segment.actionType === ActionType.Mute && segment.segment[0] <= currentTime && segment.segment[1] > currentTime;
|
||||
function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
|
||||
const checkFunction = (segment) => segment.actionType === ActionType.Mute
|
||||
&& segment.segment[0] <= currentTime
|
||||
&& (segment.segment[1] > currentTime || (includeOverlap && segment.segment[1] + 0.02 > currentTime));
|
||||
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
||||
}
|
||||
|
||||
@@ -695,27 +729,30 @@ function setupVideoMutationListener() {
|
||||
});
|
||||
}
|
||||
|
||||
function refreshVideoAttachments() {
|
||||
const newVideo = findValidElement(document.querySelectorAll('video')) as HTMLVideoElement;
|
||||
if (newVideo && newVideo !== video) {
|
||||
video = newVideo;
|
||||
async function refreshVideoAttachments(): Promise<void> {
|
||||
if (waitingForNewVideo) return;
|
||||
|
||||
if (!videosWithEventListeners.includes(video)) {
|
||||
videosWithEventListeners.push(video);
|
||||
waitingForNewVideo = true;
|
||||
const newVideo = await utils.waitForElement("video", true) as HTMLVideoElement;
|
||||
waitingForNewVideo = false;
|
||||
|
||||
setupVideoListeners();
|
||||
setupSkipButtonControlBar();
|
||||
setupCategoryPill();
|
||||
}
|
||||
video = newVideo;
|
||||
if (!videosWithEventListeners.includes(video)) {
|
||||
videosWithEventListeners.push(video);
|
||||
|
||||
// Create a new bar in the new video element
|
||||
if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) {
|
||||
previewBar.remove();
|
||||
previewBar = null;
|
||||
|
||||
createPreviewBar();
|
||||
}
|
||||
setupVideoListeners();
|
||||
setupSkipButtonControlBar();
|
||||
setupCategoryPill();
|
||||
}
|
||||
|
||||
if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) {
|
||||
previewBar.remove();
|
||||
previewBar = null;
|
||||
|
||||
createPreviewBar();
|
||||
}
|
||||
|
||||
videoIDChange(getYouTubeVideoID(document));
|
||||
}
|
||||
|
||||
function setupVideoListeners() {
|
||||
@@ -727,17 +764,18 @@ function setupVideoListeners() {
|
||||
switchingVideos = false;
|
||||
|
||||
let startedWaiting = false;
|
||||
let lastPausedAtZero = true;
|
||||
|
||||
video.addEventListener('play', () => {
|
||||
// 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;
|
||||
if (video.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA
|
||||
&& video.currentTime === 0) return;
|
||||
|
||||
updateVirtualTime();
|
||||
|
||||
if (switchingVideos) {
|
||||
if (switchingVideos || lastPausedAtZero) {
|
||||
switchingVideos = false;
|
||||
logDebug("Setting switching videos to false");
|
||||
|
||||
@@ -745,6 +783,8 @@ function setupVideoListeners() {
|
||||
if (sponsorTimes) startSkipScheduleCheckingForStartSponsors();
|
||||
}
|
||||
|
||||
lastPausedAtZero = false;
|
||||
|
||||
// Check if an ad is playing
|
||||
updateAdFlag();
|
||||
|
||||
@@ -760,6 +800,7 @@ function setupVideoListeners() {
|
||||
});
|
||||
video.addEventListener('playing', () => {
|
||||
updateVirtualTime();
|
||||
lastPausedAtZero = false;
|
||||
|
||||
if (startedWaiting) {
|
||||
startedWaiting = false;
|
||||
@@ -767,6 +808,14 @@ function setupVideoListeners() {
|
||||
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)}`);
|
||||
}
|
||||
|
||||
if (switchingVideos) {
|
||||
switchingVideos = false;
|
||||
logDebug("Setting switching videos to false");
|
||||
|
||||
// If already segments loaded before video, retry to skip starting segments
|
||||
if (sponsorTimes) startSkipScheduleCheckingForStartSponsors();
|
||||
}
|
||||
|
||||
// Make sure it doesn't get double called with the play event
|
||||
if (Math.abs(lastCheckVideoTime - video.currentTime) > 0.3
|
||||
|| (lastCheckVideoTime !== video.currentTime && Date.now() - lastCheckTime > 2000)) {
|
||||
@@ -788,6 +837,10 @@ function setupVideoListeners() {
|
||||
startSponsorSchedule();
|
||||
} else {
|
||||
updateActiveSegment(video.currentTime);
|
||||
|
||||
if (video.currentTime === 0) {
|
||||
lastPausedAtZero = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
video.addEventListener('ratechange', () => startSponsorSchedule());
|
||||
@@ -867,7 +920,6 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
||||
const hashParams = getHashParams();
|
||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||
|
||||
// Check for hashPrefix setting
|
||||
const hashPrefix = (await utils.getHash(sponsorVideoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||
const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||
categories,
|
||||
@@ -876,6 +928,9 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
||||
...extraRequestData
|
||||
});
|
||||
|
||||
// store last response status
|
||||
lastResponseStatus = response?.status;
|
||||
|
||||
if (response?.ok) {
|
||||
const recievedSegments: SponsorTime[] = JSON.parse(response.responseText)
|
||||
?.filter((video) => video.videoID === sponsorVideoID)
|
||||
@@ -887,7 +942,7 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
||||
?.sort((a, b) => a.segment[0] - b.segment[0]);
|
||||
if (!recievedSegments || !recievedSegments.length) {
|
||||
// return if no video found
|
||||
retryFetch();
|
||||
retryFetch(404);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -949,8 +1004,8 @@ async function sponsorsLookup(keepOldSubmissions = true) {
|
||||
//otherwise the listener can handle it
|
||||
updatePreviewBar();
|
||||
}
|
||||
} else if (response?.status === 404) {
|
||||
retryFetch();
|
||||
} else {
|
||||
retryFetch(lastResponseStatus);
|
||||
}
|
||||
|
||||
importExistingChapters(true);
|
||||
@@ -999,17 +1054,24 @@ async function lockedCategoriesLookup(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function retryFetch(): void {
|
||||
function retryFetch(errorCode: number): void {
|
||||
if (!Config.config.refetchWhenNotFound) return;
|
||||
|
||||
sponsorDataFound = false;
|
||||
|
||||
if (errorCode !== 404 && retryCount > 1) {
|
||||
// Too many errors (50x), give up
|
||||
return;
|
||||
}
|
||||
|
||||
retryCount++;
|
||||
|
||||
const delay = errorCode === 404 ? (10000 + Math.random() * 30000) : (2000 + Math.random() * 10000);
|
||||
setTimeout(() => {
|
||||
if (sponsorVideoID && sponsorTimes?.length === 0
|
||||
|| sponsorTimes.every((segment) => segment.source !== SponsorSourceType.Server)) {
|
||||
sponsorsLookup();
|
||||
}
|
||||
}, 10000 + Math.random() * 30000);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1073,28 +1135,29 @@ function startSkipScheduleCheckingForStartSponsors() {
|
||||
}
|
||||
}
|
||||
|
||||
function getYouTubeVideoID(document: Document): string | boolean {
|
||||
const url = document.URL;
|
||||
function getYouTubeVideoID(document: Document, url?: string): string | boolean {
|
||||
url ||= document.URL;
|
||||
// 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/
|
||||
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
|
||||
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/")) return getYouTubeVideoIDFromDocument();
|
||||
// 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)
|
||||
const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href");
|
||||
const element = video?.parentElement?.parentElement?.querySelector("a.ytp-title-link[data-sessionlink='feature=player-title']");
|
||||
const videoURL = element?.getAttribute("href");
|
||||
if (videoURL) {
|
||||
onInvidious = hideIcon;
|
||||
return getYouTubeVideoIDFromURL(videoURL);
|
||||
} else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1241,13 +1304,33 @@ async function whitelistCheck() {
|
||||
* Returns info about the next upcoming sponsor skip
|
||||
*/
|
||||
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
|
||||
{array: ScheduledTime[], index: number, endIndex: number, openNotice: boolean} {
|
||||
{array: ScheduledTime[], index: number, endIndex: number, extraIndexes: number[], openNotice: boolean} {
|
||||
|
||||
const autoSkipSorter = (segment: ScheduledTime) => {
|
||||
const skipOption = utils.getCategorySelection(segment.category)?.option;
|
||||
if ((skipOption === CategorySkipOption.AutoSkip || shouldAutoSkip(segment))
|
||||
&& segment.actionType === ActionType.Skip) {
|
||||
return 0;
|
||||
} else if (skipOption !== CategorySkipOption.ShowOverlay) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
const { includedTimes: submittedArray, scheduledTimes: sponsorStartTimes } =
|
||||
getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
|
||||
const { scheduledTimes: sponsorStartTimesAfterCurrentTime } = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true);
|
||||
|
||||
const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||
// This is an array in-case multiple segments have the exact same start time
|
||||
const minSponsorTimeIndexes = GenericUtils.indexesOf(sponsorStartTimes, Math.min(...sponsorStartTimesAfterCurrentTime));
|
||||
// Find auto skipping segments if possible, sort by duration otherwise
|
||||
const minSponsorTimeIndex = minSponsorTimeIndexes.sort(
|
||||
(a, b) => ((autoSkipSorter(submittedArray[a]) - autoSkipSorter(submittedArray[b]))
|
||||
|| (submittedArray[a].segment[1] - submittedArray[a].segment[0]) - (submittedArray[b].segment[1] - submittedArray[b].segment[0])))[0] ?? -1;
|
||||
// Store extra indexes for the non-auto skipping segments if others occur at the exact same start time
|
||||
const extraIndexes = minSponsorTimeIndexes.filter((i) => i !== minSponsorTimeIndex && autoSkipSorter(submittedArray[i]) !== 0);
|
||||
|
||||
const endTimeIndex = getLatestEndTimeIndex(submittedArray, minSponsorTimeIndex);
|
||||
|
||||
const { includedTimes: unsubmittedArray, scheduledTimes: unsubmittedSponsorStartTimes } =
|
||||
@@ -1263,6 +1346,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
||||
array: submittedArray,
|
||||
index: minSponsorTimeIndex,
|
||||
endIndex: endTimeIndex,
|
||||
extraIndexes, // Segments at same time that need seperate notices
|
||||
openNotice: true
|
||||
};
|
||||
} else {
|
||||
@@ -1270,6 +1354,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
||||
array: unsubmittedArray,
|
||||
index: minUnsubmittedSponsorTimeIndex,
|
||||
endIndex: previewEndTimeIndex,
|
||||
extraIndexes: [], // No manual things for unsubmitted
|
||||
openNotice: false
|
||||
};
|
||||
}
|
||||
@@ -1855,10 +1940,7 @@ async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNo
|
||||
skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category);
|
||||
} else if (response.successType == -1) {
|
||||
if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a warning from a moderator.")) {
|
||||
skipNotice.setNoticeInfoMessageWithOnClick.bind(skipNotice)(() => {
|
||||
Chat.openWarningChat(response.responseText);
|
||||
skipNotice.closeListener.call(skipNotice);
|
||||
}, chrome.i18n.getMessage("voteRejectedWarning"));
|
||||
openWarningDialog(skipNoticeContentContainer);
|
||||
} else {
|
||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)(GenericUtils.getErrorMessage(response.statusCode, response.responseText))
|
||||
}
|
||||
@@ -2046,7 +2128,7 @@ async function sendSubmitMessage() {
|
||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
||||
|
||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
|
||||
Chat.openWarningChat(response.responseText);
|
||||
openWarningDialog(skipNoticeContentContainer);
|
||||
} else {
|
||||
alert(GenericUtils.getErrorMessage(response.status, response.responseText));
|
||||
}
|
||||
@@ -2272,7 +2354,8 @@ function checkForPreloadedSegment() {
|
||||
const navigationApiAvailable = "navigation" in window;
|
||||
if (navigationApiAvailable) {
|
||||
// TODO: Remove type cast once type declarations are updated
|
||||
(window as unknown as { navigation: EventTarget }).navigation.addEventListener("navigate", () => videoIDChange(getYouTubeVideoID(document)));
|
||||
(window as unknown as { navigation: EventTarget }).navigation.addEventListener("navigate", (e) =>
|
||||
videoIDChange(getYouTubeVideoID(document, (e as unknown as Record<string, Record<string, string>>).destination.url)));
|
||||
}
|
||||
|
||||
// Record availability of Navigation API
|
||||
|
||||
Reference in New Issue
Block a user