mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-17 13:08:54 +03:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7db6d58c5 | ||
|
|
56a2d76f38 | ||
|
|
4a7d431a17 | ||
|
|
a91ff4e49f | ||
|
|
fcb9f32d4c | ||
|
|
cf4d13a756 | ||
|
|
75d0043e45 | ||
|
|
2391337ee2 | ||
|
|
1074f653b2 | ||
|
|
a12601180e | ||
|
|
3a7716240d | ||
|
|
93f44859b2 | ||
|
|
376ce21eae | ||
|
|
19635d2f06 | ||
|
|
b2a5539324 | ||
|
|
01df1e655e | ||
|
|
467e1cae50 | ||
|
|
822e00a46a | ||
|
|
74433fe751 | ||
|
|
a45690b26e | ||
|
|
f69d6736d4 | ||
|
|
ae7916b7e7 | ||
|
|
3bb4cf4fbc | ||
|
|
8b305ba5ec | ||
|
|
0fdcade27e | ||
|
|
897ebda42d | ||
|
|
ddd0b4ea05 | ||
|
|
4826bcf7df | ||
|
|
09d8bc4425 | ||
|
|
aeb3299c50 | ||
|
|
be11012096 | ||
|
|
22da289151 | ||
|
|
67a16a7b34 | ||
|
|
312ac788b3 | ||
|
|
8117f3c215 | ||
|
|
3c1d3f5d8e | ||
|
|
7cbd1fa59d | ||
|
|
f9e9c5c868 | ||
|
|
3b89ef0487 | ||
|
|
e656dd8d8b |
12
.github/workflows/tests.yml
vendored
12
.github/workflows/tests.yml
vendored
@@ -17,9 +17,21 @@ jobs:
|
|||||||
node-version: '18'
|
node-version: '18'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: sudo apt-get install chromium-chromedriver
|
- 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
|
- name: Copy configuration
|
||||||
run: cp config.json.example config.json
|
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
|
- name: Run tests
|
||||||
run: npm run test
|
run: npm run test
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_fullName__",
|
"name": "__MSG_fullName__",
|
||||||
"short_name": "SponsorBlock",
|
"short_name": "SponsorBlock",
|
||||||
"version": "5.11.11",
|
"version": "5.12.4",
|
||||||
"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: b22477a864...5d5e0b096b
881
package-lock.json
generated
881
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
|||||||
"@types/wicg-mediasession": "^1.1.4",
|
"@types/wicg-mediasession": "^1.1.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
||||||
"@typescript-eslint/parser": "^5.54.1",
|
"@typescript-eslint/parser": "^5.54.1",
|
||||||
"chromedriver": "^110.0.0",
|
"chromedriver": "^135.0.0",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
|
|||||||
Submodule public/_locales updated: e9efadcf82...b6bb85b7f7
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 |
@@ -293,7 +293,7 @@
|
|||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
transition: background-color 0.2s ease-in-out;
|
transition: background-color 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
.sbControlsMenu-item:hover {
|
.sbControlsMenu-item:hover, .sbControlsMenu-item:focus {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
.sbControlsMenu-itemIcon {
|
.sbControlsMenu-itemIcon {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
<div id="issueReporterImportExport" class="hidden">
|
<div id="issueReporterImportExport" class="hidden">
|
||||||
<div id="importExportButtons">
|
<div id="importExportButtons">
|
||||||
<button id="importSegmentsButton" title="__MSG_importSegments__">
|
<button id="importSegmentsButton" title="__MSG_importSegments__">
|
||||||
<img src="/icons/import.svg" alt="Refresh icon" id="importSegments" />
|
<img src="/icons/import.svg" alt="Import icon" id="importSegments" />
|
||||||
</button>
|
</button>
|
||||||
<button id="exportSegmentsButton" class="hidden" title="__MSG_exportSegments__">
|
<button id="exportSegmentsButton" class="hidden" title="__MSG_exportSegments__">
|
||||||
<img src="/icons/export.svg" alt="Export icon" id="exportSegments" />
|
<img src="/icons/export.svg" alt="Export icon" id="exportSegments" />
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<!-- Toggle Box -->
|
<!-- Toggle Box -->
|
||||||
<div class="sbControlsMenu">
|
<div class="sbControlsMenu">
|
||||||
<label id="whitelistButton" for="whitelistToggle" class="hidden sbControlsMenu-item">
|
<label id="whitelistButton" for="whitelistToggle" class="hidden sbControlsMenu-item" role="button" tabIndex="0">
|
||||||
<input type="checkbox" style="display: none" id="whitelistToggle">
|
<input type="checkbox" style="display: none" id="whitelistToggle">
|
||||||
<svg viewBox="0 0 24 24" width="23" height="23" class="SBWhitelistIcon sbControlsMenu-itemIcon">
|
<svg viewBox="0 0 24 24" width="23" height="23" class="SBWhitelistIcon sbControlsMenu-itemIcon">
|
||||||
<path d="M24 10H14V0h-4v10H0v4h10v10h4V14h10z" />
|
<path d="M24 10H14V0h-4v10H0v4h10v10h4V14h10z" />
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
<span id="unwhitelistChannel" style="display: none">__MSG_removeFromWhitelist__</span>
|
<span id="unwhitelistChannel" style="display: none">__MSG_removeFromWhitelist__</span>
|
||||||
</label>
|
</label>
|
||||||
<!--github: mbledkowski/toggle-switch-->
|
<!--github: mbledkowski/toggle-switch-->
|
||||||
<label id="disableExtension" for="toggleSwitch" class="toggleSwitchContainer sbControlsMenu-item">
|
<label id="disableExtension" for="toggleSwitch" class="toggleSwitchContainer sbControlsMenu-item" role="button" tabIndex="0">
|
||||||
<span class="toggleSwitchContainer-switch">
|
<span class="toggleSwitchContainer-switch">
|
||||||
<input type="checkbox" style="display: none" id="toggleSwitch" checked>
|
<input type="checkbox" style="display: none" id="toggleSwitch" checked>
|
||||||
<span class="switchBg shadow"></span>
|
<span class="switchBg shadow"></span>
|
||||||
|
|||||||
@@ -243,6 +243,9 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
|
|||||||
return [{
|
return [{
|
||||||
configKey: "autoSkipOnMusicVideos",
|
configKey: "autoSkipOnMusicVideos",
|
||||||
label: chrome.i18n.getMessage("autoSkipOnMusicVideos"),
|
label: chrome.i18n.getMessage("autoSkipOnMusicVideos"),
|
||||||
|
}, {
|
||||||
|
configKey: "skipNonMusicOnlyOnYoutubeMusic",
|
||||||
|
label: chrome.i18n.getMessage("skipNonMusicOnlyOnYoutubeMusic"),
|
||||||
}];
|
}];
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ interface SBConfig {
|
|||||||
donateClicked: number;
|
donateClicked: number;
|
||||||
autoHideInfoButton: boolean;
|
autoHideInfoButton: boolean;
|
||||||
autoSkipOnMusicVideos: boolean;
|
autoSkipOnMusicVideos: boolean;
|
||||||
|
skipNonMusicOnlyOnYoutubeMusic: boolean;
|
||||||
colorPalette: {
|
colorPalette: {
|
||||||
red: string;
|
red: string;
|
||||||
white: string;
|
white: string;
|
||||||
@@ -322,6 +323,7 @@ const syncDefaults = {
|
|||||||
donateClicked: 0,
|
donateClicked: 0,
|
||||||
autoHideInfoButton: true,
|
autoHideInfoButton: true,
|
||||||
autoSkipOnMusicVideos: false,
|
autoSkipOnMusicVideos: false,
|
||||||
|
skipNonMusicOnlyOnYoutubeMusic: false,
|
||||||
scrollToEditTimeUpdate: false, // false means the tooltip will be shown
|
scrollToEditTimeUpdate: false, // false means the tooltip will be shown
|
||||||
categoryPillUpdate: false,
|
categoryPillUpdate: false,
|
||||||
showChapterInfoMessage: true,
|
showChapterInfoMessage: true,
|
||||||
|
|||||||
131
src/content.ts
131
src/content.ts
@@ -33,9 +33,9 @@ import { logDebug, logWarn } from "./utils/logger";
|
|||||||
import { importTimes } from "./utils/exporter";
|
import { importTimes } from "./utils/exporter";
|
||||||
import { ChapterVote } from "./render/ChapterVote";
|
import { ChapterVote } from "./render/ChapterVote";
|
||||||
import { openWarningDialog } from "./utils/warnings";
|
import { openWarningDialog } from "./utils/warnings";
|
||||||
import { isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
import { extensionUserAgent, isFirefoxOrSafari, waitFor } from "../maze-utils/src";
|
||||||
import { getErrorMessage, getFormattedTime } from "../maze-utils/src/formating";
|
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 { Keybind, StorageChangesObject, isSafari, keybindEquals, keybindToString } from "../maze-utils/src/config";
|
||||||
import { findValidElement } from "../maze-utils/src/dom"
|
import { findValidElement } from "../maze-utils/src/dom"
|
||||||
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
import { getHash, HashedValue } from "../maze-utils/src/hash";
|
||||||
@@ -76,6 +76,7 @@ let sponsorTimes: SponsorTime[] = [];
|
|||||||
let existingChaptersImported = false;
|
let existingChaptersImported = false;
|
||||||
let importingChaptersWaitingForFocus = false;
|
let importingChaptersWaitingForFocus = false;
|
||||||
let importingChaptersWaiting = false;
|
let importingChaptersWaiting = false;
|
||||||
|
let loopedChapter :SponsorTime = null;
|
||||||
// List of open skip notices
|
// List of open skip notices
|
||||||
const skipNotices: SkipNotice[] = [];
|
const skipNotices: SkipNotice[] = [];
|
||||||
let upcomingNotice: UpcomingNotice | null = null;
|
let upcomingNotice: UpcomingNotice | null = null;
|
||||||
@@ -301,6 +302,20 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo
|
|||||||
case "copyToClipboard":
|
case "copyToClipboard":
|
||||||
navigator.clipboard.writeText(request.text);
|
navigator.clipboard.writeText(request.text);
|
||||||
break;
|
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": {
|
case "importSegments": {
|
||||||
const importedSegments = importTimes(request.data, getVideoDuration());
|
const importedSegments = importTimes(request.data, getVideoDuration());
|
||||||
let addedSegments = false;
|
let addedSegments = false;
|
||||||
@@ -397,6 +412,7 @@ function resetValues() {
|
|||||||
sponsorTimes = [];
|
sponsorTimes = [];
|
||||||
existingChaptersImported = false;
|
existingChaptersImported = false;
|
||||||
sponsorSkipped = [];
|
sponsorSkipped = [];
|
||||||
|
loopedChapter = null;
|
||||||
lastResponseStatus = 0;
|
lastResponseStatus = 0;
|
||||||
shownSegmentFailedToFetchWarning = false;
|
shownSegmentFailedToFetchWarning = false;
|
||||||
|
|
||||||
@@ -694,7 +710,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
for (const segment of skipInfo.array) {
|
for (const segment of skipInfo.array) {
|
||||||
if (shouldAutoSkip(segment) &&
|
if (shouldAutoSkip(segment) &&
|
||||||
segment.segment[0] >= skipTime[0] && segment.segment[1] <= skipTime[1]
|
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);
|
skippingSegments.push(segment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,7 +727,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
forceVideoTime ||= Math.max(getCurrentTime(), getVirtualTime());
|
forceVideoTime ||= Math.max(getCurrentTime(), getVirtualTime());
|
||||||
|
|
||||||
if ((shouldSkip(currentSkip) || sponsorTimesSubmitting?.some((segment) => segment.segment === currentSkip.segment))) {
|
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({
|
skipToTime({
|
||||||
v: getVideo(),
|
v: getVideo(),
|
||||||
skipTime,
|
skipTime,
|
||||||
@@ -719,7 +735,7 @@ async function startSponsorSchedule(includeIntersectingSegments = false, current
|
|||||||
openNotice: skipInfo.openNotice
|
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) {
|
for (const extra of skipInfo.extraIndexes) {
|
||||||
const extraSkip = skipInfo.array[extra];
|
const extraSkip = skipInfo.array[extra];
|
||||||
if (shouldSkip(extraSkip)) {
|
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
|
// 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;
|
forcedSkipTime = forceVideoTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,7 +883,8 @@ function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boole
|
|||||||
const recordedVideoID = videoID || getVideoID();
|
const recordedVideoID = videoID || getVideoID();
|
||||||
if (currentVideoID !== recordedVideoID || (sponsorTime
|
if (currentVideoID !== recordedVideoID || (sponsorTime
|
||||||
&& (!sponsorTimes || !sponsorTimes?.some((time) => time.segment[0] === sponsorTime.segment[0] && time.segment[1] === sponsorTime.segment[1]))
|
&& (!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
|
// 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] The videoID recorded when trying to skip is different than what it should be.");
|
||||||
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
|
console.error("[SponsorBlock] VideoID recorded: " + recordedVideoID + ". Actual VideoID: " + currentVideoID);
|
||||||
@@ -1499,7 +1516,10 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
|||||||
|
|
||||||
const autoSkipSorter = (segment: ScheduledTime) => {
|
const autoSkipSorter = (segment: ScheduledTime) => {
|
||||||
const skipOption = utils.getCategorySelection(segment.category)?.option;
|
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) {
|
&& segment.actionType === ActionType.Skip) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (skipOption !== CategorySkipOption.ShowOverlay) {
|
} else if (skipOption !== CategorySkipOption.ShowOverlay) {
|
||||||
@@ -1537,7 +1557,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
|
|||||||
array: submittedArray,
|
array: submittedArray,
|
||||||
index: minSponsorTimeIndex,
|
index: minSponsorTimeIndex,
|
||||||
endIndex: endTimeIndex,
|
endIndex: endTimeIndex,
|
||||||
extraIndexes, // Segments at same time that need seperate notices
|
extraIndexes, // Segments at same time that need separate notices
|
||||||
openNotice: true
|
openNotice: true
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -1572,8 +1592,16 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to the normal endTime
|
let latestEndTimeIndex = -1;
|
||||||
let latestEndTimeIndex = index;
|
// 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++) {
|
for (let i = 0; i < sponsorTimes?.length; i++) {
|
||||||
const currentSegment = sponsorTimes[i].segment;
|
const currentSegment = sponsorTimes[i].segment;
|
||||||
@@ -1616,7 +1644,8 @@ function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments:
|
|||||||
const shouldIncludeTime = (segment: ScheduledTime ) => (minimum === undefined
|
const shouldIncludeTime = (segment: ScheduledTime ) => (minimum === undefined
|
||||||
|| ((includeNonIntersectingSegments && segment.scheduledTime >= minimum)
|
|| ((includeNonIntersectingSegments && segment.scheduledTime >= minimum)
|
||||||
|| (includeIntersectingSegments && 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)
|
&& (!hideHiddenSponsors || segment.hidden === SponsorHideType.Visible)
|
||||||
&& segment.segment.length === 2
|
&& segment.segment.length === 2
|
||||||
&& segment.actionType !== ActionType.Poi
|
&& 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++) {
|
for (let i = 0; i < possibleTimes.length; i++) {
|
||||||
if (shouldIncludeTime(possibleTimes[i])) {
|
if (shouldIncludeTime(possibleTimes[i])) {
|
||||||
scheduledTimes.push(possibleTimes[i].scheduledTime);
|
scheduledTimes.push(possibleTimes[i].scheduledTime);
|
||||||
@@ -1891,11 +1926,17 @@ function createButton(baseID: string, title: string, callback: () => void, image
|
|||||||
}
|
}
|
||||||
|
|
||||||
function shouldAutoSkip(segment: SponsorTime): boolean {
|
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))
|
return (!Config.config.manualSkipOnFullVideo || !sponsorTimes?.some((s) => s.category === segment.category && s.actionType === ActionType.Full))
|
||||||
&& (utils.getCategorySelection(segment.category)?.option === CategorySkipOption.AutoSkip ||
|
&& (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)
|
&& 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 {
|
function shouldSkip(segment: SponsorTime): boolean {
|
||||||
@@ -1903,7 +1944,13 @@ function shouldSkip(segment: SponsorTime): boolean {
|
|||||||
&& segment.source !== SponsorSourceType.YouTube
|
&& segment.source !== SponsorSourceType.YouTube
|
||||||
&& utils.getCategorySelection(segment.category)?.option !== CategorySkipOption.ShowOverlay)
|
&& utils.getCategorySelection(segment.category)?.option !== CategorySkipOption.ShowOverlay)
|
||||||
|| (Config.config.autoSkipOnMusicVideos && sponsorTimes?.some((s) => s.category === "music_offtopic")
|
|| (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. */
|
/** Creates any missing buttons on the YouTube player if possible. */
|
||||||
@@ -2423,7 +2470,7 @@ async function sendSubmitMessage(): Promise<boolean> {
|
|||||||
userID: Config.config.userID,
|
userID: Config.config.userID,
|
||||||
segments: sponsorTimesSubmitting,
|
segments: sponsorTimesSubmitting,
|
||||||
videoDuration: getVideoDuration(),
|
videoDuration: getVideoDuration(),
|
||||||
userAgent: `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
userAgent: extensionUserAgent(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
@@ -2570,23 +2617,13 @@ async function handleKeybindVote(type: number): Promise<void>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addHotkeyListener(): void {
|
function addHotkeyListener(): void {
|
||||||
document.addEventListener("keydown", hotkeyListener);
|
document.addEventListener("keydown", hotkeyListener, true);
|
||||||
|
document.addEventListener("keyup", hotkeyPropagationListener, true);
|
||||||
|
|
||||||
const onLoad = () => {
|
addCleanupListener(() => {
|
||||||
// Allow us to stop propagation to YouTube by being deeper
|
document.body.removeEventListener("keydown", hotkeyListener, true);
|
||||||
document.removeEventListener("keydown", hotkeyListener);
|
document.body.removeEventListener("keyup", hotkeyPropagationListener, true);
|
||||||
document.body.addEventListener("keydown", hotkeyListener);
|
});
|
||||||
|
|
||||||
addCleanupListener(() => {
|
|
||||||
document.body.removeEventListener("keydown", hotkeyListener);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (document.readyState === "complete") {
|
|
||||||
onLoad();
|
|
||||||
} else {
|
|
||||||
document.addEventListener("DOMContentLoaded", onLoad);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hotkeyListener(e: KeyboardEvent): void {
|
function hotkeyListener(e: KeyboardEvent): void {
|
||||||
@@ -2616,7 +2653,7 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||||||
const downvoteKey = Config.config.downvoteKeybind;
|
const downvoteKey = Config.config.downvoteKeybind;
|
||||||
|
|
||||||
if (keybindEquals(key, skipKey)) {
|
if (keybindEquals(key, skipKey)) {
|
||||||
if (activeSkipKeybindElement) {
|
if (activeSkipKeybindElement && !(activeSkipKeybindElement instanceof SkipButtonControlBar)) {
|
||||||
activeSkipKeybindElement.toggleSkip.call(activeSkipKeybindElement);
|
activeSkipKeybindElement.toggleSkip.call(activeSkipKeybindElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2666,6 +2703,32 @@ function hotkeyListener(e: KeyboardEvent): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hotkeyPropagationListener(e: KeyboardEvent): void {
|
||||||
|
if ((["textarea", "input"].includes(document.activeElement?.tagName?.toLowerCase())
|
||||||
|
|| (document.activeElement as HTMLElement)?.isContentEditable
|
||||||
|
|| document.activeElement?.id?.toLowerCase()?.match(/editable|input/))
|
||||||
|
&& document.hasFocus()) return;
|
||||||
|
|
||||||
|
const key: Keybind = {
|
||||||
|
key: e.key,
|
||||||
|
code: e.code,
|
||||||
|
alt: e.altKey,
|
||||||
|
ctrl: e.ctrlKey,
|
||||||
|
shift: e.shiftKey
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextChapterKey = Config.config.nextChapterKeybind;
|
||||||
|
const previousChapterKey = Config.config.previousChapterKeybind;
|
||||||
|
|
||||||
|
if (keybindEquals(key, nextChapterKey)) {
|
||||||
|
if (sponsorTimes.length > 0) e.stopPropagation();
|
||||||
|
return;
|
||||||
|
} else if (keybindEquals(key, previousChapterKey)) {
|
||||||
|
if (sponsorTimes.length > 0) e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the CSS to the page if needed. Required on optional sites with Chrome.
|
* Adds the CSS to the page if needed. Required on optional sites with Chrome.
|
||||||
*/
|
*/
|
||||||
@@ -2715,7 +2778,7 @@ function showTimeWithoutSkips(skippedDuration: number): void {
|
|||||||
isOnInvidious() ? ".vjs-duration" :
|
isOnInvidious() ? ".vjs-duration" :
|
||||||
isOnYTTV() ? ".ypl-full-controls .ypmcs-control .time-info-bar" :
|
isOnYTTV() ? ".ypl-full-controls .ypmcs-control .time-info-bar" :
|
||||||
isOnMobileYouTube() ? ".ytwPlayerTimeDisplayContent" :
|
isOnMobileYouTube() ? ".ytwPlayerTimeDisplayContent" :
|
||||||
".ytp-time-display.notranslate .ytp-time-wrapper";
|
".ytp-time-display.notranslate .ytp-time-wrapper .ytp-time-contents";
|
||||||
const display = document.querySelector(selector);
|
const display = document.querySelector(selector);
|
||||||
if (!display) return;
|
if (!display) return;
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ interface DefaultMessage {
|
|||||||
| "submitTimes"
|
| "submitTimes"
|
||||||
| "refreshSegments"
|
| "refreshSegments"
|
||||||
| "closePopup"
|
| "closePopup"
|
||||||
| "getLogs";
|
| "getLogs"
|
||||||
|
| "getLoopedChapter";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BoolValueMessage {
|
interface BoolValueMessage {
|
||||||
@@ -58,6 +59,11 @@ interface ImportSegmentsMessage {
|
|||||||
data: string;
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoopChapterMessage {
|
||||||
|
message: "loopChapter";
|
||||||
|
UUID: SegmentUUID;
|
||||||
|
}
|
||||||
|
|
||||||
interface KeyDownMessage {
|
interface KeyDownMessage {
|
||||||
message: "keydown";
|
message: "keydown";
|
||||||
key: string;
|
key: string;
|
||||||
@@ -70,7 +76,7 @@ interface KeyDownMessage {
|
|||||||
metaKey: boolean;
|
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 {
|
export interface IsInfoFoundMessageResponse {
|
||||||
found: boolean;
|
found: boolean;
|
||||||
@@ -97,6 +103,10 @@ export interface IsChannelWhitelistedResponse {
|
|||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LoopedChapterResponse {
|
||||||
|
UUID: SegmentUUID;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageResponse =
|
export type MessageResponse =
|
||||||
IsInfoFoundMessageResponse
|
IsInfoFoundMessageResponse
|
||||||
| GetVideoIdResponse
|
| GetVideoIdResponse
|
||||||
@@ -107,7 +117,8 @@ export type MessageResponse =
|
|||||||
| VoteResponse
|
| VoteResponse
|
||||||
| ImportSegmentsResponse
|
| ImportSegmentsResponse
|
||||||
| RefreshSegmentsResponse
|
| RefreshSegmentsResponse
|
||||||
| LogResponse;
|
| LogResponse
|
||||||
|
| LoopedChapterResponse;
|
||||||
|
|
||||||
export interface VoteResponse {
|
export interface VoteResponse {
|
||||||
successType: number;
|
successType: number;
|
||||||
|
|||||||
40
src/popup.ts
40
src/popup.ts
@@ -13,6 +13,7 @@ import {
|
|||||||
IsChannelWhitelistedResponse,
|
IsChannelWhitelistedResponse,
|
||||||
IsInfoFoundMessageResponse,
|
IsInfoFoundMessageResponse,
|
||||||
LogResponse,
|
LogResponse,
|
||||||
|
LoopedChapterResponse,
|
||||||
Message,
|
Message,
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
PopupMessage,
|
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
|
//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;
|
let currentSegmentTab = segmentTab;
|
||||||
if (!sponsorTimes.some((segment) => segment.actionType === ActionType.Chapter && segment.source !== SponsorSourceType.YouTube)) {
|
if (!sponsorTimes.some((segment) => segment.actionType === ActionType.Chapter && segment.source !== SponsorSourceType.YouTube)) {
|
||||||
PageElements.issueReporterTabs.classList.add("hidden");
|
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
|
// Sort list by start time
|
||||||
const downloadedTimes = sponsorTimes
|
const downloadedTimes = sponsorTimes
|
||||||
.filter((segment) => {
|
.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");
|
const skipButton = document.createElement("img");
|
||||||
skipButton.id = "sponsorTimesSkipButtonContainer" + UUID;
|
skipButton.id = "sponsorTimesSkipButtonContainer" + UUID;
|
||||||
skipButton.className = "voteButton";
|
skipButton.className = "voteButton";
|
||||||
@@ -743,6 +778,9 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
|||||||
voteButtonsContainer.appendChild(upvoteButton);
|
voteButtonsContainer.appendChild(upvoteButton);
|
||||||
voteButtonsContainer.appendChild(downvoteButton);
|
voteButtonsContainer.appendChild(downvoteButton);
|
||||||
voteButtonsContainer.appendChild(uuidButton);
|
voteButtonsContainer.appendChild(uuidButton);
|
||||||
|
if (downloadedTimes[i].actionType === ActionType.Chapter) {
|
||||||
|
voteButtonsContainer.appendChild(loopButtonLabel);
|
||||||
|
}
|
||||||
if (downloadedTimes[i].actionType === ActionType.Skip || downloadedTimes[i].actionType === ActionType.Mute
|
if (downloadedTimes[i].actionType === ActionType.Skip || downloadedTimes[i].actionType === ActionType.Mute
|
||||||
|| downloadedTimes[i].actionType === ActionType.Poi
|
|| downloadedTimes[i].actionType === ActionType.Poi
|
||||||
&& [SponsorHideType.Visible, SponsorHideType.Hidden].includes(downloadedTimes[i].hidden)) {
|
&& [SponsorHideType.Visible, SponsorHideType.Hidden].includes(downloadedTimes[i].hidden)) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import Config from "../config";
|
|||||||
|
|
||||||
export function getControls(): HTMLElement {
|
export function getControls(): HTMLElement {
|
||||||
const controlsSelectors = [
|
const controlsSelectors = [
|
||||||
|
// New YouTube (2025 April)
|
||||||
|
".ytp-right-controls-right",
|
||||||
// YouTube
|
// YouTube
|
||||||
".ytp-right-controls",
|
".ytp-right-controls",
|
||||||
// Mobile YouTube
|
// Mobile YouTube
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as CompileConfig from "../../config.json";
|
|||||||
import { ActionType, ActionTypes, SponsorSourceType, SponsorTime, VideoID } from "../types";
|
import { ActionType, ActionTypes, SponsorSourceType, SponsorTime, VideoID } from "../types";
|
||||||
import { getHashParams } from "./pageUtils";
|
import { getHashParams } from "./pageUtils";
|
||||||
import { asyncRequestToServer } from "./requests";
|
import { asyncRequestToServer } from "./requests";
|
||||||
|
import { extensionUserAgent } from "../../maze-utils/src";
|
||||||
|
|
||||||
const segmentDataCache = new DataCache<VideoID, SegmentResponse>(() => {
|
const segmentDataCache = new DataCache<VideoID, SegmentResponse>(() => {
|
||||||
return {
|
return {
|
||||||
@@ -50,14 +51,14 @@ async function fetchSegmentsForVideo(videoID: VideoID): Promise<SegmentResponse>
|
|||||||
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment;
|
||||||
|
|
||||||
const hashPrefix = (await getHash(videoID, 1)).slice(0, 5) as VideoID & HashedValue;
|
const hashPrefix = (await getHash(videoID, 1)).slice(0, 5) as VideoID & HashedValue;
|
||||||
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix];
|
const hasDownvotedSegments = !!Config.local.downvotedSegments[hashPrefix.slice(0, 4)];
|
||||||
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
const response = await asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, {
|
||||||
categories: CompileConfig.categoryList,
|
categories: CompileConfig.categoryList,
|
||||||
actionTypes: ActionTypes,
|
actionTypes: ActionTypes,
|
||||||
trimUUIDs: hasDownvotedSegments ? null : 5,
|
trimUUIDs: hasDownvotedSegments ? null : 5,
|
||||||
...extraRequestData
|
...extraRequestData
|
||||||
}, {
|
}, {
|
||||||
"X-CLIENT-NAME": `${chrome.runtime.id}/v${chrome.runtime.getManifest().version}`
|
"X-CLIENT-NAME": extensionUserAgent(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@@ -103,4 +104,4 @@ function getEnabledActionTypes(forceFullVideo = false): ActionType[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return actionTypes;
|
return actionTypes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ async function editSegments(driver: WebDriver, index: number, expectedStartTimeB
|
|||||||
await endTimeBox.clear();
|
await endTimeBox.clear();
|
||||||
await endTimeBox.sendKeys(endTime);
|
await endTimeBox.sendKeys(endTime);
|
||||||
|
|
||||||
|
await driver.sleep(1000);
|
||||||
|
|
||||||
editButton = await driver.findElement(By.id("sponsorTimeEditButtonSubmissionNotice" + index));
|
editButton = await driver.findElement(By.id("sponsorTimeEditButtonSubmissionNotice" + index));
|
||||||
await editButton.click();
|
await editButton.click();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user