mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-14 15:37:12 +03:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c7e3b5cb3 | ||
|
|
c3cbc44d28 | ||
|
|
6965279d36 | ||
|
|
eb61ad76c1 | ||
|
|
7a1fa601da | ||
|
|
945ac5aad3 | ||
|
|
617f0e25e8 | ||
|
|
ac52a1cb1d | ||
|
|
2cefb9822b | ||
|
|
b81de96277 | ||
|
|
236c95d2f6 | ||
|
|
0f75953ad1 | ||
|
|
0f4c1c50cb | ||
|
|
9e4939e2e3 | ||
|
|
4a217300a2 | ||
|
|
f4822fa587 | ||
|
|
e4d6fdbd97 | ||
|
|
1bd72e4db1 | ||
|
|
307eadc2da | ||
|
|
99014dd113 | ||
|
|
89b7cb0230 | ||
|
|
e3da6c8643 | ||
|
|
136f5e7d61 | ||
|
|
16f24978ff | ||
|
|
091d67e6b3 | ||
|
|
fcf9bdc68e | ||
|
|
af73aabbae | ||
|
|
778b433ad4 | ||
|
|
dc9c08f1fa | ||
|
|
4efa8b464e | ||
|
|
e7d0180e15 |
@@ -24,7 +24,7 @@
|
|||||||
"intro": "https://wiki.sponsor.ajay.app/w/Intermission/Intro_Animation",
|
"intro": "https://wiki.sponsor.ajay.app/w/Intermission/Intro_Animation",
|
||||||
"outro": "https://wiki.sponsor.ajay.app/w/Endcards/Credits",
|
"outro": "https://wiki.sponsor.ajay.app/w/Endcards/Credits",
|
||||||
"preview": "https://wiki.sponsor.ajay.app/w/Preview/Recap",
|
"preview": "https://wiki.sponsor.ajay.app/w/Preview/Recap",
|
||||||
"filler": "https://wiki.sponsor.ajay.app/w/Filler_Tangent",
|
"filler": "https://wiki.sponsor.ajay.app/w/Tangents/Jokes",
|
||||||
"music_offtopic": "https://wiki.sponsor.ajay.app/w/Music:_Non-Music_Section",
|
"music_offtopic": "https://wiki.sponsor.ajay.app/w/Music:_Non-Music_Section",
|
||||||
"poi_highlight": "https://wiki.sponsor.ajay.app/w/Highlight",
|
"poi_highlight": "https://wiki.sponsor.ajay.app/w/Highlight",
|
||||||
"guidelines": "https://wiki.sponsor.ajay.app/w/Guidelines",
|
"guidelines": "https://wiki.sponsor.ajay.app/w/Guidelines",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.10.3",
|
"version": "5.11.5",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"description": "__MSG_Description__",
|
"description": "__MSG_Description__",
|
||||||
"homepage_url": "https://sponsor.ajay.app",
|
"homepage_url": "https://sponsor.ajay.app",
|
||||||
|
|||||||
Submodule maze-utils updated: c92044ab31...67799eebf0
Submodule public/_locales updated: 1b7ef51703...a45cc387ca
@@ -88,7 +88,7 @@ div:hover > #previewbar.sbNotInvidious {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ytp-tooltip.sponsorCategoryTooltipVisible {
|
.ytp-tooltip.sponsorCategoryTooltipVisible:not(.sponsorTooltipHasYTChapters) {
|
||||||
transform: translateY(-1em) !important;
|
transform: translateY(-1em) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -373,6 +373,7 @@ input[type='number'] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch input {
|
.switch input {
|
||||||
|
|||||||
@@ -154,20 +154,6 @@
|
|||||||
<div class="small-description">__MSG_whatForceChannelCheck__</div>
|
<div class="small-description">__MSG_whatForceChannelCheck__</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-type="toggle" data-sync="refetchWhenNotFound">
|
|
||||||
<div class="switch-container">
|
|
||||||
<label class="switch">
|
|
||||||
<input id="refetchWhenNotFound" type="checkbox" checked>
|
|
||||||
<span class="slider round"></span>
|
|
||||||
</label>
|
|
||||||
<label class="switch-label" for="refetchWhenNotFound">
|
|
||||||
__MSG_enableRefetchWhenNotFound__
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="small-description">__MSG_whatRefetchWhenNotFound__</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-type="toggle" data-sync="showCategoryWithoutPermission">
|
<div data-type="toggle" data-sync="showCategoryWithoutPermission">
|
||||||
<div class="switch-container">
|
<div class="switch-container">
|
||||||
<label class="switch">
|
<label class="switch">
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
|
|||||||
chrome.tabs.create({url: chrome.runtime.getURL(request.url)});
|
chrome.tabs.create({url: chrome.runtime.getURL(request.url)});
|
||||||
return false;
|
return false;
|
||||||
case "submitVote":
|
case "submitVote":
|
||||||
submitVote(request.type, request.UUID, request.category).then(callback);
|
submitVote(request.type, request.UUID, request.category, request.videoID).then(callback);
|
||||||
|
|
||||||
//this allows the callback to be called later
|
//this allows the callback to be called later
|
||||||
return true;
|
return true;
|
||||||
@@ -214,7 +214,7 @@ async function unregisterFirefoxContentScript(id: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitVote(type: number, UUID: string, category: string) {
|
async function submitVote(type: number, UUID: string, category: string, videoID: string) {
|
||||||
let userID = Config.config.userID;
|
let userID = Config.config.userID;
|
||||||
|
|
||||||
if (userID == undefined || userID === "undefined") {
|
if (userID == undefined || userID === "undefined") {
|
||||||
@@ -226,7 +226,7 @@ async function submitVote(type: number, UUID: string, category: string) {
|
|||||||
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&videoID=" + videoID + "&userID=" + userID + typeSection);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ interface SBConfig {
|
|||||||
audioNotificationOnSkip: boolean;
|
audioNotificationOnSkip: boolean;
|
||||||
checkForUnlistedVideos: boolean;
|
checkForUnlistedVideos: boolean;
|
||||||
testingServer: boolean;
|
testingServer: boolean;
|
||||||
refetchWhenNotFound: boolean;
|
|
||||||
ytInfoPermissionGranted: boolean;
|
ytInfoPermissionGranted: boolean;
|
||||||
allowExpirements: boolean;
|
allowExpirements: boolean;
|
||||||
showDonationLink: boolean;
|
showDonationLink: boolean;
|
||||||
@@ -312,7 +311,6 @@ const syncDefaults = {
|
|||||||
audioNotificationOnSkip: false,
|
audioNotificationOnSkip: false,
|
||||||
checkForUnlistedVideos: false,
|
checkForUnlistedVideos: false,
|
||||||
testingServer: false,
|
testingServer: false,
|
||||||
refetchWhenNotFound: true,
|
|
||||||
ytInfoPermissionGranted: false,
|
ytInfoPermissionGranted: false,
|
||||||
allowExpirements: true,
|
allowExpirements: true,
|
||||||
showDonationLink: true,
|
showDonationLink: true,
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ let importingChaptersWaiting = false;
|
|||||||
const skipNotices: SkipNotice[] = [];
|
const skipNotices: SkipNotice[] = [];
|
||||||
let upcomingNotice: UpcomingNotice | null = null;
|
let upcomingNotice: UpcomingNotice | null = null;
|
||||||
let activeSkipKeybindElement: ToggleSkippable = null;
|
let activeSkipKeybindElement: ToggleSkippable = null;
|
||||||
let retryFetchTimeout: NodeJS.Timeout = null;
|
|
||||||
let shownSegmentFailedToFetchWarning = false;
|
let shownSegmentFailedToFetchWarning = false;
|
||||||
let selectedSegment: SegmentUUID | null = null;
|
let selectedSegment: SegmentUUID | null = null;
|
||||||
let previewedSegment = false;
|
let previewedSegment = false;
|
||||||
@@ -173,7 +172,6 @@ let popupInitialised = false;
|
|||||||
let submissionNotice: SubmissionNotice = null;
|
let submissionNotice: SubmissionNotice = null;
|
||||||
|
|
||||||
let lastResponseStatus: number;
|
let lastResponseStatus: number;
|
||||||
let retryCount = 0;
|
|
||||||
|
|
||||||
// Contains all of the functions and variables needed by the skip notice
|
// Contains all of the functions and variables needed by the skip notice
|
||||||
const skipNoticeContentContainer: ContentContainer = () => ({
|
const skipNoticeContentContainer: ContentContainer = () => ({
|
||||||
@@ -391,7 +389,6 @@ if (!Config.configSyncListeners.includes(contentConfigUpdateListener)) {
|
|||||||
function resetValues() {
|
function resetValues() {
|
||||||
lastCheckTime = 0;
|
lastCheckTime = 0;
|
||||||
lastCheckVideoTime = -1;
|
lastCheckVideoTime = -1;
|
||||||
retryCount = 0;
|
|
||||||
previewedSegment = false;
|
previewedSegment = false;
|
||||||
firstPlay = true;
|
firstPlay = true;
|
||||||
|
|
||||||
@@ -519,7 +516,7 @@ function handleMobileControlsMutations(): void {
|
|||||||
function getPreviewBarAttachElement(): HTMLElement | null {
|
function getPreviewBarAttachElement(): HTMLElement | null {
|
||||||
const progressElementOptions = [{
|
const progressElementOptions = [{
|
||||||
// For newer mobile YouTube (Sept 2024)
|
// For newer mobile YouTube (Sept 2024)
|
||||||
selector: ".ytChapteredProgressBarHost, .YtProgressBarLineHost, .YtChapteredProgressBarHost",
|
selector: ".ytChapteredProgressBarHost, .ytProgressBarLineHost, .YtProgressBarLineHost, .YtChapteredProgressBarHost",
|
||||||
isVisibleCheck: true
|
isVisibleCheck: true
|
||||||
}, {
|
}, {
|
||||||
// For newer mobile YouTube (May 2024)
|
// For newer mobile YouTube (May 2024)
|
||||||
@@ -760,7 +757,8 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
skippingFunction(currentTime);
|
skippingFunction(currentTime);
|
||||||
} else {
|
} else {
|
||||||
let delayTime = timeUntilSponsor * 1000 * (1 / getVideo().playbackRate);
|
let delayTime = timeUntilSponsor * 1000 * (1 / getVideo().playbackRate);
|
||||||
if (delayTime < (isFirefoxOrSafari() && !isSafari() ? 750 : 300)) {
|
if (delayTime < (isFirefoxOrSafari() && !isSafari() ? 750 : 300)
|
||||||
|
&& shouldAutoSkip(skippingSegments[0])) {
|
||||||
let forceStartIntervalTime: number | null = null;
|
let forceStartIntervalTime: number | null = null;
|
||||||
if (isFirefoxOrSafari() && !isSafari() && delayTime > 300) {
|
if (isFirefoxOrSafari() && !isSafari() && delayTime > 300) {
|
||||||
forceStartIntervalTime = await waitForNextTimeChange();
|
forceStartIntervalTime = await waitForNextTimeChange();
|
||||||
@@ -1241,11 +1239,7 @@ async function sponsorsLookup(keepOldSubmissions = true, ignoreCache = false) {
|
|||||||
if (!isNaN(getVideoDuration())) {
|
if (!isNaN(getVideoDuration())) {
|
||||||
updatePreviewBar();
|
updatePreviewBar();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
retryFetch(404);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
retryFetch(lastResponseStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
importExistingChapters(true);
|
importExistingChapters(true);
|
||||||
@@ -1312,27 +1306,6 @@ async function lockedCategoriesLookup(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryFetch(errorCode: number): void {
|
|
||||||
sponsorDataFound = false;
|
|
||||||
if (!Config.config.refetchWhenNotFound) return;
|
|
||||||
|
|
||||||
if (retryFetchTimeout) clearTimeout(retryFetchTimeout);
|
|
||||||
if ((errorCode !== 404 && retryCount > 1) || (errorCode !== 404 && retryCount > 10)) {
|
|
||||||
// Too many errors (50x), give up
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
retryCount++;
|
|
||||||
|
|
||||||
const delay = errorCode === 404 ? (30000 + Math.random() * 30000) : (2000 + Math.random() * 10000);
|
|
||||||
retryFetchTimeout = setTimeout(() => {
|
|
||||||
if (getVideoID() && sponsorTimes?.length === 0
|
|
||||||
|| sponsorTimes.every((segment) => segment.source !== SponsorSourceType.Server)) {
|
|
||||||
// sponsorsLookup();
|
|
||||||
}
|
|
||||||
}, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only should be used when it is okay to skip a sponsor when in the middle of it
|
* Only should be used when it is okay to skip a sponsor when in the middle of it
|
||||||
*
|
*
|
||||||
@@ -1427,7 +1400,7 @@ function updatePreviewBar(): void {
|
|||||||
showLarger: segment.actionType === ActionType.Poi,
|
showLarger: segment.actionType === ActionType.Poi,
|
||||||
description: segment.description,
|
description: segment.description,
|
||||||
source: segment.source,
|
source: segment.source,
|
||||||
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID?.startsWith(requiredSegment)),
|
requiredSegment: requiredSegment && (segment.UUID === requiredSegment || segment.UUID?.startsWith(requiredSegment) || requiredSegment.startsWith(segment.UUID)),
|
||||||
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
selectedSegment: selectedSegment && segment.UUID === selectedSegment
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1705,7 +1678,7 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
|||||||
counted = true;
|
counted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID + "&videoID=" + getVideoID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2295,7 +2268,8 @@ async function voteAsync(type: number, UUID: SegmentUUID, category?: Category):
|
|||||||
message: "submitVote",
|
message: "submitVote",
|
||||||
type: type,
|
type: type,
|
||||||
UUID: UUID,
|
UUID: UUID,
|
||||||
category: category
|
category: category,
|
||||||
|
videoID: getVideoID()
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
if (response.successType === 1) {
|
if (response.successType === 1) {
|
||||||
// Change the sponsor locally
|
// Change the sponsor locally
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class PreviewBar {
|
|||||||
progressBar: HTMLElement;
|
progressBar: HTMLElement;
|
||||||
|
|
||||||
segments: PreviewBarSegment[] = [];
|
segments: PreviewBarSegment[] = [];
|
||||||
|
hasYouTubeChapters = false;
|
||||||
existingChapters: PreviewBarSegment[] = [];
|
existingChapters: PreviewBarSegment[] = [];
|
||||||
videoDuration = 0;
|
videoDuration = 0;
|
||||||
updateExistingChapters: () => void;
|
updateExistingChapters: () => void;
|
||||||
@@ -136,7 +137,7 @@ class PreviewBar {
|
|||||||
|
|
||||||
// Find the segment at that location, using the shortest if multiple found
|
// Find the segment at that location, using the shortest if multiple found
|
||||||
const [normalSegments, chapterSegments] =
|
const [normalSegments, chapterSegments] =
|
||||||
partition(this.segments.filter((s) => s.source !== SponsorSourceType.YouTube),
|
partition(this.segments,
|
||||||
(segment) => segment.actionType !== ActionType.Chapter);
|
(segment) => segment.actionType !== ActionType.Chapter);
|
||||||
let mainSegment = this.getSmallestSegment(timeInSeconds, normalSegments, "normal");
|
let mainSegment = this.getSmallestSegment(timeInSeconds, normalSegments, "normal");
|
||||||
let secondarySegment = this.getSmallestSegment(timeInSeconds, chapterSegments, "chapter");
|
let secondarySegment = this.getSmallestSegment(timeInSeconds, chapterSegments, "chapter");
|
||||||
@@ -145,13 +146,28 @@ class PreviewBar {
|
|||||||
secondarySegment = this.getSmallestSegment(timeInSeconds, chapterSegments.filter((s) => s !== secondarySegment));
|
secondarySegment = this.getSmallestSegment(timeInSeconds, chapterSegments.filter((s) => s !== secondarySegment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasAYouTubeChapterRemoved = this.hasYouTubeChapters
|
||||||
|
|| (!Config.config.showAutogeneratedChapters && hasAutogeneratedChapters());
|
||||||
|
if (hasAYouTubeChapterRemoved) {
|
||||||
|
// Hide original tooltip if some chapter has been filtered out
|
||||||
|
originalTooltip.style.display = "none";
|
||||||
|
noYoutubeChapters = true;
|
||||||
|
|
||||||
|
originalTooltip.classList.add("sponsorTooltipHasYTChapters");
|
||||||
|
} else {
|
||||||
|
originalTooltip.classList.remove("sponsorTooltipHasYTChapters");
|
||||||
|
}
|
||||||
|
|
||||||
if (mainSegment === null && secondarySegment === null) {
|
if (mainSegment === null && secondarySegment === null) {
|
||||||
this.categoryTooltipContainer.classList.remove(TOOLTIP_VISIBLE_CLASS);
|
if (!hasAYouTubeChapterRemoved) {
|
||||||
originalTooltip.style.removeProperty("display");
|
this.categoryTooltipContainer.classList.remove(TOOLTIP_VISIBLE_CLASS);
|
||||||
|
originalTooltip.style.removeProperty("display");
|
||||||
|
}
|
||||||
} 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) {
|
||||||
this.categoryTooltipContainer.classList.add("sponsorTwoTooltips");
|
this.categoryTooltipContainer.classList.add("sponsorTwoTooltips");
|
||||||
|
originalTooltip.classList.remove("sponsorTooltipHasYTChapters");
|
||||||
} else {
|
} else {
|
||||||
this.categoryTooltipContainer.classList.remove("sponsorTwoTooltips");
|
this.categoryTooltipContainer.classList.remove("sponsorTwoTooltips");
|
||||||
}
|
}
|
||||||
@@ -172,16 +188,16 @@ class PreviewBar {
|
|||||||
originalTooltip.style.removeProperty("display");
|
originalTooltip.style.removeProperty("display");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to prevent overlapping
|
|
||||||
this.categoryTooltip.classList.toggle("ytp-tooltip-text-no-title", noYoutubeChapters);
|
|
||||||
this.chapterTooltip.classList.toggle("ytp-tooltip-text-no-title", noYoutubeChapters);
|
|
||||||
|
|
||||||
// To prevent offset issue
|
// To prevent offset issue
|
||||||
this.categoryTooltip.style.right = titleTooltip.style.right;
|
this.categoryTooltip.style.right = titleTooltip.style.right;
|
||||||
this.chapterTooltip.style.right = titleTooltip.style.right;
|
this.chapterTooltip.style.right = titleTooltip.style.right;
|
||||||
this.categoryTooltip.style.textAlign = titleTooltip.style.textAlign;
|
this.categoryTooltip.style.textAlign = titleTooltip.style.textAlign;
|
||||||
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
|
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to prevent overlapping
|
||||||
|
this.categoryTooltip.classList.toggle("ytp-tooltip-text-no-title", noYoutubeChapters);
|
||||||
|
this.chapterTooltip.classList.toggle("ytp-tooltip-text-no-title", noYoutubeChapters);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +248,7 @@ class PreviewBar {
|
|||||||
set(segments: PreviewBarSegment[], videoDuration: number): void {
|
set(segments: PreviewBarSegment[], videoDuration: number): void {
|
||||||
this.segments = segments ?? [];
|
this.segments = segments ?? [];
|
||||||
this.videoDuration = videoDuration ?? 0;
|
this.videoDuration = videoDuration ?? 0;
|
||||||
|
this.hasYouTubeChapters = segments.some((segment) => segment.source === SponsorSourceType.YouTube);
|
||||||
|
|
||||||
// Remove unnecessary original chapters if submitted replacements exist
|
// Remove unnecessary original chapters if submitted replacements exist
|
||||||
for (const chapter of this.segments.filter((s) => s.actionType === ActionType.Chapter && s.source === SponsorSourceType.Server)) {
|
for (const chapter of this.segments.filter((s) => s.actionType === ActionType.Chapter && s.source === SponsorSourceType.Server)) {
|
||||||
|
|||||||
17
src/popup.ts
17
src/popup.ts
@@ -681,9 +681,22 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
uuidButton.className = "voteButton";
|
uuidButton.className = "voteButton";
|
||||||
uuidButton.src = chrome.runtime.getURL("icons/clipboard.svg");
|
uuidButton.src = chrome.runtime.getURL("icons/clipboard.svg");
|
||||||
uuidButton.title = chrome.i18n.getMessage("copySegmentID");
|
uuidButton.title = chrome.i18n.getMessage("copySegmentID");
|
||||||
uuidButton.addEventListener("click", () => {
|
uuidButton.addEventListener("click", async () => {
|
||||||
copyToClipboard(UUID);
|
|
||||||
const stopAnimation = AnimationUtils.applyLoadingAnimation(uuidButton, 0.3);
|
const stopAnimation = AnimationUtils.applyLoadingAnimation(uuidButton, 0.3);
|
||||||
|
|
||||||
|
if (UUID.length > 60) {
|
||||||
|
copyToClipboard(UUID);
|
||||||
|
} else {
|
||||||
|
const segmentIDData = await asyncRequestToServer("GET", "/api/segmentID", {
|
||||||
|
UUID: UUID,
|
||||||
|
videoID: currentVideoID
|
||||||
|
});
|
||||||
|
|
||||||
|
if (segmentIDData.ok && segmentIDData.responseText) {
|
||||||
|
copyToClipboard(segmentIDData.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
15
src/utils.ts
15
src/utils.ts
@@ -5,6 +5,7 @@ import { getHash, HashedValue } from "../maze-utils/src/hash";
|
|||||||
import { waitFor } from "../maze-utils/src";
|
import { waitFor } from "../maze-utils/src";
|
||||||
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
import { findValidElementFromSelector } from "../maze-utils/src/dom";
|
||||||
import { isSafari } from "../maze-utils/src/config";
|
import { isSafari } from "../maze-utils/src/config";
|
||||||
|
import { asyncRequestToServer } from "./utils/requests";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
|
|
||||||
@@ -198,7 +199,7 @@ export default class Utils {
|
|||||||
|
|
||||||
getSponsorIndexFromUUID(sponsorTimes: SponsorTime[], UUID: string): number {
|
getSponsorIndexFromUUID(sponsorTimes: SponsorTime[], UUID: string): number {
|
||||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||||
if (sponsorTimes[i].UUID === UUID) {
|
if (sponsorTimes[i].UUID && (sponsorTimes[i].UUID.startsWith(UUID) || UUID.startsWith(sponsorTimes[i].UUID))) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +283,17 @@ export default class Utils {
|
|||||||
if ((chrome.extension.inIncognitoContext && !Config.config.trackDownvotesInPrivate)
|
if ((chrome.extension.inIncognitoContext && !Config.config.trackDownvotesInPrivate)
|
||||||
|| !Config.config.trackDownvotes) return;
|
|| !Config.config.trackDownvotes) return;
|
||||||
|
|
||||||
|
if (segmentUUID.length < 60) {
|
||||||
|
const segmentIDData = await asyncRequestToServer("GET", "/api/segmentID", {
|
||||||
|
UUID: segmentUUID,
|
||||||
|
videoID
|
||||||
|
});
|
||||||
|
|
||||||
|
if (segmentIDData.ok && segmentIDData.responseText) {
|
||||||
|
segmentUUID = segmentIDData.responseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
const hashedVideoID = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||||
const UUIDHash = await getHash(segmentUUID, 1);
|
const UUIDHash = await getHash(segmentUUID, 1);
|
||||||
|
|
||||||
@@ -308,6 +320,7 @@ export default class Utils {
|
|||||||
|
|
||||||
allDownvotes[hashedVideoID] = currentVideoData;
|
allDownvotes[hashedVideoID] = currentVideoData;
|
||||||
}
|
}
|
||||||
|
console.log(allDownvotes)
|
||||||
|
|
||||||
const entries = Object.entries(allDownvotes);
|
const entries = Object.entries(allDownvotes);
|
||||||
if (entries.length > 10000) {
|
if (entries.length > 10000) {
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse>
|
|||||||
const extraRequestData: Record<string, unknown> = {};
|
const extraRequestData: Record<string, unknown> = {};
|
||||||
const hashParams = getHashParams();
|
const hashParams = getHashParams();
|
||||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||||
|
|
||||||
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||||
|
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix];
|
||||||
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||||
categories: CompileConfig.categoryList,
|
categories: CompileConfig.categoryList,
|
||||||
actionTypes: ActionTypes,
|
actionTypes: ActionTypes,
|
||||||
|
trimUUIDs: hasDownvotedSegments ? null : 5,
|
||||||
...extraRequestData
|
...extraRequestData
|
||||||
}, {
|
}, {
|
||||||
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
|
import { isOnInvidious, parseYouTubeVideoIDFromURL } from "../../maze-utils/src/video";
|
||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import { getVideoLabel } from "./videoLabels";
|
import { getHasStartSegment, getVideoLabel } from "./videoLabels";
|
||||||
import { getThumbnailSelector, setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
|
import { getThumbnailSelector, setThumbnailListener } from "../../maze-utils/src/thumbnailManagement";
|
||||||
import { VideoID } from "../types";
|
import { VideoID } from "../types";
|
||||||
import { getSegmentsForVideo } from "./segmentData";
|
import { getSegmentsForVideo } from "./segmentData";
|
||||||
@@ -58,10 +58,27 @@ function thumbnailHoverListener(e: MouseEvent) {
|
|||||||
if (!thumbnail) return;
|
if (!thumbnail) return;
|
||||||
|
|
||||||
// Pre-fetch data for this video
|
// Pre-fetch data for this video
|
||||||
const videoID = extractVideoID(thumbnail);
|
let fetched = false;
|
||||||
if (videoID) {
|
const preFetch = async () => {
|
||||||
void getSegmentsForVideo(videoID, false);
|
fetched = true;
|
||||||
}
|
const videoID = extractVideoID(thumbnail);
|
||||||
|
if (videoID && await getHasStartSegment(videoID)) {
|
||||||
|
void getSegmentsForVideo(videoID, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const timeout = setTimeout(preFetch, 100);
|
||||||
|
const onMouseDown = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (!fetched) {
|
||||||
|
preFetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
e.target.addEventListener("mousedown", onMouseDown, { once: true });
|
||||||
|
e.target.addEventListener("mouseleave", () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
e.target.removeEventListener("mousedown", onMouseDown);
|
||||||
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLink(thumbnail: HTMLImageElement): HTMLAnchorElement | null {
|
function getLink(thumbnail: HTMLImageElement): HTMLAnchorElement | null {
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ import { asyncRequestToServer } from "./requests";
|
|||||||
|
|
||||||
const utils = new Utils();
|
const utils = new Utils();
|
||||||
|
|
||||||
|
interface VideoLabelsCacheData {
|
||||||
|
category: Category;
|
||||||
|
hasStartSegment: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LabelCacheEntry {
|
export interface LabelCacheEntry {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
videos: Record<VideoID, Category>;
|
videos: Record<VideoID, VideoLabelsCacheData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelCache: Record<string, LabelCacheEntry> = {};
|
const labelCache: Record<string, LabelCacheEntry> = {};
|
||||||
@@ -21,7 +26,7 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
return cachedEntry;
|
return cachedEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`);
|
const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}?hasStartSegment=true`);
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
// No video labels or server down
|
// No video labels or server down
|
||||||
labelCache[hashPrefix] = {
|
labelCache[hashPrefix] = {
|
||||||
@@ -36,7 +41,10 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
|
|
||||||
const newEntry: LabelCacheEntry = {
|
const newEntry: LabelCacheEntry = {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
videos: Object.fromEntries(data.map(video => [video.videoID, video.segments[0].category])),
|
videos: Object.fromEntries(data.map(video => [video.videoID, {
|
||||||
|
category: video.segments[0]?.category,
|
||||||
|
hasStartSegment: video.hasStartSegment
|
||||||
|
}])),
|
||||||
};
|
};
|
||||||
labelCache[hashPrefix] = newEntry;
|
labelCache[hashPrefix] = newEntry;
|
||||||
|
|
||||||
@@ -55,11 +63,11 @@ async function getLabelHashBlock(hashPrefix: string): Promise<LabelCacheEntry |
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getVideoLabel(videoID: VideoID): Promise<Category | null> {
|
export async function getVideoLabel(videoID: VideoID): Promise<Category | null> {
|
||||||
const prefix = (await getHash(videoID, 1)).slice(0, 3);
|
const prefix = (await getHash(videoID, 1)).slice(0, 4);
|
||||||
const result = await getLabelHashBlock(prefix);
|
const result = await getLabelHashBlock(prefix);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
const category = result.videos[videoID];
|
const category = result.videos[videoID]?.category;
|
||||||
if (category && utils.getCategorySelection(category).option !== CategorySkipOption.Disabled) {
|
if (category && utils.getCategorySelection(category).option !== CategorySkipOption.Disabled) {
|
||||||
return category;
|
return category;
|
||||||
} else {
|
} else {
|
||||||
@@ -67,5 +75,16 @@ export async function getVideoLabel(videoID: VideoID): Promise<Category | null>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getHasStartSegment(videoID: VideoID): Promise<boolean | null> {
|
||||||
|
const prefix = (await getHash(videoID, 1)).slice(0, 4);
|
||||||
|
const result = await getLabelHashBlock(prefix);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result?.videos[videoID]?.hasStartSegment ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user