mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-13 15:07:02 +03:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f8447ec6b | ||
|
|
e6db5b43ff | ||
|
|
6566c129c7 | ||
|
|
e7f4be2d57 | ||
|
|
9d04482d19 | ||
|
|
7c661f8e67 | ||
|
|
0b7a2fd197 | ||
|
|
3382d8a500 | ||
|
|
5d871d5fe7 | ||
|
|
b7c5737a95 | ||
|
|
0cca1c3566 | ||
|
|
e0fe0fad67 | ||
|
|
c0bc068a18 | ||
|
|
7cb413db15 | ||
|
|
fdf1a6acf9 | ||
|
|
b533c6c1c8 | ||
|
|
926423db5c | ||
|
|
e7d55d2bac | ||
|
|
16f27e5c5c | ||
|
|
88dc8db6e7 | ||
|
|
c69a574379 | ||
|
|
516d624f16 | ||
|
|
a662c3e04f | ||
|
|
985910cbf6 | ||
|
|
feae86f6ea | ||
|
|
1f96e3b117 | ||
|
|
b3b5d46e4e | ||
|
|
c996680a58 | ||
|
|
ade4654ae0 | ||
|
|
4e9e6282f6 | ||
|
|
783326afca | ||
|
|
8dfd06919b | ||
|
|
50ee690717 | ||
|
|
74aebd32a7 | ||
|
|
e489d0f913 | ||
|
|
814df46521 |
@@ -75,4 +75,4 @@ Icons made by:
|
||||
|
||||
### License
|
||||
|
||||
This project is licensed under GNU LGPL v3 or any later version
|
||||
This project is licensed under GNU GPL v3 or any later version
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "SponsorBlock",
|
||||
"version": "5.5",
|
||||
"version": "5.5.6",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"homepage_url": "https://sponsor.ajay.app",
|
||||
|
||||
Submodule maze-utils updated: 27db39e39b...9fbde12534
Submodule public/_locales updated: 840b5c3ca2...3f17e35086
@@ -123,7 +123,7 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
// If there is no userID, then it is the first install.
|
||||
if (!userID && !Config.local.alreadyInstalled){
|
||||
//open up the install page
|
||||
chrome.tabs.create({url: chrome.extension.getURL("/help/index.html")});
|
||||
chrome.tabs.create({url: chrome.runtime.getURL("/help/index.html")});
|
||||
|
||||
//generate a userID
|
||||
const newUserID = generateUserID();
|
||||
@@ -137,7 +137,7 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
|
||||
if (Config.config.supportInvidious) {
|
||||
if (!(await utils.containsInvidiousPermission())) {
|
||||
chrome.tabs.create({url: chrome.extension.getURL("/permissions/index.html")});
|
||||
chrome.tabs.create({url: chrome.runtime.getURL("/permissions/index.html")});
|
||||
}
|
||||
}
|
||||
}, 1500);
|
||||
@@ -160,8 +160,8 @@ async function registerFirefoxContentScript(options: Registration) {
|
||||
ids: [options.id]
|
||||
}).catch(() => []);
|
||||
|
||||
if (existingRegistrations.length > 0
|
||||
&& existingRegistrations[0].matches.every((match) => options.matches.includes(match))) {
|
||||
if (existingRegistrations && existingRegistrations.length > 0
|
||||
&& options.matches.every((match) => existingRegistrations[0].matches.includes(match))) {
|
||||
// No need to register another script, already registered
|
||||
return;
|
||||
}
|
||||
@@ -222,27 +222,35 @@ async function submitVote(type: number, UUID: string, category: string) {
|
||||
|
||||
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
|
||||
|
||||
//publish this vote
|
||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
||||
|
||||
if (response.ok) {
|
||||
return {
|
||||
successType: 1,
|
||||
responseText: await response.text()
|
||||
};
|
||||
} else if (response.status == 405) {
|
||||
//duplicate vote
|
||||
return {
|
||||
successType: 0,
|
||||
statusCode: response.status,
|
||||
responseText: await response.text()
|
||||
};
|
||||
} else {
|
||||
//error while connect
|
||||
try {
|
||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
||||
|
||||
if (response.ok) {
|
||||
return {
|
||||
successType: 1,
|
||||
responseText: await response.text()
|
||||
};
|
||||
} else if (response.status == 405) {
|
||||
//duplicate vote
|
||||
return {
|
||||
successType: 0,
|
||||
statusCode: response.status,
|
||||
responseText: await response.text()
|
||||
};
|
||||
} else {
|
||||
//error while connect
|
||||
return {
|
||||
successType: -1,
|
||||
statusCode: response.status,
|
||||
responseText: await response.text()
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {
|
||||
successType: -1,
|
||||
statusCode: response.status,
|
||||
responseText: await response.text()
|
||||
statusCode: -1,
|
||||
responseText: ""
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||
import { VoteResponse } from "../messageTypes";
|
||||
import { AnimationUtils } from "../utils/animationUtils";
|
||||
import { AnimationUtils } from "../../maze-utils/src/animationUtils";
|
||||
import { Tooltip } from "../render/Tooltip";
|
||||
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||
|
||||
@@ -53,7 +53,7 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
|
||||
|
||||
<span className="sponsorBlockCategoryPillTitleSection">
|
||||
<img className="sponsorSkipLogo sponsorSkipObject"
|
||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
</img>
|
||||
|
||||
{
|
||||
@@ -86,7 +86,7 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
|
||||
)}
|
||||
|
||||
{/* Close Button */}
|
||||
<img src={chrome.extension.getURL("icons/close.png")}
|
||||
<img src={chrome.runtime.getURL("icons/close.png")}
|
||||
className="categoryPillClose"
|
||||
onClick={() => {
|
||||
this.setState({ show: false });
|
||||
|
||||
@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||
import { VoteResponse } from "../messageTypes";
|
||||
import { AnimationUtils } from "../utils/animationUtils";
|
||||
import { AnimationUtils } from "../../maze-utils/src/animationUtils";
|
||||
import { Tooltip } from "../render/Tooltip";
|
||||
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
|
||||
|
||||
|
||||
{/* Close button */}
|
||||
<img src={chrome.extension.getURL("icons/close.png")}
|
||||
<img src={chrome.runtime.getURL("icons/close.png")}
|
||||
className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
|
||||
+ (this.props.biggerCloseButton ? " biggerCloseButton" : "")}
|
||||
onClick={() => this.close()}>
|
||||
|
||||
@@ -34,7 +34,7 @@ export interface SponsorTimeEditState {
|
||||
chapterNameSelectorHovering: boolean;
|
||||
}
|
||||
|
||||
const categoryNamesGrams: string[] = [].concat(...CompileConfig.categoryList.filter((name) => name !== "chapter")
|
||||
const categoryNamesGrams: string[] = [].concat(...CompileConfig.categoryList.filter((name) => !["chapter", "intro"].includes(name))
|
||||
.map((name) => chrome.i18n.getMessage("category_" + name).split(/\/|\s|-/)));
|
||||
|
||||
class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> {
|
||||
@@ -81,13 +81,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
|
||||
componentDidMount(): void {
|
||||
// Prevent inputs from triggering key events
|
||||
document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', function (event) {
|
||||
event.stopPropagation();
|
||||
document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// Prevent scrolling while changing times
|
||||
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', function (event) {
|
||||
event.preventDefault();
|
||||
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', (e) => {
|
||||
if (this.state.editing) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, {passive: false});
|
||||
|
||||
// Add as a config listener
|
||||
@@ -221,7 +223,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
target="_blank" rel="noreferrer">
|
||||
<img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix}
|
||||
className="helpButton"
|
||||
src={chrome.extension.getURL("icons/help.svg")}
|
||||
src={chrome.runtime.getURL("icons/help.svg")}
|
||||
title={chrome.i18n.getMessage("categoryGuidelines")} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface SubmissionNoticeProps {
|
||||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: ContentContainer;
|
||||
|
||||
callback: () => unknown;
|
||||
callback: () => Promise<boolean>;
|
||||
|
||||
closeListener: () => void;
|
||||
}
|
||||
@@ -69,6 +69,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
this.videoObserver.observe(this.contentContainer().v, {
|
||||
attributes: true
|
||||
});
|
||||
|
||||
// Prevent zooming while changing times
|
||||
document.getElementById("sponsorSkipNoticeMiddleRow" + this.state.idSuffix).addEventListener('wheel', function (event) {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}, {passive: false});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
@@ -100,7 +107,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
onClick={() => this.sortSegments()}
|
||||
title={chrome.i18n.getMessage("sortSegments")}
|
||||
key="sortButton"
|
||||
src={chrome.extension.getURL("icons/sort.svg")}>
|
||||
src={chrome.runtime.getURL("icons/sort.svg")}>
|
||||
</img>;
|
||||
const exportButton =
|
||||
<img id={"sponsorSkipExportButton" + this.state.idSuffix}
|
||||
@@ -108,7 +115,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
onClick={() => this.exportSegments()}
|
||||
title={chrome.i18n.getMessage("exportSegments")}
|
||||
key="exportButton"
|
||||
src={chrome.extension.getURL("icons/export.svg")}>
|
||||
src={chrome.runtime.getURL("icons/export.svg")}>
|
||||
</img>;
|
||||
return (
|
||||
<NoticeComponent noticeTitle={this.state.noticeTitle}
|
||||
@@ -232,9 +239,11 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
||||
}
|
||||
}
|
||||
|
||||
this.props.callback();
|
||||
|
||||
this.cancel();
|
||||
this.props.callback().then((success) => {
|
||||
if (success) {
|
||||
this.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sortSegments(): void {
|
||||
|
||||
@@ -158,7 +158,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
||||
});
|
||||
}
|
||||
|
||||
Config.forceLocalUpdate("categorySelections");
|
||||
Config.forceSyncUpdate("categorySelections");
|
||||
}
|
||||
|
||||
getCategorySkipOptions(): JSX.Element[] {
|
||||
|
||||
@@ -26,7 +26,7 @@ import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||
import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isVisible } from "./utils/pageUtils";
|
||||
import { CategoryPill } from "./render/CategoryPill";
|
||||
import { AnimationUtils } from "./utils/animationUtils";
|
||||
import { AnimationUtils } from "../maze-utils/src/animationUtils";
|
||||
import { GenericUtils } from "./utils/genericUtils";
|
||||
import { logDebug, logWarn } from "./utils/logger";
|
||||
import { importTimes } from "./utils/exporter";
|
||||
@@ -454,7 +454,9 @@ function videoIDChange(): void {
|
||||
}
|
||||
|
||||
function handleMobileControlsMutations(): void {
|
||||
if (!chrome.runtime?.id) return;
|
||||
// Don't update while scrubbing
|
||||
if (!chrome.runtime?.id
|
||||
|| document.querySelector(".YtProgressBarProgressBarPlayheadDotInDragging")) return;
|
||||
|
||||
updateVisibilityOfPlayerControlsButton();
|
||||
|
||||
@@ -770,6 +772,7 @@ function getVirtualTime(): number {
|
||||
|
||||
function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
|
||||
const checkFunction = (segment) => segment.actionType === ActionType.Mute
|
||||
&& segment.hidden === SponsorHideType.Visible
|
||||
&& segment.segment[0] <= currentTime
|
||||
&& (segment.segment[1] > currentTime || (includeOverlap && segment.segment[1] + 0.02 > currentTime));
|
||||
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
||||
@@ -1608,6 +1611,9 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
||||
}
|
||||
|
||||
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
||||
} else if (!previewedSegment && sponsorTimesSubmitting.some((s) => s.segment === segment.segment)) {
|
||||
// Count that as a previewed segment
|
||||
previewedSegment = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1768,7 +1774,7 @@ function createButton(baseID: string, title: string, callback: () => void, image
|
||||
newButton.draggable = isDraggable;
|
||||
newButtonImage.id = baseID + "Image";
|
||||
newButtonImage.className = "playerButtonImage";
|
||||
newButtonImage.src = chrome.extension.getURL("icons/" + imageName);
|
||||
newButtonImage.src = chrome.runtime.getURL("icons/" + imageName);
|
||||
|
||||
// Append image to button
|
||||
newButton.appendChild(newButtonImage);
|
||||
@@ -1867,10 +1873,10 @@ function updateEditButtonsOnPlayer(): void {
|
||||
|
||||
if (buttonsEnabled) {
|
||||
if (creatingSegment) {
|
||||
playerButtons.startSegment.image.src = chrome.extension.getURL("icons/PlayerStopIconSponsorBlocker.svg");
|
||||
playerButtons.startSegment.image.src = chrome.runtime.getURL("icons/PlayerStopIconSponsorBlocker.svg");
|
||||
playerButtons.startSegment.button.setAttribute("title", chrome.i18n.getMessage("sponsorEnd"));
|
||||
} else {
|
||||
playerButtons.startSegment.image.src = chrome.extension.getURL("icons/PlayerStartIconSponsorBlocker.svg");
|
||||
playerButtons.startSegment.image.src = chrome.runtime.getURL("icons/PlayerStartIconSponsorBlocker.svg");
|
||||
playerButtons.startSegment.button.setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
|
||||
}
|
||||
}
|
||||
@@ -1983,6 +1989,9 @@ function updateSponsorTimesSubmitting(getFromConfig = true) {
|
||||
}
|
||||
|
||||
if (sponsorTimesSubmitting.length > 0) {
|
||||
// Assume they already previewed a segment
|
||||
previewedSegment = true;
|
||||
|
||||
importExistingChapters(true);
|
||||
}
|
||||
}
|
||||
@@ -2048,7 +2057,7 @@ function openInfoMenu() {
|
||||
}
|
||||
}
|
||||
});
|
||||
frame.src = chrome.extension.getURL("popup.html");
|
||||
frame.src = chrome.runtime.getURL("popup.html");
|
||||
popup.appendChild(frame);
|
||||
|
||||
const elemHasChild = (elements: NodeListOf<HTMLElement>): Element => {
|
||||
@@ -2257,22 +2266,26 @@ function submitSegments() {
|
||||
|
||||
//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() {
|
||||
async function sendSubmitMessage(): Promise<boolean> {
|
||||
// check if all segments are full video
|
||||
const onlyFullVideo = sponsorTimesSubmitting.every((segment) => segment.actionType === ActionType.Full);
|
||||
// Block if submitting on a running livestream or premiere
|
||||
if (!onlyFullVideo && (getIsLivePremiere() || isVisible(document.querySelector(".ytp-live-badge")))) {
|
||||
alert(chrome.i18n.getMessage("liveOrPremiere"));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!previewedSegment) {
|
||||
if (!previewedSegment
|
||||
&& !sponsorTimesSubmitting.every((segment) =>
|
||||
[ActionType.Full, ActionType.Chapter, ActionType.Poi].includes(segment.actionType)
|
||||
|| segment.segment[1] >= getVideo()?.duration
|
||||
|| segment.segment[0] === 0)) {
|
||||
alert(`${chrome.i18n.getMessage("previewSegmentRequired")} ${keybindToString(Config.config.previewKeybind)}`);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add loading animation
|
||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
|
||||
playerButtons.submit.image.src = chrome.runtime.getURL("icons/PlayerUploadIconSponsorBlocker.svg");
|
||||
const stopAnimation = AnimationUtils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
|
||||
|
||||
//check if a sponsor exceeds the duration of the video
|
||||
@@ -2294,7 +2307,7 @@ async function sendSubmitMessage() {
|
||||
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||
getSegmentsMessage(sponsorTimesSubmitting);
|
||||
|
||||
if(!confirm(confirmShort)) return;
|
||||
if(!confirm(confirmShort)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2344,10 +2357,12 @@ async function sendSubmitMessage() {
|
||||
if (fullVideoSegment) {
|
||||
categoryPill?.setSegment(fullVideoSegment);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Show that the upload failed
|
||||
playerButtons.submit.button.style.animation = "unset";
|
||||
playerButtons.submit.image.src = chrome.extension.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
||||
playerButtons.submit.image.src = chrome.runtime.getURL("icons/PlayerUploadFailedIconSponsorBlocker.svg");
|
||||
|
||||
if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a tip from a moderator.")) {
|
||||
openWarningDialog(skipNoticeContentContainer);
|
||||
@@ -2355,6 +2370,8 @@ async function sendSubmitMessage() {
|
||||
alert(getErrorMessage(response.status, response.responseText));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//get the message that visually displays the video times
|
||||
@@ -2380,6 +2397,8 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
|
||||
}
|
||||
|
||||
function updateActiveSegment(currentTime: number): void {
|
||||
previewBar?.updateChapterText(sponsorTimes, sponsorTimesSubmitting, currentTime);
|
||||
|
||||
chrome.runtime.sendMessage({
|
||||
message: "time",
|
||||
time: currentTime
|
||||
@@ -2531,7 +2550,7 @@ function addCSS() {
|
||||
|
||||
fileref.rel = "stylesheet";
|
||||
fileref.type = "text/css";
|
||||
fileref.href = chrome.extension.getURL(file);
|
||||
fileref.href = chrome.runtime.getURL(file);
|
||||
|
||||
head.appendChild(fileref);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceT
|
||||
import { partition } from "../utils/arrayUtils";
|
||||
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
|
||||
import { normalizeChapterName } from "../utils/exporter";
|
||||
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
||||
import { findValidElement } from "../../maze-utils/src/dom";
|
||||
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
||||
|
||||
@@ -125,34 +124,11 @@ class PreviewBar {
|
||||
mouseOnSeekBar = false;
|
||||
});
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer) return;
|
||||
seekBar.addEventListener("mousemove", (e: MouseEvent) => {
|
||||
if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer || !chrome.runtime?.id) return;
|
||||
|
||||
// Only care about mutations to time tooltip
|
||||
if (!mutations.some((mutation) => (mutation.target as HTMLElement).classList.contains("ytp-tooltip-text"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tooltipTextElements = tooltipTextWrapper.querySelectorAll(".ytp-tooltip-text");
|
||||
let timeInSeconds: number | null = null;
|
||||
let noYoutubeChapters = false;
|
||||
|
||||
for (const tooltipTextElement of tooltipTextElements) {
|
||||
if (tooltipTextElement.classList.contains('ytp-tooltip-text-no-title')) noYoutubeChapters = true;
|
||||
|
||||
const tooltipText = tooltipTextElement.textContent;
|
||||
if (tooltipText === null || tooltipText.length === 0) continue;
|
||||
|
||||
timeInSeconds = getFormattedTimeToSeconds(tooltipText);
|
||||
|
||||
if (timeInSeconds !== null) break;
|
||||
}
|
||||
|
||||
if (timeInSeconds === null) {
|
||||
originalTooltip.style.removeProperty("display");
|
||||
|
||||
return;
|
||||
}
|
||||
let noYoutubeChapters = !!tooltipTextWrapper.querySelector(".ytp-tooltip-text.ytp-tooltip-text-no-title");
|
||||
const timeInSeconds = this.decimalToTime((e.clientX - seekBar.getBoundingClientRect().x) / seekBar.clientWidth);
|
||||
|
||||
// Find the segment at that location, using the shortest if multiple found
|
||||
const [normalSegments, chapterSegments] =
|
||||
@@ -198,15 +174,6 @@ class PreviewBar {
|
||||
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(tooltipTextWrapper, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
addCleanupListener(() => {
|
||||
observer.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
||||
@@ -307,6 +274,7 @@ class PreviewBar {
|
||||
return (b[1] - b[0]) - (a[1] - a[0]);
|
||||
});
|
||||
for (const segment of sortedSegments) {
|
||||
if (segment.actionType === ActionType.Chapter) continue;
|
||||
const bar = this.createBar(segment);
|
||||
|
||||
this.container.appendChild(bar);
|
||||
@@ -346,7 +314,7 @@ class PreviewBar {
|
||||
bar.style.left = this.timeToPercentage(startTime);
|
||||
|
||||
if (duration > 0) {
|
||||
bar.style.right = this.timeToPercentage(this.videoDuration - endTime);
|
||||
bar.style.right = this.timeToRightPercentage(endTime);
|
||||
}
|
||||
if (this.chapterFilter(barSegment) && segment[1] < this.videoDuration) {
|
||||
bar.style.marginRight = `${this.chapterMargin}px`;
|
||||
@@ -919,7 +887,22 @@ class PreviewBar {
|
||||
return `${this.timeToDecimal(time) * 100}%`
|
||||
}
|
||||
|
||||
timeToRightPercentage(time: number): string {
|
||||
return `${(1 - this.timeToDecimal(time)) * 100}%`
|
||||
}
|
||||
|
||||
timeToDecimal(time: number): number {
|
||||
return this.decimalTimeConverter(time, true);
|
||||
}
|
||||
|
||||
decimalToTime(decimal: number): number {
|
||||
return this.decimalTimeConverter(decimal, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decimal to time or time to decimal
|
||||
*/
|
||||
decimalTimeConverter(value: number, isTime: boolean): number {
|
||||
if (this.originalChapterBarBlocks?.length > 1 && this.existingChapters.length === this.originalChapterBarBlocks?.length) {
|
||||
// Parent element to still work when display: none
|
||||
const totalPixels = this.originalChapterBar.parentElement.clientWidth;
|
||||
@@ -929,8 +912,9 @@ class PreviewBar {
|
||||
const chapterElement = this.originalChapterBarBlocks[i];
|
||||
const widthPixels = parseFloat(chapterElement.style.width.replace("px", ""));
|
||||
|
||||
if (time >= this.existingChapters[i].segment[1]) {
|
||||
const marginPixels = chapterElement.style.marginRight ? parseFloat(chapterElement.style.marginRight.replace("px", "")) : 0;
|
||||
const marginPixels = chapterElement.style.marginRight ? parseFloat(chapterElement.style.marginRight.replace("px", "")) : 0;
|
||||
if ((isTime && value >= this.existingChapters[i].segment[1])
|
||||
|| (!isTime && value >= (pixelOffset + widthPixels + marginPixels) / totalPixels)) {
|
||||
pixelOffset += widthPixels + marginPixels;
|
||||
lastCheckedChapter = i;
|
||||
} else {
|
||||
@@ -944,13 +928,22 @@ class PreviewBar {
|
||||
const latestWidth = parseFloat(this.originalChapterBarBlocks[lastCheckedChapter + 1].style.width.replace("px", ""));
|
||||
const latestChapterDuration = latestChapter.segment[1] - latestChapter.segment[0];
|
||||
|
||||
const percentageInCurrentChapter = (time - latestChapter.segment[0]) / latestChapterDuration;
|
||||
const sizeOfCurrentChapter = latestWidth / totalPixels;
|
||||
return Math.min(1, ((pixelOffset / totalPixels) + (percentageInCurrentChapter * sizeOfCurrentChapter)));
|
||||
if (isTime) {
|
||||
const percentageInCurrentChapter = (value - latestChapter.segment[0]) / latestChapterDuration;
|
||||
const sizeOfCurrentChapter = latestWidth / totalPixels;
|
||||
return Math.min(1, ((pixelOffset / totalPixels) + (percentageInCurrentChapter * sizeOfCurrentChapter)));
|
||||
} else {
|
||||
const percentageInCurrentChapter = (value * totalPixels - pixelOffset) / latestWidth;
|
||||
return Math.max(0, latestChapter.segment[0] + (percentageInCurrentChapter * latestChapterDuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(1, time / this.videoDuration);
|
||||
if (isTime) {
|
||||
return Math.min(1, value / this.videoDuration);
|
||||
} else {
|
||||
return Math.max(0, value * this.videoDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Config from "../config";
|
||||
import { SegmentUUID, SponsorTime } from "../types";
|
||||
import { getSkippingText } from "../utils/categoryUtils";
|
||||
import { AnimationUtils } from "../utils/animationUtils";
|
||||
import { AnimationUtils } from "../../maze-utils/src/animationUtils";
|
||||
import { keybindToString } from "../../maze-utils/src/config";
|
||||
import { isMobileControlsOpen } from "../utils/mobileUtils";
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
VoteResponse,
|
||||
} from "./messageTypes";
|
||||
import { showDonationLink } from "./utils/configUtils";
|
||||
import { AnimationUtils } from "./utils/animationUtils";
|
||||
import { AnimationUtils } from "../maze-utils/src/animationUtils";
|
||||
import { shortCategoryName } from "./utils/categoryUtils";
|
||||
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||
import { exportTimes } from "./utils/exporter";
|
||||
|
||||
@@ -59,7 +59,7 @@ export class RectangleTooltip {
|
||||
className="sponsorBlockRectangleTooltip" >
|
||||
<div>
|
||||
<img className="sponsorSkipLogo sponsorSkipObject"
|
||||
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
|
||||
</img>
|
||||
<span className="sponsorSkipObject">
|
||||
{this.text + (props.link ? ". " : "")}
|
||||
|
||||
@@ -11,7 +11,7 @@ class SubmissionNotice {
|
||||
// Contains functions and variables from the content script needed by the skip notice
|
||||
contentContainer: () => unknown;
|
||||
|
||||
callback: () => unknown;
|
||||
callback: () => Promise<boolean>;
|
||||
|
||||
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
|
||||
|
||||
@@ -19,7 +19,7 @@ class SubmissionNotice {
|
||||
|
||||
root: Root;
|
||||
|
||||
constructor(contentContainer: ContentContainer, callback: () => unknown) {
|
||||
constructor(contentContainer: ContentContainer, callback: () => Promise<boolean>) {
|
||||
this.noticeRef = React.createRef();
|
||||
|
||||
this.contentContainer = contentContainer;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Starts a spinning animation and returns a function to be called when it should be stopped
|
||||
* The callback will be called when the animation is finished
|
||||
* It waits until a full rotation is complete
|
||||
*/
|
||||
function applyLoadingAnimation(element: HTMLElement, time: number, callback?: () => void): () => Promise<void> {
|
||||
element.style.animation = `rotate ${time}s 0s infinite`;
|
||||
|
||||
return async () => new Promise((resolve) => {
|
||||
// Make the animation finite
|
||||
element.style.animation = `rotate ${time}s`;
|
||||
|
||||
// When the animation is over, hide the button
|
||||
const animationEndListener = () => {
|
||||
if (callback) callback();
|
||||
|
||||
element.style.animation = "none";
|
||||
|
||||
element.removeEventListener("animationend", animationEndListener);
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
element.addEventListener("animationend", animationEndListener);
|
||||
});
|
||||
}
|
||||
|
||||
function setupCustomHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): { hide: () => void; show: () => void } {
|
||||
if (enabled) element.classList.add("autoHiding");
|
||||
element.classList.add("sbhidden");
|
||||
element.classList.add("animationDone");
|
||||
if (!rightSlide) element.classList.add("autoHideLeft");
|
||||
|
||||
let mouseEntered = false;
|
||||
|
||||
return {
|
||||
hide: () => {
|
||||
mouseEntered = false;
|
||||
if (element.classList.contains("autoHiding")) {
|
||||
element.classList.add("sbhidden");
|
||||
}
|
||||
},
|
||||
show: () => {
|
||||
mouseEntered = true;
|
||||
element.classList.remove("animationDone");
|
||||
|
||||
// Wait for next event loop
|
||||
setTimeout(() => {
|
||||
if (mouseEntered) element.classList.remove("sbhidden")
|
||||
}, 10);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setupAutoHideAnimation(element: Element, container: Element, enabled = true, rightSlide = true): void {
|
||||
const { hide, show } = this.setupCustomHideAnimation(element, container, enabled, rightSlide);
|
||||
|
||||
container.addEventListener("mouseleave", () => hide());
|
||||
container.addEventListener("mouseenter", () => show());
|
||||
}
|
||||
|
||||
function enableAutoHideAnimation(element: Element): void {
|
||||
element.classList.add("autoHiding");
|
||||
element.classList.add("sbhidden");
|
||||
}
|
||||
|
||||
function disableAutoHideAnimation(element: Element): void {
|
||||
element.classList.remove("autoHiding");
|
||||
element.classList.remove("sbhidden");
|
||||
}
|
||||
|
||||
export const AnimationUtils = {
|
||||
applyLoadingAnimation,
|
||||
setupAutoHideAnimation,
|
||||
setupCustomHideAnimation,
|
||||
enableAutoHideAnimation,
|
||||
disableAutoHideAnimation
|
||||
};
|
||||
@@ -160,7 +160,7 @@ module.exports = env => {
|
||||
if (path.match(/(\/|\\)_locales(\/|\\).+/)) {
|
||||
const parsed = JSON.parse(content.toString());
|
||||
if (env.browser.toLowerCase() === "safari") {
|
||||
parsed.fullName.message = parsed.fullName.message.match(/^.+(?= -)/)?.[0] || parsed.fullName.message;
|
||||
parsed.fullName.message = parsed.fullName.message.match(/^.+(?= [-–])/)?.[0] || parsed.fullName.message;
|
||||
if (parsed.fullName.message.length > 50) {
|
||||
parsed.fullName.message = parsed.fullName.message.slice(0, 47) + "...";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user