mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-13 15:07:02 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlock into chapters
This commit is contained in:
265
src/content.ts
265
src/content.ts
@@ -11,13 +11,15 @@ import PreviewBar, {PreviewBarSegment} from "./js-components/previewBar";
|
||||
import SkipNotice from "./render/SkipNotice";
|
||||
import SkipNoticeComponent from "./components/SkipNoticeComponent";
|
||||
import SubmissionNotice from "./render/SubmissionNotice";
|
||||
import { Message, MessageResponse } from "./messageTypes";
|
||||
import { Message, MessageResponse, VoteResponse } from "./messageTypes";
|
||||
import * as Chat from "./js-components/chat";
|
||||
import { getCategoryActionType } from "./utils/categoryUtils";
|
||||
import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
import { Tooltip } from "./render/Tooltip";
|
||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||
import { getControls } from "./utils/pageUtils";
|
||||
import { findValidElement, getControls, getHashParams, isVisible } from "./utils/pageUtils";
|
||||
import { CategoryPill } from "./render/CategoryPill";
|
||||
import { AnimationUtils } from "./utils/animationUtils";
|
||||
import { GenericUtils } from "./utils/genericUtils";
|
||||
|
||||
// Hack to get the CSS loaded on permission-based sites (Invidious)
|
||||
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
|
||||
@@ -75,9 +77,11 @@ let lastCheckVideoTime = -1;
|
||||
//is this channel whitelised from getting sponsors skipped
|
||||
let channelWhitelisted = false;
|
||||
|
||||
// create preview bar
|
||||
let previewBar: PreviewBar = null;
|
||||
// Skip to highlight button
|
||||
let skipButtonControlBar: SkipButtonControlBar = null;
|
||||
// For full video sponsors/selfpromo
|
||||
let categoryPill: CategoryPill = null;
|
||||
|
||||
/** Element containing the player controls on the YouTube player. */
|
||||
let controls: HTMLElement | null = null;
|
||||
@@ -86,7 +90,8 @@ let controls: HTMLElement | null = null;
|
||||
const playerButtons: Record<string, {button: HTMLButtonElement, image: HTMLImageElement, setupListener: boolean}> = {};
|
||||
|
||||
// Direct Links after the config is loaded
|
||||
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
|
||||
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document)));
|
||||
addPageListeners();
|
||||
addHotkeyListener();
|
||||
|
||||
//the amount of times the sponsor lookup has retried
|
||||
@@ -138,7 +143,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
||||
//messages from popup script
|
||||
switch(request.message){
|
||||
case "update":
|
||||
videoIDChange(getYouTubeVideoID(document.URL));
|
||||
videoIDChange(getYouTubeVideoID(document));
|
||||
break;
|
||||
case "sponsorStart":
|
||||
startOrEndTimingNewSegment()
|
||||
@@ -264,15 +269,16 @@ function resetValues() {
|
||||
isAdPlaying = false;
|
||||
|
||||
for (let i = 0; i < skipNotices.length; i++) {
|
||||
skipNotices.pop().close();
|
||||
skipNotices.pop()?.close();
|
||||
}
|
||||
|
||||
skipButtonControlBar?.disable();
|
||||
categoryPill?.setVisibility(false);
|
||||
}
|
||||
|
||||
async function videoIDChange(id) {
|
||||
//if the id has not changed return
|
||||
if (sponsorVideoID === id) return;
|
||||
//if the id has not changed return unless the video element has changed
|
||||
if (sponsorVideoID === id && isVisible(video)) return;
|
||||
|
||||
//set the global videoID
|
||||
sponsorVideoID = id;
|
||||
@@ -338,26 +344,6 @@ async function videoIDChange(id) {
|
||||
// Clear unsubmitted segments from the previous video
|
||||
sponsorTimesSubmitting = [];
|
||||
updateSponsorTimesSubmitting();
|
||||
|
||||
// Filler update
|
||||
if (!Config.config.fillerUpdate) {
|
||||
Config.config.fillerUpdate = true;
|
||||
|
||||
utils.wait(getControls).then(() => {
|
||||
const playButton = document.querySelector(".ytp-play-button") as HTMLElement;
|
||||
const allCategories = ["sponsor", "intro", "outro", "selfpromo", "interaction"];
|
||||
if (playButton && allCategories.every((name) => Config.config.categorySelections.some((selection) => selection.name === name))
|
||||
&& utils.getCategorySelection("filler") === undefined) {
|
||||
new Tooltip({
|
||||
text: chrome.i18n.getMessage("fillerNewFeature"),
|
||||
link: "https://wiki.sponsor.ajay.app/w/Filler_Tangent",
|
||||
referenceNode: playButton.parentElement,
|
||||
prependElement: playButton,
|
||||
timeout: 10
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleMobileControlsMutations(): void {
|
||||
@@ -402,7 +388,7 @@ function createPreviewBar(): void {
|
||||
];
|
||||
|
||||
for (const selector of progressElementSelectors) {
|
||||
const el = document.querySelector<HTMLElement>(selector);
|
||||
const el = findValidElement(document.querySelectorAll(selector));
|
||||
|
||||
if (el) {
|
||||
previewBar = new PreviewBar(el, onMobileYouTube, onInvidious);
|
||||
@@ -423,6 +409,16 @@ function durationChangeListener(): void {
|
||||
updatePreviewBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered once the video is ready.
|
||||
* This is mainly to attach to embedded players who don't have a video element visible.
|
||||
*/
|
||||
function videoOnReadyListener(): void {
|
||||
createPreviewBar();
|
||||
updatePreviewBar();
|
||||
createButtons();
|
||||
}
|
||||
|
||||
function cancelSponsorSchedule(): void {
|
||||
if (currentSkipSchedule !== null) {
|
||||
clearTimeout(currentSkipSchedule);
|
||||
@@ -535,7 +531,7 @@ function inMuteSegment(currentTime: number): boolean {
|
||||
* This makes sure the videoID is still correct and if the sponsorTime is included
|
||||
*/
|
||||
function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean {
|
||||
const currentVideoID = getYouTubeVideoID(document.URL);
|
||||
const currentVideoID = getYouTubeVideoID(document);
|
||||
if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime
|
||||
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment === sponsorTime.segment))
|
||||
&& !sponsorTimesSubmitting.some((time) => time.segment === sponsorTime.segment))) {
|
||||
@@ -566,7 +562,7 @@ function setupVideoMutationListener() {
|
||||
}
|
||||
|
||||
function refreshVideoAttachments() {
|
||||
const newVideo = document.querySelector('video');
|
||||
const newVideo = findValidElement(document.querySelectorAll('video')) as HTMLVideoElement;
|
||||
if (newVideo && newVideo !== video) {
|
||||
video = newVideo;
|
||||
|
||||
@@ -575,12 +571,22 @@ function refreshVideoAttachments() {
|
||||
|
||||
setupVideoListeners();
|
||||
setupSkipButtonControlBar();
|
||||
setupCategoryPill();
|
||||
}
|
||||
|
||||
// Create a new bar in the new video element
|
||||
if (previewBar && !utils.findReferenceNode()?.contains(previewBar.container)) {
|
||||
previewBar.remove();
|
||||
previewBar = null;
|
||||
|
||||
createPreviewBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupVideoListeners() {
|
||||
//wait until it is loaded
|
||||
video.addEventListener('loadstart', videoOnReadyListener)
|
||||
video.addEventListener('durationchange', durationChangeListener);
|
||||
|
||||
if (!Config.config.disableSkipping) {
|
||||
@@ -665,8 +671,16 @@ function setupSkipButtonControlBar() {
|
||||
skipButtonControlBar.attachToPage();
|
||||
}
|
||||
|
||||
function setupCategoryPill() {
|
||||
if (!categoryPill) {
|
||||
categoryPill = new CategoryPill();
|
||||
}
|
||||
|
||||
categoryPill.attachToPage(onMobileYouTube, onInvidious, voteAsync);
|
||||
}
|
||||
|
||||
async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
||||
if (!video) refreshVideoAttachments();
|
||||
if (!video || !isVisible(video)) refreshVideoAttachments();
|
||||
//there is still no video here
|
||||
if (!video) {
|
||||
setTimeout(() => sponsorsLookup(id), 100);
|
||||
@@ -685,22 +699,14 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
||||
}
|
||||
|
||||
const extraRequestData: Record<string, unknown> = {};
|
||||
const windowHash = window.location.hash.substr(1);
|
||||
if (windowHash) {
|
||||
const params: Record<string, unknown> = windowHash.split('&').reduce((acc, param) => {
|
||||
const [key, value] = param.split('=');
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (params.requiredSegment) extraRequestData.requiredSegment = params.requiredSegment;
|
||||
}
|
||||
const hashParams = getHashParams();
|
||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||
|
||||
// Check for hashPrefix setting
|
||||
const hashPrefix = (await utils.getHash(id, 1)).substr(0, 4);
|
||||
const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||
categories,
|
||||
actionTypes: Config.config.muteSegments ? [ActionType.Skip, ActionType.Mute, ActionType.Chapter] : [ActionType.Skip, ActionType.Chapter],
|
||||
actionTypes: getEnabledActionTypes(),
|
||||
userAgent: `${chrome.runtime.id}`,
|
||||
...extraRequestData
|
||||
});
|
||||
@@ -785,6 +791,18 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) {
|
||||
lookupVipInformation(id);
|
||||
}
|
||||
|
||||
function getEnabledActionTypes(): ActionType[] {
|
||||
const actionTypes = [ActionType.Skip, ActionType.Chapter];
|
||||
if (Config.config.muteSegments) {
|
||||
actionTypes.push(ActionType.Mute);
|
||||
}
|
||||
if (Config.config.fullVideoSegments) {
|
||||
actionTypes.push(ActionType.Full);
|
||||
}
|
||||
|
||||
return actionTypes;
|
||||
}
|
||||
|
||||
function lookupVipInformation(id: string): void {
|
||||
updateVipInfo().then((isVip) => {
|
||||
if (isVip) {
|
||||
@@ -896,6 +914,11 @@ function startSkipScheduleCheckingForStartSponsors() {
|
||||
}
|
||||
}
|
||||
|
||||
const fullVideoSegment = sponsorTimes.filter((time) => time.actionType === ActionType.Full)[0];
|
||||
if (fullVideoSegment) {
|
||||
categoryPill?.setSegment(fullVideoSegment);
|
||||
}
|
||||
|
||||
if (startingSegmentTime !== -1) {
|
||||
startSponsorSchedule(undefined, startingSegmentTime);
|
||||
} else {
|
||||
@@ -924,8 +947,30 @@ async function getVideoInfo(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function getYouTubeVideoID(url: string): string | boolean {
|
||||
// For YouTube TV support
|
||||
function getYouTubeVideoID(document: Document): string | boolean {
|
||||
const url = document.URL;
|
||||
// skip to URL if matches youtube watch or invidious or matches youtube pattern
|
||||
if ((!url.includes("youtube.com")) || url.includes("/watch") || url.includes("/shorts/") || url.includes("playlist")) return getYouTubeVideoIDFromURL(url);
|
||||
// skip to document and don't hide if on /embed/
|
||||
if (url.includes("/embed/")) return getYouTubeVideoIDFromDocument(document, false);
|
||||
// skip to document if matches pattern
|
||||
if (url.includes("/channel/") || url.includes("/user/") || url.includes("/c/")) return getYouTubeVideoIDFromDocument(document);
|
||||
// not sure, try URL then document
|
||||
return getYouTubeVideoIDFromURL(url) || getYouTubeVideoIDFromDocument(document);
|
||||
}
|
||||
|
||||
function getYouTubeVideoIDFromDocument(document: Document, hideIcon = true): string | boolean {
|
||||
// get ID from document (channel trailer / embedded playlist)
|
||||
const videoURL = document.querySelector("[data-sessionlink='feature=player-title']")?.getAttribute("href");
|
||||
if (videoURL) {
|
||||
onInvidious = hideIcon;
|
||||
return getYouTubeVideoIDFromURL(videoURL);
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function getYouTubeVideoIDFromURL(url: string): string | boolean {
|
||||
if(url.startsWith("https://www.youtube.com/tv#/")) url = url.replace("#", "");
|
||||
|
||||
//Attempt to parse url
|
||||
@@ -945,7 +990,7 @@ function getYouTubeVideoID(url: string): string | boolean {
|
||||
} else if (!["m.youtube.com", "www.youtube.com", "www.youtube-nocookie.com", "music.youtube.com"].includes(urlObject.host)) {
|
||||
if (!Config.config) {
|
||||
// Call this later, in case this is an Invidious tab
|
||||
utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoID(url)));
|
||||
utils.wait(() => Config.config !== null).then(() => videoIDChange(getYouTubeVideoIDFromURL(url)));
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -963,7 +1008,7 @@ function getYouTubeVideoID(url: string): string | boolean {
|
||||
console.error("[SB] Video ID not valid for " + url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1013,7 +1058,7 @@ function updatePreviewBar(): void {
|
||||
});
|
||||
});
|
||||
|
||||
previewBar.set(previewBarSegments, video?.duration)
|
||||
previewBar.set(previewBarSegments.filter((segment) => segment.actionType !== ActionType.Full), video?.duration)
|
||||
|
||||
if (Config.config.showTimeWithSkips) {
|
||||
const skippedDuration = utils.getTimestampsDuration(previewBarSegments.map(({segment}) => segment));
|
||||
@@ -1257,7 +1302,12 @@ function skipToTime({v, skipTime, skippingSegments, openNotice, forceAutoSkip, u
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (autoSkip && Config.config.audioNotificationOnSkip) {
|
||||
const beep = new Audio(chrome.runtime.getURL("icons/beep.ogg"));
|
||||
beep.volume = video.volume * 0.1;
|
||||
beep.play();
|
||||
}
|
||||
|
||||
if (!autoSkip
|
||||
@@ -1357,8 +1407,9 @@ function shouldAutoSkip(segment: SponsorTime): boolean {
|
||||
}
|
||||
|
||||
function shouldSkip(segment: SponsorTime): boolean {
|
||||
return utils.getCategorySelection(segment.category)?.option !== CategorySkipOption.ShowOverlay ||
|
||||
(Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic"));
|
||||
return (segment.actionType !== ActionType.Full
|
||||
&& utils.getCategorySelection(segment.category)?.option !== CategorySkipOption.ShowOverlay)
|
||||
|| (Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic"));
|
||||
}
|
||||
|
||||
/** Creates any missing buttons on the YouTube player if possible. */
|
||||
@@ -1379,7 +1430,7 @@ async function createButtons(): Promise<void> {
|
||||
&& playerButtons["info"]?.button && !controlsWithEventListeners.includes(controlsContainer)) {
|
||||
controlsWithEventListeners.push(controlsContainer);
|
||||
|
||||
utils.setupAutoHideAnimation(playerButtons["info"].button, controlsContainer);
|
||||
AnimationUtils.setupAutoHideAnimation(playerButtons["info"].button, controlsContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1537,6 +1588,8 @@ function updateSponsorTimesSubmitting(getFromConfig = true) {
|
||||
if (submissionNotice !== null) {
|
||||
submissionNotice.update();
|
||||
}
|
||||
|
||||
checkForPreloadedSegment();
|
||||
}
|
||||
|
||||
function openInfoMenu() {
|
||||
@@ -1660,13 +1713,37 @@ function clearSponsorTimes() {
|
||||
}
|
||||
|
||||
//if skipNotice is null, it will not affect the UI
|
||||
function vote(type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent) {
|
||||
async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNotice?: SkipNoticeComponent): Promise<void> {
|
||||
if (skipNotice !== null && skipNotice !== undefined) {
|
||||
//add loading info
|
||||
skipNotice.addVoteButtonInfo.bind(skipNotice)(chrome.i18n.getMessage("Loading"))
|
||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)();
|
||||
}
|
||||
|
||||
const response = await voteAsync(type, UUID, category);
|
||||
if (response != undefined) {
|
||||
//see if it was a success or failure
|
||||
if (skipNotice != null) {
|
||||
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
|
||||
//success (treat rate limits as a success)
|
||||
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"));
|
||||
} else {
|
||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)(GenericUtils.getErrorMessage(response.statusCode, response.responseText))
|
||||
}
|
||||
|
||||
skipNotice.resetVoteButtonInfo.bind(skipNotice)();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function voteAsync(type: number, UUID: SegmentUUID, category?: Category): Promise<VoteResponse> {
|
||||
const sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
|
||||
|
||||
// Don't vote for preview sponsors
|
||||
@@ -1686,33 +1763,14 @@ function vote(type: number, UUID: SegmentUUID, category?: Category, skipNotice?:
|
||||
|
||||
Config.config.skipCount = Config.config.skipCount + factor;
|
||||
}
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
message: "submitVote",
|
||||
type: type,
|
||||
UUID: UUID,
|
||||
category: category
|
||||
}, function(response) {
|
||||
if (response != undefined) {
|
||||
//see if it was a success or failure
|
||||
if (skipNotice != null) {
|
||||
if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) {
|
||||
//success (treat rate limits as a success)
|
||||
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"));
|
||||
} else {
|
||||
skipNotice.setNoticeInfoMessage.bind(skipNotice)(utils.getErrorMessage(response.statusCode, response.responseText))
|
||||
}
|
||||
|
||||
skipNotice.resetVoteButtonInfo.bind(skipNotice)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
chrome.runtime.sendMessage({
|
||||
message: "submitVote",
|
||||
type: type,
|
||||
UUID: UUID,
|
||||
category: category
|
||||
}, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1753,9 +1811,15 @@ function submitSponsorTimes() {
|
||||
//send the message to the background js
|
||||
//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"))) {
|
||||
alert(chrome.i18n.getMessage("liveOrPremiere"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add loading animation
|
||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
|
||||
const stopAnimation = utils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
|
||||
const stopAnimation = AnimationUtils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
|
||||
|
||||
//check if a sponsor exceeds the duration of the video
|
||||
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
||||
@@ -1770,8 +1834,8 @@ async function sendSubmitMessage() {
|
||||
// Check to see if any of the submissions are below the minimum duration set
|
||||
if (Config.config.minDuration > 0) {
|
||||
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
|
||||
if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration
|
||||
&& getCategoryActionType(sponsorTimesSubmitting[i].category) !== CategoryActionType.POI) {
|
||||
const duration = sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0];
|
||||
if (duration > 0 && duration < Config.config.minDuration) {
|
||||
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||
getSegmentsMessage(sponsorTimesSubmitting);
|
||||
|
||||
@@ -1827,7 +1891,7 @@ async function sendSubmitMessage() {
|
||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a warning from a moderator.")) {
|
||||
Chat.openWarningChat(response.responseText);
|
||||
} else {
|
||||
alert(utils.getErrorMessage(response.status, response.responseText));
|
||||
alert(GenericUtils.getErrorMessage(response.status, response.responseText));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1854,6 +1918,16 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
|
||||
return sponsorTimesMessage;
|
||||
}
|
||||
|
||||
function addPageListeners(): void {
|
||||
const refreshListners = () => {
|
||||
if (!isVisible(video)) {
|
||||
refreshVideoAttachments();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("yt-navigate-finish", refreshListners);
|
||||
}
|
||||
|
||||
function addHotkeyListener(): void {
|
||||
document.addEventListener("keydown", hotkeyListener);
|
||||
}
|
||||
@@ -1963,3 +2037,24 @@ function showTimeWithoutSkips(skippedDuration: number): void {
|
||||
|
||||
duration.innerText = (durationAfterSkips == null || skippedDuration <= 0) ? "" : " (" + durationAfterSkips + ")";
|
||||
}
|
||||
|
||||
function checkForPreloadedSegment() {
|
||||
const hashParams = getHashParams();
|
||||
|
||||
const segments = hashParams.segments;
|
||||
if (Array.isArray(segments)) {
|
||||
for (const segment of segments) {
|
||||
if (Array.isArray(segment.segment)) {
|
||||
if (!sponsorTimesSubmitting.some((s) => s.segment[0] === segment.segment[0] && s.segment[1] === s.segment[1])) {
|
||||
sponsorTimesSubmitting.push({
|
||||
segment: segment.segment,
|
||||
UUID: utils.generateUserID() as SegmentUUID,
|
||||
category: segment.category ? segment.category : Config.config.defaultCategory,
|
||||
actionType: segment.actionType ? segment.actionType : ActionType.Skip,
|
||||
source: SponsorSourceType.Local
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user