diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json
index c1b27a43..29902ccb 100644
--- a/public/_locales/en/messages.json
+++ b/public/_locales/en/messages.json
@@ -299,6 +299,14 @@
"message": "Submit segments",
"description": "Keybind label"
},
+ "nextChapterKeybind": {
+ "message": "Next chapter",
+ "description": "Keybind label"
+ },
+ "previousChapterKeybind": {
+ "message": "Previous chapter",
+ "description": "Keybind label"
+ },
"keybindDescription": {
"message": "Select a key by typing it and choose any modifier keys you wish to use."
},
diff --git a/public/options/options.html b/public/options/options.html
index eab43198..45a84b75 100644
--- a/public/options/options.html
+++ b/public/options/options.html
@@ -345,6 +345,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/config.ts b/src/config.ts
index 2576f062..fb1b4c0a 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -71,6 +71,8 @@ interface SBConfig {
skipKeybind: Keybind,
startSponsorKeybind: Keybind,
submitKeybind: Keybind,
+ nextChapterKeybind: Keybind,
+ previousChapterKeybind: Keybind,
// What categories should be skipped
categorySelections: CategorySelection[],
@@ -191,6 +193,8 @@ const Config: SBObject = {
skipKeybind: {key: "Enter"},
startSponsorKeybind: {key: ";"},
submitKeybind: {key: "'"},
+ nextChapterKeybind: {key: "]"},
+ previousChapterKeybind: {key: "["},
categorySelections: [{
name: "sponsor" as Category,
diff --git a/src/content.ts b/src/content.ts
index 2d533d44..47befecd 100644
--- a/src/content.ts
+++ b/src/content.ts
@@ -881,7 +881,8 @@ async function sponsorsLookup(keepOldSubmissions = true) {
?.map((segment) => ({
...segment,
source: SponsorSourceType.Server
- }));
+ }))
+ ?.sort((a, b) => a.segment[0] - b.segment[0]);
if (!recievedSegments || !recievedSegments.length) {
// return if no video found
retryFetch();
@@ -962,7 +963,7 @@ function importExistingChapters(wait: boolean) {
GenericUtils.wait(() => video && getExistingChapters(sponsorVideoID, video.duration),
wait ? 5000 : 0, 100, (c) => c?.length > 0).then((chapters) => {
if (!existingChaptersImported && chapters?.length > 0) {
- sponsorTimes = (sponsorTimes ?? []).concat(...chapters);
+ sponsorTimes = (sponsorTimes ?? []).concat(...chapters).sort((a, b) => a.segment[0] - b.segment[0]);
existingChaptersImported = true;
updatePreviewBar();
}
@@ -2019,7 +2020,7 @@ async function sendSubmitMessage() {
} catch(e) {} // eslint-disable-line no-empty
// Add submissions to current sponsors list
- sponsorTimes = (sponsorTimes || []).concat(newSegments);
+ sponsorTimes = (sponsorTimes || []).concat(newSegments).sort((a, b) => a.segment[0] - b.segment[0]);
// Increase contribution count
Config.config.sponsorTimesContributed = Config.config.sponsorTimesContributed + sponsorTimesSubmitting.length;
@@ -2080,6 +2081,35 @@ function updateActiveSegment(currentTime: number): void {
});
}
+function nextChapter(): void {
+ const chapters = sponsorTimes.filter((time) => time.actionType === ActionType.Chapter);
+ if (chapters.length <= 0) return;
+
+ const nextChapter = chapters.findIndex((time) => time.actionType === ActionType.Chapter
+ && time.segment[0] > video.currentTime);
+ if (nextChapter !== -1) {
+ unskipSponsorTime(chapters[nextChapter], null, true);
+ } else {
+ video.currentTime = video.duration;
+ }
+}
+
+function previousChapter(): void {
+ const chapters = sponsorTimes.filter((time) => time.actionType === ActionType.Chapter);
+ if (chapters.length <= 0) return;
+
+ // subtract 5 seconds to allow skipping back to the previous chapter if close to start of
+ // the current one
+ const nextChapter = chapters.findIndex((time) => time.actionType === ActionType.Chapter
+ && time.segment[0] > video.currentTime - Math.min(5, time.segment[1] - time.segment[0]));
+ const previousChapter = nextChapter !== -1 ? (nextChapter - 1) : (chapters.length - 1);
+ if (previousChapter !== -1) {
+ unskipSponsorTime(chapters[previousChapter], null, true);
+ } else {
+ video.currentTime = 0;
+ }
+}
+
function addPageListeners(): void {
const refreshListners = () => {
if (!isVisible(video)) {
@@ -2109,6 +2139,8 @@ function hotkeyListener(e: KeyboardEvent): void {
const skipKey = Config.config.skipKeybind;
const startSponsorKey = Config.config.startSponsorKeybind;
const submitKey = Config.config.submitKeybind;
+ const nextChapterKey = Config.config.nextChapterKeybind;
+ const previousChapterKey = Config.config.previousChapterKeybind;
if (keybindEquals(key, skipKey)) {
if (activeSkipKeybindElement)
@@ -2120,6 +2152,12 @@ function hotkeyListener(e: KeyboardEvent): void {
} else if (keybindEquals(key, submitKey)) {
submitSponsorTimes();
return;
+ } else if (keybindEquals(key, nextChapterKey)) {
+ nextChapter();
+ return;
+ } else if (keybindEquals(key, previousChapterKey)) {
+ previousChapter();
+ return;
}
//legacy - to preserve keybinds for skipKey, startSponsorKey and submitKey for people who set it before the update. (shouldn't be changed for future keybind options)