Compare commits

..

48 Commits
5.5 ... 5.5.8

Author SHA1 Message Date
Ajay
6c71036356 update translations 2024-03-07 17:22:22 -05:00
Ajay
f55f2606a7 bump version 2024-03-07 17:21:50 -05:00
Ajay
e9b7abd219 Fix rate-limiting when setting options from options page 2024-03-07 17:20:23 -05:00
Ajay Ramachandran
63d3309605 Merge pull request #1948 from ajayyy/dependabot/npm_and_yarn/follow-redirects-1.15.4
Bump follow-redirects from 1.15.3 to 1.15.4
2024-02-29 03:35:22 -05:00
Ajay Ramachandran
b4615d7d79 Merge pull request #1950 from ajayyy/dependabot/npm_and_yarn/postcss-and-web-ext-8.4.33
Bump postcss and web-ext
2024-02-29 03:35:13 -05:00
Ajay Ramachandran
ec7eee650b Merge pull request #1979 from HanYaodong/dev_refresh
[Fix] Stop Refresh Animation on Popup When Triggered on Non-YouTube Pages
2024-02-29 03:34:45 -05:00
HanYaodong
154bb1749d Stop refresh animation on popup when not on youtube page 2024-02-29 12:36:06 +08:00
Ajay
742eb7ef57 bump version 2024-02-28 01:13:57 -05:00
Ajay
88526aa46e Fix missing key in tooltip and update maze utils 2024-02-27 22:16:53 -05:00
Ajay
1909e66c87 Move category pill to top if on description on right layout 2024-02-27 22:13:58 -05:00
Ajay
5f8447ec6b bump version 2024-02-24 18:54:19 -05:00
Ajay
e6db5b43ff update translations 2024-02-24 18:54:11 -05:00
Ajay
6566c129c7 Fix issue with BlockTube version 0.4.1 2024-02-24 18:53:26 -05:00
Ajay
e7f4be2d57 Don't require preview for starting segments 2024-02-21 14:32:44 -05:00
Ajay
9d04482d19 bump version 2024-02-13 22:35:44 -05:00
Ajay
7c661f8e67 update translations 2024-02-13 22:33:11 -05:00
Ajay
0b7a2fd197 Fix adding custom Invidious instances on Firefox
Fixes #815
2024-02-13 22:32:37 -05:00
Ajay
3382d8a500 Fix chapter names not appearing 2024-02-13 21:52:10 -05:00
Ajay
5d871d5fe7 Fix hidden mute segments sometimes still muting 2024-02-13 21:32:23 -05:00
Ajay Ramachandran
b7c5737a95 Merge pull request #1967 from HanYaodong/dev
Fix license info in README
2024-02-02 19:17:14 -05:00
HanYaodong
0cca1c3566 Fix license info in README 2024-02-02 19:32:31 +08:00
Ajay
e0fe0fad67 Don't close submission menu if submission didn't go through
Fxies submission menu closing for warning about previewing a segment
2024-02-01 13:31:19 -05:00
Ajay
c0bc068a18 Handle exceptions for voting
Maybe fixes #1961
2024-01-31 19:06:16 -05:00
Ajay
7cb413db15 Don't display preview bar while scrubbing on mobile
Should help with #1962
2024-01-31 18:58:06 -05:00
Ajay
fdf1a6acf9 bump version 2024-01-30 23:12:36 -05:00
Ajay
b533c6c1c8 update translations 2024-01-30 23:12:28 -05:00
Ajay
926423db5c Fix compatibility with vinegar on Safari 2024-01-30 23:12:03 -05:00
Ajay
e7d55d2bac Fix preview bar end time sometimes being inaccurate 2024-01-25 19:38:10 -05:00
Ajay
16f27e5c5c Move animation utils to maze utils 2024-01-24 16:21:31 -05:00
Ajay
88dc8db6e7 bump version 2024-01-23 14:27:05 -05:00
Ajay
c69a574379 update translations 2024-01-23 14:27:01 -05:00
Ajay
516d624f16 Don't require preview for segments ending at end of the video
Fixes #1959
2024-01-23 14:25:07 -05:00
Ajay
a662c3e04f Reloading after creating segments shouldn't require previewing 2024-01-23 14:22:54 -05:00
Ajay
985910cbf6 Count previewed unsubmitted segments previewed manually 2024-01-23 14:20:24 -05:00
Ajay
feae86f6ea Don't throw errors on extension live-update 2024-01-21 20:42:40 -05:00
Ajay
1f96e3b117 Improve precision of hover preview
Also fixes issue with YouTube "most-replayed" messing it up
2024-01-21 20:35:00 -05:00
Ajay
b3b5d46e4e bump version 2024-01-21 15:57:35 -05:00
Ajay
c996680a58 Fix category selection changes not saving 2024-01-21 15:57:22 -05:00
Ajay
ade4654ae0 bump version 2024-01-21 14:26:27 -05:00
Ajay
4e9e6282f6 update translations 2024-01-21 14:26:16 -05:00
Ajay
783326afca Use runtime.getURL 2024-01-21 13:51:32 -05:00
Ajay
8dfd06919b Only require a preview for skip and mute segments 2024-01-21 13:40:01 -05:00
Ajay
50ee690717 Only prevent scrolling while editing 2024-01-21 13:37:29 -05:00
Ajay
74aebd32a7 Prevent zooming when trying to scroll to edit times 2024-01-21 13:31:45 -05:00
Ajay
e489d0f913 Fix german title not being shortened 2024-01-16 18:39:29 -05:00
Ajay
814df46521 Don't warn about using the word intro in chapter names 2024-01-16 18:21:44 -05:00
dependabot[bot]
ba92e6e386 Bump postcss and web-ext
Bumps [postcss](https://github.com/postcss/postcss) to 8.4.33 and updates ancestor dependency [web-ext](https://github.com/mozilla/web-ext). These dependencies need to be updated together.


Updates `postcss` from 8.4.21 to 8.4.33
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.21...8.4.33)

Updates `web-ext` from 7.6.2 to 7.10.0
- [Release notes](https://github.com/mozilla/web-ext/releases)
- [Commits](https://github.com/mozilla/web-ext/compare/7.6.2...7.10.0)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
- dependency-name: web-ext
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-12 18:16:09 +00:00
dependabot[bot]
bdbe8b60cc Bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 03:55:58 +00:00
24 changed files with 1035 additions and 584 deletions

View File

@@ -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

View File

@@ -1,7 +1,7 @@
{ {
"name": "__MSG_fullName__", "name": "__MSG_fullName__",
"short_name": "SponsorBlock", "short_name": "SponsorBlock",
"version": "5.5", "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",

1238
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"

View File

@@ -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;

View File

@@ -123,7 +123,7 @@ chrome.runtime.onInstalled.addListener(function () {
// If there is no userID, then it is the first install. // If there is no userID, then it is the first install.
if (!userID && !Config.local.alreadyInstalled){ if (!userID && !Config.local.alreadyInstalled){
//open up the install page //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 //generate a userID
const newUserID = generateUserID(); const newUserID = generateUserID();
@@ -137,7 +137,7 @@ chrome.runtime.onInstalled.addListener(function () {
if (Config.config.supportInvidious) { if (Config.config.supportInvidious) {
if (!(await utils.containsInvidiousPermission())) { 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); }, 1500);
@@ -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: ""
}; };
} }
} }

View File

@@ -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,17 +45,21 @@ 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"
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}> src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
</img> </img>
{ {
@@ -86,7 +92,7 @@ class CategoryPillComponent extends React.Component<CategoryPillProps, CategoryP
)} )}
{/* Close Button */} {/* Close Button */}
<img src={chrome.extension.getURL("icons/close.png")} <img src={chrome.runtime.getURL("icons/close.png")}
className="categoryPillClose" className="categoryPillClose"
onClick={() => { onClick={() => {
this.setState({ show: false }); this.setState({ show: false });

View File

@@ -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";

View File

@@ -207,7 +207,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
{/* Close button */} {/* Close button */}
<img src={chrome.extension.getURL("icons/close.png")} <img src={chrome.runtime.getURL("icons/close.png")}
className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton" className={"sponsorSkipObject sponsorSkipNoticeButton sponsorSkipNoticeCloseButton sponsorSkipNoticeRightButton"
+ (this.props.biggerCloseButton ? " biggerCloseButton" : "")} + (this.props.biggerCloseButton ? " biggerCloseButton" : "")}
onClick={() => this.close()}> onClick={() => this.close()}>

View File

@@ -34,7 +34,7 @@ export interface SponsorTimeEditState {
chapterNameSelectorHovering: boolean; 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|-/))); .map((name) => chrome.i18n.getMessage("category_" + name).split(/\/|\s|-/)));
class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> { class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, SponsorTimeEditState> {
@@ -81,13 +81,15 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
componentDidMount(): void { componentDidMount(): void {
// Prevent inputs from triggering key events // Prevent inputs from triggering key events
document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', function (event) { document.getElementById("sponsorTimeEditContainer" + this.idSuffix).addEventListener('keydown', (e) => {
event.stopPropagation(); e.stopPropagation();
}); });
// Prevent scrolling while changing times // Prevent scrolling while changing times
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', function (event) { document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('wheel', (e) => {
event.preventDefault(); if (this.state.editing) {
e.preventDefault();
}
}, {passive: false}); }, {passive: false});
// Add as a config listener // Add as a config listener
@@ -221,7 +223,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
target="_blank" rel="noreferrer"> target="_blank" rel="noreferrer">
<img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix} <img id={"sponsorTimeCategoriesHelpButton" + this.idSuffix}
className="helpButton" className="helpButton"
src={chrome.extension.getURL("icons/help.svg")} src={chrome.runtime.getURL("icons/help.svg")}
title={chrome.i18n.getMessage("categoryGuidelines")} /> title={chrome.i18n.getMessage("categoryGuidelines")} />
</a> </a>
</div> </div>

View File

@@ -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;
} }
@@ -69,6 +69,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.videoObserver.observe(this.contentContainer().v, { this.videoObserver.observe(this.contentContainer().v, {
attributes: true 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 { componentWillUnmount(): void {
@@ -100,7 +107,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
onClick={() => this.sortSegments()} onClick={() => this.sortSegments()}
title={chrome.i18n.getMessage("sortSegments")} title={chrome.i18n.getMessage("sortSegments")}
key="sortButton" key="sortButton"
src={chrome.extension.getURL("icons/sort.svg")}> src={chrome.runtime.getURL("icons/sort.svg")}>
</img>; </img>;
const exportButton = const exportButton =
<img id={"sponsorSkipExportButton" + this.state.idSuffix} <img id={"sponsorSkipExportButton" + this.state.idSuffix}
@@ -108,7 +115,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
onClick={() => this.exportSegments()} onClick={() => this.exportSegments()}
title={chrome.i18n.getMessage("exportSegments")} title={chrome.i18n.getMessage("exportSegments")}
key="exportButton" key="exportButton"
src={chrome.extension.getURL("icons/export.svg")}> src={chrome.runtime.getURL("icons/export.svg")}>
</img>; </img>;
return ( return (
<NoticeComponent noticeTitle={this.state.noticeTitle} <NoticeComponent noticeTitle={this.state.noticeTitle}
@@ -232,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 {

View File

@@ -158,7 +158,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
}); });
} }
Config.forceLocalUpdate("categorySelections"); Config.forceSyncUpdate("categorySelections");
} }
getCategorySkipOptions(): JSX.Element[] { getCategorySkipOptions(): JSX.Element[] {

View File

@@ -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;
} }
} }
} }
@@ -1768,7 +1779,7 @@ function createButton(baseID: string, title: string, callback: () => void, image
newButton.draggable = isDraggable; newButton.draggable = isDraggable;
newButtonImage.id = baseID + "Image"; newButtonImage.id = baseID + "Image";
newButtonImage.className = "playerButtonImage"; newButtonImage.className = "playerButtonImage";
newButtonImage.src = chrome.extension.getURL("icons/" + imageName); newButtonImage.src = chrome.runtime.getURL("icons/" + imageName);
// Append image to button // Append image to button
newButton.appendChild(newButtonImage); newButton.appendChild(newButtonImage);
@@ -1867,10 +1878,10 @@ function updateEditButtonsOnPlayer(): void {
if (buttonsEnabled) { if (buttonsEnabled) {
if (creatingSegment) { 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")); playerButtons.startSegment.button.setAttribute("title", chrome.i18n.getMessage("sponsorEnd"));
} else { } 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")); playerButtons.startSegment.button.setAttribute("title", chrome.i18n.getMessage("sponsorStart"));
} }
} }
@@ -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);
} }
} }
@@ -2048,7 +2062,7 @@ function openInfoMenu() {
} }
} }
}); });
frame.src = chrome.extension.getURL("popup.html"); frame.src = chrome.runtime.getURL("popup.html");
popup.appendChild(frame); popup.appendChild(frame);
const elemHasChild = (elements: NodeListOf<HTMLElement>): Element => { const elemHasChild = (elements: NodeListOf<HTMLElement>): Element => {
@@ -2257,22 +2271,26 @@ 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) =>
[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
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()); const stopAnimation = AnimationUtils.applyLoadingAnimation(playerButtons.submit.button, 1, () => updateEditButtonsOnPlayer());
//check if a sponsor exceeds the duration of the video //check if a sponsor exceeds the duration of the video
@@ -2294,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;
} }
} }
} }
@@ -2344,10 +2362,12 @@ 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";
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.")) { if (response.status === 403 && response.responseText.startsWith("Submission rejected due to a tip from a moderator.")) {
openWarningDialog(skipNoticeContentContainer); openWarningDialog(skipNoticeContentContainer);
@@ -2355,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
@@ -2380,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
@@ -2531,7 +2555,7 @@ function addCSS() {
fileref.rel = "stylesheet"; fileref.rel = "stylesheet";
fileref.type = "text/css"; fileref.type = "text/css";
fileref.href = chrome.extension.getURL(file); fileref.href = chrome.runtime.getURL(file);
head.appendChild(fileref); head.appendChild(fileref);
} }

View File

@@ -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);
}
} }
/* /*

View File

@@ -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";

View File

@@ -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"));
} }

View File

@@ -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

View File

@@ -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";
}
} }
} }

View File

@@ -59,7 +59,7 @@ export class RectangleTooltip {
className="sponsorBlockRectangleTooltip" > className="sponsorBlockRectangleTooltip" >
<div> <div>
<img className="sponsorSkipLogo sponsorSkipObject" <img className="sponsorSkipLogo sponsorSkipObject"
src={chrome.extension.getURL("icons/IconSponsorBlocker256px.png")}> src={chrome.runtime.getURL("icons/IconSponsorBlocker256px.png")}>
</img> </img>
<span className="sponsorSkipObject"> <span className="sponsorSkipObject">
{this.text + (props.link ? ". " : "")} {this.text + (props.link ? ". " : "")}

View File

@@ -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;

View File

@@ -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
};

View File

@@ -160,7 +160,7 @@ module.exports = env => {
if (path.match(/(\/|\\)_locales(\/|\\).+/)) { if (path.match(/(\/|\\)_locales(\/|\\).+/)) {
const parsed = JSON.parse(content.toString()); const parsed = JSON.parse(content.toString());
if (env.browser.toLowerCase() === "safari") { 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) { if (parsed.fullName.message.length > 50) {
parsed.fullName.message = parsed.fullName.message.slice(0, 47) + "..."; parsed.fullName.message = parsed.fullName.message.slice(0, 47) + "...";
} }