mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 19:47:04 +03:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c71036356 | ||
|
|
f55f2606a7 | ||
|
|
e9b7abd219 | ||
|
|
63d3309605 | ||
|
|
b4615d7d79 | ||
|
|
ec7eee650b | ||
|
|
154bb1749d | ||
|
|
742eb7ef57 | ||
|
|
88526aa46e | ||
|
|
1909e66c87 | ||
|
|
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 | ||
|
|
ba92e6e386 | ||
|
|
bdbe8b60cc |
@@ -75,4 +75,4 @@ Icons made by:
|
|||||||
|
|
||||||
### License
|
### 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__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.5.2",
|
"version": "5.5.8",
|
||||||
"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: 27db39e39b...036086403f
1238
package-lock.json
generated
1238
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@
|
|||||||
"ts-loader": "^9.4.2",
|
"ts-loader": "^9.4.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "4.9",
|
"typescript": "4.9",
|
||||||
"web-ext": "^7.6.2",
|
"web-ext": "^7.10.0",
|
||||||
"webpack": "^5.75.0",
|
"webpack": "^5.75.0",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
|
|||||||
Submodule public/_locales updated: 8f84082dde...01ee778967
@@ -780,6 +780,18 @@ input::-webkit-inner-spin-button {
|
|||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Description on right layout */
|
||||||
|
#title > #categoryPillParent {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 2.8rem;
|
||||||
|
}
|
||||||
|
#title > #categoryPillParent > #categoryPill.cbPillOpen {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
#categoryPillParent {
|
#categoryPillParent {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ async function registerFirefoxContentScript(options: Registration) {
|
|||||||
ids: [options.id]
|
ids: [options.id]
|
||||||
}).catch(() => []);
|
}).catch(() => []);
|
||||||
|
|
||||||
if (existingRegistrations.length > 0
|
if (existingRegistrations && existingRegistrations.length > 0
|
||||||
&& existingRegistrations[0].matches.every((match) => options.matches.includes(match))) {
|
&& options.matches.every((match) => existingRegistrations[0].matches.includes(match))) {
|
||||||
// No need to register another script, already registered
|
// No need to register another script, already registered
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -222,27 +222,35 @@ 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;
|
||||||
|
|
||||||
//publish this vote
|
try {
|
||||||
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return {
|
return {
|
||||||
successType: 1,
|
successType: 1,
|
||||||
responseText: await response.text()
|
responseText: await response.text()
|
||||||
};
|
};
|
||||||
} else if (response.status == 405) {
|
} else if (response.status == 405) {
|
||||||
//duplicate vote
|
//duplicate vote
|
||||||
return {
|
return {
|
||||||
successType: 0,
|
successType: 0,
|
||||||
statusCode: response.status,
|
statusCode: response.status,
|
||||||
responseText: await response.text()
|
responseText: await response.text()
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
//error while connect
|
//error while connect
|
||||||
|
return {
|
||||||
|
successType: -1,
|
||||||
|
statusCode: response.status,
|
||||||
|
responseText: await response.text()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
return {
|
return {
|
||||||
successType: -1,
|
successType: -1,
|
||||||
statusCode: response.status,
|
statusCode: -1,
|
||||||
responseText: await response.text()
|
responseText: ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
|||||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||||
import { VoteResponse } from "../messageTypes";
|
import { VoteResponse } from "../messageTypes";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../../maze-utils/src/animationUtils";
|
||||||
import { Tooltip } from "../render/Tooltip";
|
import { Tooltip } from "../render/Tooltip";
|
||||||
import { getErrorMessage } from "../../maze-utils/src/formating";
|
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
@@ -23,12 +23,14 @@ export interface CategoryPillState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryPillState> {
|
class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryPillState> {
|
||||||
|
mainRef: React.MutableRefObject<HTMLSpanElement>;
|
||||||
tooltip?: Tooltip;
|
tooltip?: Tooltip;
|
||||||
|
|
||||||
constructor(props: CategoryPillProps) {
|
constructor(props: CategoryPillProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.mainRef = React.createRef();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
segment: null,
|
segment: null,
|
||||||
show: false,
|
show: false,
|
||||||
@@ -43,13 +45,17 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
|
|||||||
color: this.getTextColor(),
|
color: this.getTextColor(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To be able to remove the margin from the parent
|
||||||
|
this.mainRef?.current?.parentElement?.classList?.toggle("cbPillOpen", this.state.show);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span style={style}
|
<span style={style}
|
||||||
className={"sponsorBlockCategoryPill" + (!this.props.showTextByDefault ? " sbPillNoText" : "")}
|
className={"sponsorBlockCategoryPill" + (!this.props.showTextByDefault ? " sbPillNoText" : "")}
|
||||||
aria-label={this.getTitleText()}
|
aria-label={this.getTitleText()}
|
||||||
onClick={(e) => this.toggleOpen(e)}
|
onClick={(e) => this.toggleOpen(e)}
|
||||||
onMouseEnter={() => this.openTooltip()}
|
onMouseEnter={() => this.openTooltip()}
|
||||||
onMouseLeave={() => this.closeTooltip()}>
|
onMouseLeave={() => this.closeTooltip()}
|
||||||
|
ref={this.mainRef}>
|
||||||
|
|
||||||
<span className="sponsorBlockCategoryPillTitleSection">
|
<span className="sponsorBlockCategoryPillTitleSection">
|
||||||
<img className="sponsorSkipLogo sponsorSkipObject"
|
<img className="sponsorSkipLogo sponsorSkipObject"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
|||||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||||
import { VoteResponse } from "../messageTypes";
|
import { VoteResponse } from "../messageTypes";
|
||||||
import { AnimationUtils } from "../utils/animationUtils";
|
import { AnimationUtils } from "../../maze-utils/src/animationUtils";
|
||||||
import { Tooltip } from "../render/Tooltip";
|
import { Tooltip } from "../render/Tooltip";
|
||||||
import { getErrorMessage } from "../../maze-utils/src/formating";
|
import { getErrorMessage } from "../../maze-utils/src/formating";
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface SubmissionNoticeProps {
|
|||||||
// Contains functions and variables from the content script needed by the skip notice
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
contentContainer: ContentContainer;
|
contentContainer: ContentContainer;
|
||||||
|
|
||||||
callback: () => unknown;
|
callback: () => Promise<boolean>;
|
||||||
|
|
||||||
closeListener: () => void;
|
closeListener: () => void;
|
||||||
}
|
}
|
||||||
@@ -239,9 +239,11 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.callback();
|
this.props.callback().then((success) => {
|
||||||
|
if (success) {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sortSegments(): void {
|
sortSegments(): void {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { SkipButtonControlBar } from "./js-components/skipButtonControlBar";
|
|||||||
import { getStartTimeFromUrl } from "./utils/urlParser";
|
import { getStartTimeFromUrl } from "./utils/urlParser";
|
||||||
import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isVisible } from "./utils/pageUtils";
|
import { getControls, getExistingChapters, getHashParams, isPlayingPlaylist, isVisible } from "./utils/pageUtils";
|
||||||
import { CategoryPill } from "./render/CategoryPill";
|
import { CategoryPill } from "./render/CategoryPill";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "../maze-utils/src/animationUtils";
|
||||||
import { GenericUtils } from "./utils/genericUtils";
|
import { GenericUtils } from "./utils/genericUtils";
|
||||||
import { logDebug, logWarn } from "./utils/logger";
|
import { logDebug, logWarn } from "./utils/logger";
|
||||||
import { importTimes } from "./utils/exporter";
|
import { importTimes } from "./utils/exporter";
|
||||||
@@ -258,7 +258,12 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
break;
|
break;
|
||||||
case "refreshSegments":
|
case "refreshSegments":
|
||||||
// update video on refresh if videoID invalid
|
// update video on refresh if videoID invalid
|
||||||
if (!getVideoID()) checkVideoIDChange();
|
if (!getVideoID()) {
|
||||||
|
checkVideoIDChange().then(() => {
|
||||||
|
// if still no video ID found, return an empty info to the popup
|
||||||
|
if (!getVideoID()) chrome.runtime.sendMessage({ message: "infoUpdated" });
|
||||||
|
});
|
||||||
|
}
|
||||||
// fetch segments
|
// fetch segments
|
||||||
sponsorsLookup(false);
|
sponsorsLookup(false);
|
||||||
|
|
||||||
@@ -454,7 +459,9 @@ function videoIDChange(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleMobileControlsMutations(): void {
|
function handleMobileControlsMutations(): void {
|
||||||
if (!chrome.runtime?.id) return;
|
// Don't update while scrubbing
|
||||||
|
if (!chrome.runtime?.id
|
||||||
|
|| document.querySelector(".YtProgressBarProgressBarPlayheadDotInDragging")) return;
|
||||||
|
|
||||||
updateVisibilityOfPlayerControlsButton();
|
updateVisibilityOfPlayerControlsButton();
|
||||||
|
|
||||||
@@ -770,6 +777,7 @@ function getVirtualTime(): number {
|
|||||||
|
|
||||||
function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
|
function inMuteSegment(currentTime: number, includeOverlap: boolean): boolean {
|
||||||
const checkFunction = (segment) => segment.actionType === ActionType.Mute
|
const checkFunction = (segment) => segment.actionType === ActionType.Mute
|
||||||
|
&& segment.hidden === SponsorHideType.Visible
|
||||||
&& segment.segment[0] <= currentTime
|
&& segment.segment[0] <= currentTime
|
||||||
&& (segment.segment[1] > currentTime || (includeOverlap && segment.segment[1] + 0.02 > currentTime));
|
&& (segment.segment[1] > currentTime || (includeOverlap && segment.segment[1] + 0.02 > currentTime));
|
||||||
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
return sponsorTimes?.some(checkFunction) || sponsorTimesSubmitting.some(checkFunction);
|
||||||
@@ -1608,6 +1616,9 @@ function sendTelemetryAndCount(skippingSegments: SponsorTime[], secondsSkipped:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fullSkip) asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1983,6 +1994,9 @@ function updateSponsorTimesSubmitting(getFromConfig = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sponsorTimesSubmitting.length > 0) {
|
if (sponsorTimesSubmitting.length > 0) {
|
||||||
|
// Assume they already previewed a segment
|
||||||
|
previewedSegment = true;
|
||||||
|
|
||||||
importExistingChapters(true);
|
importExistingChapters(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2257,20 +2271,22 @@ function submitSegments() {
|
|||||||
|
|
||||||
//send the message to the background js
|
//send the message to the background js
|
||||||
//called after all the checks have been made that it's okay to do so
|
//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
|
// check if all segments are full video
|
||||||
const onlyFullVideo = sponsorTimesSubmitting.every((segment) => segment.actionType === ActionType.Full);
|
const onlyFullVideo = sponsorTimesSubmitting.every((segment) => segment.actionType === ActionType.Full);
|
||||||
// Block if submitting on a running livestream or premiere
|
// Block if submitting on a running livestream or premiere
|
||||||
if (!onlyFullVideo && (getIsLivePremiere() || isVisible(document.querySelector(".ytp-live-badge")))) {
|
if (!onlyFullVideo && (getIsLivePremiere() || isVisible(document.querySelector(".ytp-live-badge")))) {
|
||||||
alert(chrome.i18n.getMessage("liveOrPremiere"));
|
alert(chrome.i18n.getMessage("liveOrPremiere"));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!previewedSegment
|
if (!previewedSegment
|
||||||
&& !sponsorTimesSubmitting.every((segment) =>
|
&& !sponsorTimesSubmitting.every((segment) =>
|
||||||
[ActionType.Full, ActionType.Chapter, ActionType.Poi].includes(segment.actionType))) {
|
[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)}`);
|
alert(`${chrome.i18n.getMessage("previewSegmentRequired")} ${keybindToString(Config.config.previewKeybind)}`);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add loading animation
|
// Add loading animation
|
||||||
@@ -2296,7 +2312,7 @@ async function sendSubmitMessage() {
|
|||||||
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
|
||||||
getSegmentsMessage(sponsorTimesSubmitting);
|
getSegmentsMessage(sponsorTimesSubmitting);
|
||||||
|
|
||||||
if(!confirm(confirmShort)) return;
|
if(!confirm(confirmShort)) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2346,6 +2362,8 @@ async function sendSubmitMessage() {
|
|||||||
if (fullVideoSegment) {
|
if (fullVideoSegment) {
|
||||||
categoryPill?.setSegment(fullVideoSegment);
|
categoryPill?.setSegment(fullVideoSegment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// Show that the upload failed
|
// Show that the upload failed
|
||||||
playerButtons.submit.button.style.animation = "unset";
|
playerButtons.submit.button.style.animation = "unset";
|
||||||
@@ -2357,6 +2375,8 @@ async function sendSubmitMessage() {
|
|||||||
alert(getErrorMessage(response.status, response.responseText));
|
alert(getErrorMessage(response.status, response.responseText));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the message that visually displays the video times
|
//get the message that visually displays the video times
|
||||||
@@ -2382,6 +2402,8 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateActiveSegment(currentTime: number): void {
|
function updateActiveSegment(currentTime: number): void {
|
||||||
|
previewBar?.updateChapterText(sponsorTimes, sponsorTimesSubmitting, currentTime);
|
||||||
|
|
||||||
chrome.runtime.sendMessage({
|
chrome.runtime.sendMessage({
|
||||||
message: "time",
|
message: "time",
|
||||||
time: currentTime
|
time: currentTime
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { ActionType, Category, SegmentContainer, SponsorHideType, SponsorSourceT
|
|||||||
import { partition } from "../utils/arrayUtils";
|
import { partition } from "../utils/arrayUtils";
|
||||||
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
|
import { DEFAULT_CATEGORY, shortCategoryName } from "../utils/categoryUtils";
|
||||||
import { normalizeChapterName } from "../utils/exporter";
|
import { normalizeChapterName } from "../utils/exporter";
|
||||||
import { getFormattedTimeToSeconds } from "../../maze-utils/src/formating";
|
|
||||||
import { findValidElement } from "../../maze-utils/src/dom";
|
import { findValidElement } from "../../maze-utils/src/dom";
|
||||||
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
import { addCleanupListener } from "../../maze-utils/src/cleanup";
|
||||||
|
|
||||||
@@ -125,34 +124,11 @@ class PreviewBar {
|
|||||||
mouseOnSeekBar = false;
|
mouseOnSeekBar = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
seekBar.addEventListener("mousemove", (e: MouseEvent) => {
|
||||||
if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer) return;
|
if (!mouseOnSeekBar || !this.categoryTooltip || !this.categoryTooltipContainer || !chrome.runtime?.id) return;
|
||||||
|
|
||||||
// Only care about mutations to time tooltip
|
let noYoutubeChapters = !!tooltipTextWrapper.querySelector(".ytp-tooltip-text.ytp-tooltip-text-no-title");
|
||||||
if (!mutations.some((mutation) => (mutation.target as HTMLElement).classList.contains("ytp-tooltip-text"))) {
|
const timeInSeconds = this.decimalToTime((e.clientX - seekBar.getBoundingClientRect().x) / seekBar.clientWidth);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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] =
|
||||||
@@ -198,15 +174,6 @@ class PreviewBar {
|
|||||||
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
|
this.chapterTooltip.style.textAlign = titleTooltip.style.textAlign;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
observer.observe(tooltipTextWrapper, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
addCleanupListener(() => {
|
|
||||||
observer.disconnect();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
||||||
@@ -307,6 +274,7 @@ class PreviewBar {
|
|||||||
return (b[1] - b[0]) - (a[1] - a[0]);
|
return (b[1] - b[0]) - (a[1] - a[0]);
|
||||||
});
|
});
|
||||||
for (const segment of sortedSegments) {
|
for (const segment of sortedSegments) {
|
||||||
|
if (segment.actionType === ActionType.Chapter) continue;
|
||||||
const bar = this.createBar(segment);
|
const bar = this.createBar(segment);
|
||||||
|
|
||||||
this.container.appendChild(bar);
|
this.container.appendChild(bar);
|
||||||
@@ -346,7 +314,7 @@ class PreviewBar {
|
|||||||
bar.style.left = this.timeToPercentage(startTime);
|
bar.style.left = this.timeToPercentage(startTime);
|
||||||
|
|
||||||
if (duration > 0) {
|
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) {
|
if (this.chapterFilter(barSegment) && segment[1] < this.videoDuration) {
|
||||||
bar.style.marginRight = `${this.chapterMargin}px`;
|
bar.style.marginRight = `${this.chapterMargin}px`;
|
||||||
@@ -919,7 +887,22 @@ class PreviewBar {
|
|||||||
return `${this.timeToDecimal(time) * 100}%`
|
return `${this.timeToDecimal(time) * 100}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeToRightPercentage(time: number): string {
|
||||||
|
return `${(1 - this.timeToDecimal(time)) * 100}%`
|
||||||
|
}
|
||||||
|
|
||||||
timeToDecimal(time: number): number {
|
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) {
|
if (this.originalChapterBarBlocks?.length > 1 && this.existingChapters.length === this.originalChapterBarBlocks?.length) {
|
||||||
// Parent element to still work when display: none
|
// Parent element to still work when display: none
|
||||||
const totalPixels = this.originalChapterBar.parentElement.clientWidth;
|
const totalPixels = this.originalChapterBar.parentElement.clientWidth;
|
||||||
@@ -929,8 +912,9 @@ class PreviewBar {
|
|||||||
const chapterElement = this.originalChapterBarBlocks[i];
|
const chapterElement = this.originalChapterBarBlocks[i];
|
||||||
const widthPixels = parseFloat(chapterElement.style.width.replace("px", ""));
|
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;
|
pixelOffset += widthPixels + marginPixels;
|
||||||
lastCheckedChapter = i;
|
lastCheckedChapter = i;
|
||||||
} else {
|
} else {
|
||||||
@@ -944,13 +928,22 @@ class PreviewBar {
|
|||||||
const latestWidth = parseFloat(this.originalChapterBarBlocks[lastCheckedChapter + 1].style.width.replace("px", ""));
|
const latestWidth = parseFloat(this.originalChapterBarBlocks[lastCheckedChapter + 1].style.width.replace("px", ""));
|
||||||
const latestChapterDuration = latestChapter.segment[1] - latestChapter.segment[0];
|
const latestChapterDuration = latestChapter.segment[1] - latestChapter.segment[0];
|
||||||
|
|
||||||
const percentageInCurrentChapter = (time - latestChapter.segment[0]) / latestChapterDuration;
|
if (isTime) {
|
||||||
const sizeOfCurrentChapter = latestWidth / totalPixels;
|
const percentageInCurrentChapter = (value - latestChapter.segment[0]) / latestChapterDuration;
|
||||||
return Math.min(1, ((pixelOffset / totalPixels) + (percentageInCurrentChapter * sizeOfCurrentChapter)));
|
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 Config from "../config";
|
||||||
import { SegmentUUID, SponsorTime } from "../types";
|
import { SegmentUUID, SponsorTime } from "../types";
|
||||||
import { getSkippingText } from "../utils/categoryUtils";
|
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 { keybindToString } from "../../maze-utils/src/config";
|
||||||
import { isMobileControlsOpen } from "../utils/mobileUtils";
|
import { isMobileControlsOpen } from "../utils/mobileUtils";
|
||||||
|
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ async function init() {
|
|||||||
break;
|
break;
|
||||||
case "resetToDefault":
|
case "resetToDefault":
|
||||||
Config.resetToDefault();
|
Config.resetToDefault();
|
||||||
window.location.reload();
|
setTimeout(() => window.location.reload(), 200);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -632,8 +632,7 @@ async function setTextOption(option: string, element: HTMLElement, value: string
|
|||||||
await invidiousOnClick(checkbox, "supportInvidious");
|
await invidiousOnClick(checkbox, "supportInvidious");
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.reload();
|
setTimeout(() => window.location.reload(), 200);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(chrome.i18n.getMessage("incorrectlyFormattedOptions"));
|
alert(chrome.i18n.getMessage("incorrectlyFormattedOptions"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
VoteResponse,
|
VoteResponse,
|
||||||
} from "./messageTypes";
|
} from "./messageTypes";
|
||||||
import { showDonationLink } from "./utils/configUtils";
|
import { showDonationLink } from "./utils/configUtils";
|
||||||
import { AnimationUtils } from "./utils/animationUtils";
|
import { AnimationUtils } from "../maze-utils/src/animationUtils";
|
||||||
import { shortCategoryName } from "./utils/categoryUtils";
|
import { shortCategoryName } from "./utils/categoryUtils";
|
||||||
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
import { localizeHtmlPage } from "../maze-utils/src/setup";
|
||||||
import { exportTimes } from "./utils/exporter";
|
import { exportTimes } from "./utils/exporter";
|
||||||
@@ -465,8 +465,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if request is undefined, then the page currently being browsed is not YouTube
|
// if request has no field other than message, then the page currently being browsed is not YouTube
|
||||||
if (request != undefined) {
|
if (request.found != undefined) {
|
||||||
//remove loading text
|
//remove loading text
|
||||||
PageElements.mainControls.style.display = "block";
|
PageElements.mainControls.style.display = "block";
|
||||||
if (request.onMobileYouTube) PageElements.mainControls.classList.add("hidden");
|
if (request.onMobileYouTube) PageElements.mainControls.classList.add("hidden");
|
||||||
@@ -490,6 +490,8 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
|
|
||||||
PageElements.issueReporterImportExport.classList.remove("hidden");
|
PageElements.issueReporterImportExport.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
displayNoVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
//see if whitelist button should be swapped
|
//see if whitelist button should be swapped
|
||||||
|
|||||||
@@ -43,9 +43,15 @@ export class CategoryPill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async attachToPageInternal(): Promise<void> {
|
private async attachToPageInternal(): Promise<void> {
|
||||||
const referenceNode =
|
let referenceNode =
|
||||||
await waitFor(() => getYouTubeTitleNode());
|
await waitFor(() => getYouTubeTitleNode());
|
||||||
|
|
||||||
|
// Experimental YouTube layout with description on right
|
||||||
|
const isOnDescriptionOnRightLayout = document.querySelector("#title #description");
|
||||||
|
if (isOnDescriptionOnRightLayout) {
|
||||||
|
referenceNode = referenceNode.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
if (referenceNode && !referenceNode.contains(this.container)) {
|
if (referenceNode && !referenceNode.contains(this.container)) {
|
||||||
if (!this.container) {
|
if (!this.container) {
|
||||||
this.container = document.createElement('span');
|
this.container = document.createElement('span');
|
||||||
@@ -91,7 +97,9 @@ export class CategoryPill {
|
|||||||
parent.appendChild(this.container);
|
parent.appendChild(this.container);
|
||||||
|
|
||||||
referenceNode.prepend(parent);
|
referenceNode.prepend(parent);
|
||||||
referenceNode.style.display = "flex";
|
if (!isOnDescriptionOnRightLayout) {
|
||||||
|
referenceNode.style.display = "flex";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class SubmissionNotice {
|
|||||||
// Contains functions and variables from the content script needed by the skip notice
|
// Contains functions and variables from the content script needed by the skip notice
|
||||||
contentContainer: () => unknown;
|
contentContainer: () => unknown;
|
||||||
|
|
||||||
callback: () => unknown;
|
callback: () => Promise<boolean>;
|
||||||
|
|
||||||
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
|
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class SubmissionNotice {
|
|||||||
|
|
||||||
root: Root;
|
root: Root;
|
||||||
|
|
||||||
constructor(contentContainer: ContentContainer, callback: () => unknown) {
|
constructor(contentContainer: ContentContainer, callback: () => Promise<boolean>) {
|
||||||
this.noticeRef = React.createRef();
|
this.noticeRef = React.createRef();
|
||||||
|
|
||||||
this.contentContainer = contentContainer;
|
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
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user