mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-17 03:36:07 +03:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19635d2f06 | ||
|
|
b2a5539324 | ||
|
|
01df1e655e | ||
|
|
467e1cae50 | ||
|
|
822e00a46a | ||
|
|
74433fe751 | ||
|
|
a45690b26e | ||
|
|
f69d6736d4 | ||
|
|
ae7916b7e7 | ||
|
|
8b305ba5ec | ||
|
|
0fdcade27e | ||
|
|
897ebda42d | ||
|
|
ddd0b4ea05 | ||
|
|
4826bcf7df | ||
|
|
09d8bc4425 | ||
|
|
aeb3299c50 | ||
|
|
be11012096 | ||
|
|
22da289151 | ||
|
|
67a16a7b34 | ||
|
|
312ac788b3 | ||
|
|
8117f3c215 | ||
|
|
3c1d3f5d8e | ||
|
|
7cbd1fa59d | ||
|
|
f9e9c5c868 | ||
|
|
3b89ef0487 | ||
|
|
d49bfa0a4c | ||
|
|
28cfcf7ddc | ||
|
|
e656dd8d8b | ||
|
|
7904913fdb | ||
|
|
464561992e | ||
|
|
4db73d5e54 | ||
|
|
19020d2131 | ||
|
|
244cce56c0 | ||
|
|
b05d696057 |
12
.github/workflows/tests.yml
vendored
12
.github/workflows/tests.yml
vendored
@@ -17,9 +17,21 @@ jobs:
|
||||
node-version: '18'
|
||||
- run: npm ci
|
||||
- run: sudo apt-get install chromium-chromedriver
|
||||
|
||||
- uses: browser-actions/setup-chrome@c785b87e244131f27c9f19c1a33e2ead956ab7ce
|
||||
with:
|
||||
chrome-version: 135
|
||||
install-dependencies: true
|
||||
install-chromedriver: true
|
||||
|
||||
- name: Copy configuration
|
||||
run: cp config.json.example config.json
|
||||
|
||||
- name: Set up WireGuard Connection
|
||||
uses: niklaskeerl/easy-wireguard-action@50341d5f4b8245ff3a90e278aca67b2d283c78d0
|
||||
with:
|
||||
WG_CONFIG_FILE: ${{ secrets.WG_CONFIG_FILE }}
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_fullName__",
|
||||
"short_name": "SponsorBlock",
|
||||
"version": "5.11.9",
|
||||
"version": "5.12.1",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_Description__",
|
||||
"homepage_url": "https://sponsor.ajay.app",
|
||||
|
||||
Submodule maze-utils updated: d47ec4cca4...9cef8e27cd
868
package-lock.json
generated
868
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
"@types/wicg-mediasession": "^1.1.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
||||
"@typescript-eslint/parser": "^5.54.1",
|
||||
"chromedriver": "^110.0.0",
|
||||
"chromedriver": "^135.0.0",
|
||||
"concurrently": "^7.6.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"eslint": "^8.35.0",
|
||||
|
||||
Submodule public/_locales updated: 010e475341...63fe3f4bbc
4
public/icons/loop.svg
Normal file
4
public/icons/loop.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#FFFFFF">
|
||||
<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-8 4-8 7h2s1-5 6-5c1.66 0 3.14.69 4.22 1.78L14 10h6V4l-2.35 2.35z"/>
|
||||
<path d="M5.85 17.65C7.3 19.1 9.29 20 11.5 20c4.42 0 8-4 8-7h-2s-1 5-6 5c-1.66 0-3.14-.69-4.22-1.78L9.5 14h-6v6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 335 B |
4
public/icons/looped.svg
Normal file
4
public/icons/looped.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#80fff6">
|
||||
<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-8 4-8 7h2s1-5 6-5c1.66 0 3.14.69 4.22 1.78L14 10h6V4l-2.35 2.35z"/>
|
||||
<path d="M5.85 17.65C7.3 19.1 9.29 20 11.5 20c4.42 0 8-4 8-7h-2s-1 5-6 5c-1.66 0-3.14-.69-4.22-1.78L9.5 14h-6v6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -566,7 +566,7 @@
|
||||
|
||||
<div id="advanced" class="option-group hidden">
|
||||
|
||||
<div id="support-invidious" data-type="toggle" data-sync="supportInvidious">
|
||||
<div id="support-invidious" data-type="toggle" data-sync="supportInvidious" data-no-safari="true">
|
||||
<div class="switch-container">
|
||||
<label class="switch">
|
||||
<input id="supportInvidious" type="checkbox">
|
||||
@@ -581,7 +581,7 @@
|
||||
<div class="small-description">__MSG_supportOtherSitesDescription__ </div>
|
||||
</div>
|
||||
|
||||
<div data-type="private-text-change" data-sync="invidiousInstances" data-dependent-on="supportInvidious">
|
||||
<div data-type="private-text-change" data-sync="invidiousInstances" data-dependent-on="supportInvidious" data-no-safari="true">
|
||||
<div class="option-button trigger-button">
|
||||
__MSG_addInvidiousInstance__
|
||||
</div>
|
||||
|
||||
@@ -704,7 +704,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
||||
getFullDurationCountdown(index: number): () => number {
|
||||
return () => {
|
||||
const sponsorTime = this.segments[index];
|
||||
const duration = Math.round((sponsorTime.segment[1] - getCurrentTime()) * (1 / (getVideo()?.playbackRate ?? 1)));
|
||||
const duration = Math.round((sponsorTime.segment[1] - (getCurrentTime() ?? 0)) * (1 / (getVideo()?.playbackRate ?? 1)));
|
||||
|
||||
return Math.max(duration, Config.config.skipNoticeDuration);
|
||||
};
|
||||
|
||||
@@ -243,6 +243,9 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
||||
return [{
|
||||
configKey: "autoSkipOnMusicVideos",
|
||||
label: chrome.i18n.getMessage("autoSkipOnMusicVideos"),
|
||||
}, {
|
||||
configKey: "skipNonMusicOnlyOnYoutubeMusic",
|
||||
label: chrome.i18n.getMessage("skipNonMusicOnlyOnYoutubeMusic"),
|
||||
}];
|
||||
default:
|
||||
return [];
|
||||
|
||||
@@ -57,6 +57,7 @@ interface SBConfig {
|
||||
donateClicked: number;
|
||||
autoHideInfoButton: boolean;
|
||||
autoSkipOnMusicVideos: boolean;
|
||||
skipNonMusicOnlyOnYoutubeMusic: boolean;
|
||||
colorPalette: {
|
||||
red: string;
|
||||
white: string;
|
||||
@@ -322,6 +323,7 @@ const syncDefaults = {
|
||||
donateClicked: 0,
|
||||
autoHideInfoButton: true,
|
||||
autoSkipOnMusicVideos: false,
|
||||
skipNonMusicOnlyOnYoutubeMusic: false,
|
||||
scrollToEditTimeUpdate: false, // false means the tooltip will be shown
|
||||
categoryPillUpdate: false,
|
||||
showChapterInfoMessage: true,
|
||||
|
||||
@@ -35,7 +35,7 @@ import { ChapterVote } from "./render/ChapterVote";
|
||||
import { openWarningDialog } from "./utils/warnings";
|
||||
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
||||
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube, isOnYTTV, getLastNonInlineVideoID, triggerVideoIDChange, triggerVideoElementChange, getIsInline, getCurrentTime, setCurrentTime, getVideoDuration, verifyCurrentTime, waitForVideo } from "../maze-utils/src/video";
|
||||
import { getChannelIDInfo, getVideo, getIsAdPlaying, getIsLivePremiere, setIsAdPlaying, checkVideoIDChange, getVideoID, getYouTubeVideoID, setupVideoModule, checkIfNewVideoID, isOnInvidious, isOnMobileYouTube, isOnYouTubeMusic, isOnYTTV, getLastNonInlineVideoID, triggerVideoIDChange, triggerVideoElementChange, getIsInline, getCurrentTime, setCurrentTime, getVideoDuration, verifyCurrentTime, waitForVideo } from "../maze-utils/src/video";
|
||||
import { Keybind, StorageChangesObject, isSafari, keybindEquals, keybindToString } from "../maze-utils/src/config";
|
||||
import { findValidElement } from "../maze-utils/src/dom"
|
||||
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||
@@ -76,6 +76,7 @@ let sponsorTimes: SponsorTime[] = [];
|
||||
let existingChaptersImported = false;
|
||||
let importingChaptersWaitingForFocus = false;
|
||||
let importingChaptersWaiting = false;
|
||||
let loopedChapter :SponsorTime = null;
|
||||
// List of open skip notices
|
||||
const skipNotices: SkipNotice[] = [];
|
||||
let upcomingNotice: UpcomingNotice | null = null;
|
||||
@@ -301,6 +302,20 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
||||
case "copyToClipboard":
|
||||
navigator.clipboard.writeText(request.text);
|
||||
break;
|
||||
case "loopChapter":
|
||||
if (!request.UUID){
|
||||
loopedChapter = null;
|
||||
break;
|
||||
}
|
||||
loopedChapter = {...utils.getSponsorTimeFromUUID(sponsorTimes, request.UUID)};
|
||||
loopedChapter.actionType = ActionType.Skip;
|
||||
loopedChapter.segment = [loopedChapter.segment[1], loopedChapter.segment[0]];
|
||||
break;
|
||||
case "getLoopedChapter":
|
||||
sendResponse({
|
||||
UUID: loopedChapter?.UUID,
|
||||
});
|
||||
break;
|
||||
case "importSegments": {
|
||||
const importedSegments = importTimes(request.data, getVideoDuration());
|
||||
let addedSegments = false;
|
||||
@@ -397,6 +412,7 @@ function resetValues() {
|
||||
sponsorTimes = [];
|
||||
existingChaptersImported = false;
|
||||
sponsorSkipped = [];
|
||||
loopedChapter = null;
|
||||
lastResponseStatus = 0;
|
||||
shownSegmentFailedToFetchWarning = false;
|
||||
|
||||
@@ -694,7 +710,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
for (const segment of skipInfo.array) {
|
||||
if (shouldAutoSkip(segment) &&
|
||||
segment.segment[0] >= skipTime[0] && segment.segment[1] <= skipTime[1]
|
||||
&& segment.segment[0] === segment.scheduledTime) { // Don't include artifical scheduled segments (end times for mutes)
|
||||
&& segment.segment[0] === segment.scheduledTime) { // Don't include artificial scheduled segments (end times for mutes)
|
||||
skippingSegments.push(segment);
|
||||
}
|
||||
}
|
||||
@@ -711,7 +727,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
forceVideoTime ||= Math.max(getCurrentTime(), getVirtualTime());
|
||||
|
||||
if ((shouldSkip(currentSkip) || sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment))) {
|
||||
if (forceVideoTime >= skipTime[0] - skipBuffer && forceVideoTime < skipTime[1]) {
|
||||
if (forceVideoTime >= skipTime[0] - skipBuffer && (forceVideoTime < skipTime[1] || skipTime[1] < skipTime[0])) {
|
||||
skipToTime({
|
||||
v: getVideo(),
|
||||
skipTime,
|
||||
@@ -719,7 +735,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
openNotice: skipInfo.openNotice
|
||||
});
|
||||
|
||||
// These are segments that start at the exact same time but need seperate notices
|
||||
// These are segments that start at the exact same time but need separate notices
|
||||
for (const extra of skipInfo.extraIndexes) {
|
||||
const extraSkip = skipInfo.array[extra];
|
||||
if (shouldSkip(extraSkip)) {
|
||||
@@ -752,7 +768,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
||||
}
|
||||
|
||||
// Don't pretend to be earlier than we are, could result in loops
|
||||
if (forcedSkipTime !== null && forceVideoTime > forcedSkipTime) {
|
||||
if (forcedSkipTime !== null && forceVideoTime > forcedSkipTime && skipTime[1] > skipTime[0]) {
|
||||
forcedSkipTime = forceVideoTime;
|
||||
}
|
||||
|
||||
@@ -867,7 +883,8 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
|
||||
const recordedVideoID = videoID || getVideoID();
|
||||
if (currentVideoID !== recordedVideoID || (sponsorTime
|
||||
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))
|
||||
&& !sponsorTimesSubmitting.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))) {
|
||||
&& !sponsorTimesSubmitting.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1])
|
||||
&& (!isLoopedChapter(sponsorTime)))) {
|
||||
// Something has really gone wrong
|
||||
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
|
||||
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
|
||||
@@ -1499,7 +1516,10 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
||||
|
||||
const autoSkipSorter = (segment: ScheduledTime) => {
|
||||
const skipOption = utils.getCategorySelection(segment.category)?.option;
|
||||
if ((skipOption === CategorySkipOption.AutoSkip || shouldAutoSkip(segment))
|
||||
if (segment.hidden !== SponsorHideType.Visible) {
|
||||
// Hidden segments sometimes end up here if another segment is at the same time, use them last
|
||||
return 3;
|
||||
} else if ((skipOption === CategorySkipOption.AutoSkip || shouldAutoSkip(segment))
|
||||
&& segment.actionType === ActionType.Skip) {
|
||||
return 0;
|
||||
} else if (skipOption !== CategorySkipOption.ShowOverlay) {
|
||||
@@ -1537,7 +1557,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
||||
array: submittedArray,
|
||||
index: minSponsorTimeIndex,
|
||||
endIndex: endTimeIndex,
|
||||
extraIndexes, // Segments at same time that need seperate notices
|
||||
extraIndexes, // Segments at same time that need separate notices
|
||||
openNotice: true
|
||||
};
|
||||
} else {
|
||||
@@ -1572,8 +1592,16 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
|
||||
return index;
|
||||
}
|
||||
|
||||
// Default to the normal endTime
|
||||
let latestEndTimeIndex = index;
|
||||
let latestEndTimeIndex = -1;
|
||||
// Default to looped chapter if its end would have been skipped
|
||||
if (loopedChapter
|
||||
&& (loopedChapter.segment[0] > sponsorTimes[index].segment[0]
|
||||
&& loopedChapter.segment[0] <= sponsorTimes[index]?.segment[1])){
|
||||
latestEndTimeIndex = sponsorTimes.length - 1;
|
||||
} else {
|
||||
// or the normal end time otherwise
|
||||
latestEndTimeIndex = index;
|
||||
}
|
||||
|
||||
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||
const currentSegment = sponsorTimes[i].segment;
|
||||
@@ -1616,7 +1644,8 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|
||||
const shouldIncludeTime = (segment: ScheduledTime ) => (minimum === undefined
|
||||
|| ((includeNonIntersectingSegments && segment.scheduledTime >= minimum)
|
||||
|| (includeIntersectingSegments && segment.scheduledTime < minimum
|
||||
&& segment.segment[1] > minimum && shouldSkip(segment)))) // Only include intersecting skippable segments
|
||||
&& ((segment.segment[1] > minimum && shouldSkip(segment)) // Only include intersecting skippable segments
|
||||
|| isLoopedChapter(segment)))))
|
||||
&& (!hideHiddenSponsors || segment.hidden === SponsorHideType.Visible)
|
||||
&& segment.segment.length === 2
|
||||
&& segment.actionType !== ActionType.Poi
|
||||
@@ -1638,6 +1667,12 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|
||||
}
|
||||
});
|
||||
|
||||
if (loopedChapter){
|
||||
possibleTimes.push({
|
||||
...loopedChapter,
|
||||
scheduledTime: loopedChapter.segment[0]})
|
||||
}
|
||||
|
||||
for (let i = 0; i < possibleTimes.length; i++) {
|
||||
if (shouldIncludeTime(possibleTimes[i])) {
|
||||
scheduledTimes.push(possibleTimes[i].scheduledTime);
|
||||
@@ -1891,11 +1926,17 @@ function createButton(baseID: string, title: string, callback: () => void, image
|
||||
}
|
||||
|
||||
function shouldAutoSkip(segment: SponsorTime): boolean {
|
||||
const canSkipNonMusic = !Config.config.skipNonMusicOnlyOnYoutubeMusic || isOnYouTubeMusic();
|
||||
if (segment.category === "music_offtopic" && !canSkipNonMusic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!Config.config.manualSkipOnFullVideo || !sponsorTimes?.some((s) => s.category === segment.category && s.actionType === ActionType.Full))
|
||||
&& (utils.getCategorySelection(segment.category)?.option === CategorySkipOption.AutoSkip ||
|
||||
(Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic")
|
||||
(Config.config.autoSkipOnMusicVideos && canSkipNonMusic && sponsorTimes?.some((s) => s.category === "music_offtopic")
|
||||
&& segment.actionType === ActionType.Skip)
|
||||
|| sponsorTimesSubmitting.some((s) => s.segment === segment.segment));
|
||||
|| sponsorTimesSubmitting.some((s) => s.segment === segment.segment))
|
||||
|| isLoopedChapter(segment);
|
||||
}
|
||||
|
||||
function shouldSkip(segment: SponsorTime): boolean {
|
||||
@@ -1903,7 +1944,13 @@ function shouldSkip(segment: SponsorTime): boolean {
|
||||
&& segment.source !== SponsorSourceType.YouTube
|
||||
&& utils.getCategorySelection(segment.category)?.option !== CategorySkipOption.ShowOverlay)
|
||||
|| (Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic")
|
||||
&& segment.actionType === ActionType.Skip);
|
||||
&& segment.actionType === ActionType.Skip)
|
||||
|| isLoopedChapter(segment);
|
||||
}
|
||||
|
||||
function isLoopedChapter(segment: SponsorTime) :boolean{
|
||||
return !!segment && !!loopedChapter && segment.actionType === ActionType.Skip && segment.segment[1] != undefined
|
||||
&& segment.segment[0] === loopedChapter.segment[0] && segment.segment[1] === loopedChapter.segment[1];
|
||||
}
|
||||
|
||||
/** Creates any missing buttons on the YouTube player if possible. */
|
||||
@@ -2008,7 +2055,7 @@ function startOrEndTimingNewSegment() {
|
||||
return;
|
||||
}
|
||||
|
||||
verifyCurrentTime();
|
||||
verifyCurrentTime(getRealCurrentTime());
|
||||
const roundedTime = Math.round((getRealCurrentTime() + Number.EPSILON) * 1000) / 1000;
|
||||
if (!isSegmentCreationInProgress()) {
|
||||
sponsorTimesSubmitting.push({
|
||||
|
||||
@@ -18,7 +18,8 @@ interface DefaultMessage {
|
||||
| "submitTimes"
|
||||
| "refreshSegments"
|
||||
| "closePopup"
|
||||
| "getLogs";
|
||||
| "getLogs"
|
||||
| "getLoopedChapter";
|
||||
}
|
||||
|
||||
interface BoolValueMessage {
|
||||
@@ -58,6 +59,11 @@ interface ImportSegmentsMessage {
|
||||
data: string;
|
||||
}
|
||||
|
||||
interface LoopChapterMessage {
|
||||
message: "loopChapter";
|
||||
UUID: SegmentUUID;
|
||||
}
|
||||
|
||||
interface KeyDownMessage {
|
||||
message: "keydown";
|
||||
key: string;
|
||||
@@ -70,7 +76,7 @@ interface KeyDownMessage {
|
||||
metaKey: boolean;
|
||||
}
|
||||
|
||||
export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | IsInfoFoundMessage | SkipMessage | SubmitVoteMessage | HideSegmentMessage | CopyToClipboardMessage | ImportSegmentsMessage | KeyDownMessage);
|
||||
export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | IsInfoFoundMessage | SkipMessage | SubmitVoteMessage | HideSegmentMessage | CopyToClipboardMessage | ImportSegmentsMessage | KeyDownMessage | LoopChapterMessage);
|
||||
|
||||
export interface IsInfoFoundMessageResponse {
|
||||
found: boolean;
|
||||
@@ -97,6 +103,10 @@ export interface IsChannelWhitelistedResponse {
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export interface LoopedChapterResponse {
|
||||
UUID: SegmentUUID;
|
||||
}
|
||||
|
||||
export type MessageResponse =
|
||||
IsInfoFoundMessageResponse
|
||||
| GetVideoIdResponse
|
||||
@@ -107,7 +117,8 @@ export type MessageResponse =
|
||||
| VoteResponse
|
||||
| ImportSegmentsResponse
|
||||
| RefreshSegmentsResponse
|
||||
| LogResponse;
|
||||
| LogResponse
|
||||
| LoopedChapterResponse;
|
||||
|
||||
export interface VoteResponse {
|
||||
successType: number;
|
||||
|
||||
@@ -114,8 +114,25 @@ async function init() {
|
||||
|
||||
if (await shouldHideOption(optionsElements[i]) || (dependentOn && (isDependentOnReversed ? Config.config[dependentOnName] : !Config.config[dependentOnName]))) {
|
||||
optionsElements[i].classList.add("hidden", "hiding");
|
||||
if (!dependentOn)
|
||||
if (!dependentOn) {
|
||||
if (optionsElements[i].getAttribute("data-no-safari") === "true" && optionsElements[i].id === "support-invidious") {
|
||||
// Put message about being disabled on safari
|
||||
const infoBox = document.createElement("div");
|
||||
infoBox.innerText = chrome.i18n.getMessage("invidiousDisabledSafari");
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.style.display = "block";
|
||||
const url = "https://bugs.webkit.org/show_bug.cgi?id=290508";
|
||||
link.href = url;
|
||||
link.innerText = url;
|
||||
|
||||
infoBox.appendChild(link);
|
||||
|
||||
optionsElements[i].parentElement.insertBefore(infoBox, optionsElements[i].nextSibling);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const option = optionsElements[i].getAttribute("data-sync");
|
||||
|
||||
40
src/popup.ts
40
src/popup.ts
@@ -13,6 +13,7 @@ import {
|
||||
IsChannelWhitelistedResponse,
|
||||
IsInfoFoundMessageResponse,
|
||||
LogResponse,
|
||||
LoopedChapterResponse,
|
||||
Message,
|
||||
MessageResponse,
|
||||
PopupMessage,
|
||||
@@ -539,7 +540,7 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
}
|
||||
|
||||
//display the video times from the array at the top, in a different section
|
||||
function displayDownloadedSponsorTimes(sponsorTimes: SponsorTime[], time: number) {
|
||||
async function displayDownloadedSponsorTimes(sponsorTimes: SponsorTime[], time: number) {
|
||||
let currentSegmentTab = segmentTab;
|
||||
if (!sponsorTimes.some((segment) => segment.actionType === ActionType.Chapter && segment.source !== SponsorSourceType.YouTube)) {
|
||||
PageElements.issueReporterTabs.classList.add("hidden");
|
||||
@@ -554,6 +555,9 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const response = await sendTabMessageAsync({message : "getLoopedChapter"}) as LoopedChapterResponse
|
||||
const loopedChapter = response.UUID;
|
||||
|
||||
// Sort list by start time
|
||||
const downloadedTimes = sponsorTimes
|
||||
.filter((segment) => {
|
||||
@@ -728,6 +732,37 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
})
|
||||
});
|
||||
|
||||
const loopButton = document.createElement("input");
|
||||
const loopButtonIcon = document.createElement("img");
|
||||
loopButtonIcon.src = loopedChapter === UUID ? chrome.runtime.getURL("icons/looped.svg") : chrome.runtime.getURL("icons/loop.svg");
|
||||
loopButtonIcon.className = "loopButtonIcon";
|
||||
loopButton.type = "checkbox";
|
||||
loopButton.checked = loopedChapter === UUID;
|
||||
loopButton.id = "loopChapterButtonContainer" + UUID;
|
||||
loopButton.className = "loopButton hidden";
|
||||
|
||||
loopButton.addEventListener("click", () => {
|
||||
const stopAnimation = AnimationUtils.applyLoadingAnimation(loopButtonIcon, 0.4);
|
||||
stopAnimation();
|
||||
if (loopButton.checked) {
|
||||
document.querySelectorAll(".loopButton").forEach((e :HTMLInputElement) => e.checked = false);
|
||||
document.querySelectorAll(".loopButtonIcon").forEach((e :HTMLImageElement) => e.src = chrome.runtime.getURL("icons/loop.svg"));
|
||||
sendTabMessage({message: "loopChapter", UUID : UUID})
|
||||
loopButton.checked = true;
|
||||
} else {
|
||||
sendTabMessage({message: "loopChapter", UUID : null})
|
||||
loopButton.checked = false;
|
||||
}
|
||||
loopButtonIcon.src = loopButton.checked ? chrome.runtime.getURL("icons/looped.svg") : chrome.runtime.getURL("icons/loop.svg");
|
||||
loopButtonLabel.title = loopButton.checked ? chrome.i18n.getMessage("unloopChapter") : chrome.i18n.getMessage("loopChapter");
|
||||
});
|
||||
const loopButtonLabel = document.createElement("label");
|
||||
loopButtonLabel.setAttribute("for", loopButton.id);
|
||||
loopButtonLabel.className = "voteButton";
|
||||
loopButtonLabel.title = loopedChapter === UUID ? chrome.i18n.getMessage("unloopChapter") : chrome.i18n.getMessage("loopChapter");
|
||||
loopButtonLabel.appendChild(loopButtonIcon);
|
||||
loopButtonLabel.appendChild(loopButton);
|
||||
|
||||
const skipButton = document.createElement("img");
|
||||
skipButton.id = "sponsorTimesSkipButtonContainer" + UUID;
|
||||
skipButton.className = "voteButton";
|
||||
@@ -743,6 +778,9 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
voteButtonsContainer.appendChild(upvoteButton);
|
||||
voteButtonsContainer.appendChild(downvoteButton);
|
||||
voteButtonsContainer.appendChild(uuidButton);
|
||||
if (downloadedTimes[i].actionType === ActionType.Chapter) {
|
||||
voteButtonsContainer.appendChild(loopButtonLabel);
|
||||
}
|
||||
if (downloadedTimes[i].actionType === ActionType.Skip || downloadedTimes[i].actionType === ActionType.Mute
|
||||
|| downloadedTimes[i].actionType === ActionType.Poi
|
||||
&& [SponsorHideType.Visible, SponsorHideType.Hidden].includes(downloadedTimes[i].hidden)) {
|
||||
|
||||
@@ -4,6 +4,8 @@ import Config from "../config";
|
||||
|
||||
export function getControls(): HTMLElement {
|
||||
const controlsSelectors = [
|
||||
// New YouTube (2025 April)
|
||||
".ytp-right-controls-right",
|
||||
// YouTube
|
||||
".ytp-right-controls",
|
||||
// Mobile YouTube
|
||||
|
||||
@@ -49,8 +49,8 @@ async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse>
|
||||
const hashParams = getHashParams();
|
||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||
|
||||
const hashPrefix = (await getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue;
|
||||
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix];
|
||||
const hashPrefix = (await getHash(videoID, 1)).slice(0, 5) as VideoID & HashedValue;
|
||||
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix.slice(0, 4)];
|
||||
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||
categories: CompileConfig.categoryList,
|
||||
actionTypes: ActionTypes,
|
||||
|
||||
@@ -127,6 +127,8 @@ async function editSegments(driver: WebDriver, index: number, expectedStartTimeB
|
||||
await endTimeBox.clear();
|
||||
await endTimeBox.sendKeys(endTime);
|
||||
|
||||
await driver.sleep(1000);
|
||||
|
||||
editButton = await driver.findElement(By.id("sponsorTimeEditButtonSubmissionNotice" + index));
|
||||
await editButton.click();
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class BuildManifest {
|
||||
} else if (this.options.browser.toLowerCase() === "safari") {
|
||||
mergeObjects(manifest, manifestV2ManifestExtra);
|
||||
mergeObjects(manifest, safariManifestExtra);
|
||||
manifest.optional_permissions = manifest.optional_permissions.filter((a) => a !== "*://*/*");
|
||||
}
|
||||
|
||||
if (this.options.stream === "beta") {
|
||||
|
||||
Reference in New Issue
Block a user