mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2025-12-06 19:47:04 +03:00
Add functions for importing/exporting segments
This commit is contained in:
@@ -8,6 +8,7 @@ import { Registration } from "./types";
|
||||
window.SB = Config;
|
||||
|
||||
import Utils from "./utils";
|
||||
import { GenericUtils } from "./utils/genericUtils";
|
||||
const utils = new Utils({
|
||||
registerFirefoxContentScript,
|
||||
unregisterFirefoxContentScript
|
||||
@@ -77,7 +78,7 @@ chrome.runtime.onInstalled.addListener(function () {
|
||||
chrome.tabs.create({url: chrome.extension.getURL("/help/index.html")});
|
||||
|
||||
//generate a userID
|
||||
const newUserID = utils.generateUserID();
|
||||
const newUserID = GenericUtils.generateUserID();
|
||||
//save this UUID
|
||||
Config.config.userID = newUserID;
|
||||
|
||||
@@ -120,7 +121,7 @@ async function submitVote(type: number, UUID: string, category: string) {
|
||||
|
||||
if (userID == undefined || userID === "undefined") {
|
||||
//generate one
|
||||
userID = utils.generateUserID();
|
||||
userID = GenericUtils.generateUserID();
|
||||
Config.config.userID = userID;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import ThumbsUpSvg from "../svg-icons/thumbs_up_svg";
|
||||
import ThumbsDownSvg from "../svg-icons/thumbs_down_svg";
|
||||
import PencilSvg from "../svg-icons/pencil_svg";
|
||||
import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils";
|
||||
import { GenericUtils } from "../utils/genericUtils";
|
||||
|
||||
export interface SkipNoticeProps {
|
||||
segments: SponsorTime[];
|
||||
@@ -511,7 +512,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
|
||||
const sponsorVideoID = this.props.contentContainer().sponsorVideoID;
|
||||
const sponsorTimesSubmitting : SponsorTime = {
|
||||
segment: this.segments[index].segment,
|
||||
UUID: utils.generateUserID() as SegmentUUID,
|
||||
UUID: GenericUtils.generateUserID() as SegmentUUID,
|
||||
category: this.segments[index].category,
|
||||
actionType: this.segments[index].actionType,
|
||||
source: SponsorSourceType.Local
|
||||
|
||||
@@ -180,9 +180,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
style={timeDisplayStyle}
|
||||
className="sponsorTimeDisplay"
|
||||
onClick={this.toggleEditTime.bind(this)}>
|
||||
{utils.getFormattedTime(segment[0], true) +
|
||||
{GenericUtils.getFormattedTime(segment[0], true) +
|
||||
((!isNaN(segment[1]) && sponsorTime.actionType !== ActionType.Poi)
|
||||
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segment[1], true) : "")}
|
||||
? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(segment[1], true) : "")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -324,7 +324,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
timeAsNumber = 0;
|
||||
}
|
||||
|
||||
sponsorTimeEdits[index] = utils.getFormattedTime(timeAsNumber, true);
|
||||
sponsorTimeEdits[index] = GenericUtils.getFormattedTime(timeAsNumber, true);
|
||||
if (sponsorTime.actionType === ActionType.Poi) sponsorTimeEdits[1] = sponsorTimeEdits[0];
|
||||
|
||||
this.setState({sponsorTimeEdits});
|
||||
@@ -523,8 +523,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
|
||||
|
||||
/** Returns an array in the sponsorTimeEdits form (formatted time string) from a normal seconds sponsor time */
|
||||
getFormattedSponsorTimesEdits(sponsorTime: SponsorTime): [string, string] {
|
||||
return [utils.getFormattedTime(sponsorTime.segment[0], true),
|
||||
utils.getFormattedTime(sponsorTime.segment[1], true)];
|
||||
return [GenericUtils.getFormattedTime(sponsorTime.segment[0], true),
|
||||
GenericUtils.getFormattedTime(sponsorTime.segment[1], true)];
|
||||
}
|
||||
|
||||
saveEditTimes(): void {
|
||||
|
||||
@@ -1568,7 +1568,7 @@ function startOrEndTimingNewSegment() {
|
||||
if (!isSegmentCreationInProgress()) {
|
||||
sponsorTimesSubmitting.push({
|
||||
segment: [roundedTime],
|
||||
UUID: utils.generateUserID() as SegmentUUID,
|
||||
UUID: GenericUtils.generateUserID() as SegmentUUID,
|
||||
category: Config.config.defaultCategory,
|
||||
actionType: ActionType.Skip,
|
||||
source: SponsorSourceType.Local
|
||||
@@ -2000,7 +2000,7 @@ function getSegmentsMessage(sponsorTimes: SponsorTime[]): string {
|
||||
|
||||
for (let i = 0; i < sponsorTimes.length; i++) {
|
||||
for (let s = 0; s < sponsorTimes[i].segment.length; s++) {
|
||||
let timeMessage = utils.getFormattedTime(sponsorTimes[i].segment[s]);
|
||||
let timeMessage = GenericUtils.getFormattedTime(sponsorTimes[i].segment[s]);
|
||||
//if this is an end time
|
||||
if (s == 1) {
|
||||
timeMessage = " " + chrome.i18n.getMessage("to") + " " + timeMessage;
|
||||
@@ -2147,7 +2147,7 @@ function showTimeWithoutSkips(skippedDuration: number): void {
|
||||
display.appendChild(duration);
|
||||
}
|
||||
|
||||
const durationAfterSkips = utils.getFormattedTime(video?.duration - skippedDuration)
|
||||
const durationAfterSkips = GenericUtils.getFormattedTime(video?.duration - skippedDuration)
|
||||
|
||||
duration.innerText = (durationAfterSkips == null || skippedDuration <= 0) ? "" : " (" + durationAfterSkips + ")";
|
||||
}
|
||||
@@ -2166,7 +2166,7 @@ function checkForPreloadedSegment() {
|
||||
if (!sponsorTimesSubmitting.some((s) => s.segment[0] === segment.segment[0] && s.segment[1] === s.segment[1])) {
|
||||
sponsorTimesSubmitting.push({
|
||||
segment: segment.segment,
|
||||
UUID: utils.generateUserID() as SegmentUUID,
|
||||
UUID: GenericUtils.generateUserID() as SegmentUUID,
|
||||
category: segment.category ? segment.category : Config.config.defaultCategory,
|
||||
actionType: segment.actionType ? segment.actionType : ActionType.Skip,
|
||||
source: SponsorSourceType.Local
|
||||
|
||||
@@ -9,6 +9,7 @@ import Config from "../config";
|
||||
import { ActionType, Category, SegmentContainer, SponsorSourceType, SponsorTime } from "../types";
|
||||
import Utils from "../utils";
|
||||
import { partition } from "../utils/arrayUtils";
|
||||
import { shortCategoryName } from "../utils/categoryUtils";
|
||||
import { GenericUtils } from "../utils/genericUtils";
|
||||
const utils = new Utils();
|
||||
|
||||
@@ -154,7 +155,7 @@ class PreviewBar {
|
||||
|
||||
private setTooltipTitle(segment: PreviewBarSegment, tooltip: HTMLElement): void {
|
||||
if (segment) {
|
||||
const name = segment.description || utils.shortCategoryName(segment.category);
|
||||
const name = segment.description || shortCategoryName(segment.category);
|
||||
if (segment.unsubmitted) {
|
||||
tooltip.textContent = chrome.i18n.getMessage("unsubmitted") + " " + name;
|
||||
} else {
|
||||
@@ -603,7 +604,7 @@ class PreviewBar {
|
||||
chapterButton.disabled = false;
|
||||
|
||||
const chapterTitle = chaptersContainer.querySelector(".ytp-chapter-title-content") as HTMLDivElement;
|
||||
chapterTitle.innerText = chosenSegment.description || utils.shortCategoryName(chosenSegment.category);
|
||||
chapterTitle.innerText = chosenSegment.description || shortCategoryName(chosenSegment.category);
|
||||
} else {
|
||||
// Hide chapters menu again
|
||||
chaptersContainer.style.display = "none";
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Message, MessageResponse, IsInfoFoundMessageResponse } from "./messageT
|
||||
import { showDonationLink } from "./utils/configUtils";
|
||||
import { AnimationUtils } from "./utils/animationUtils";
|
||||
import { GenericUtils } from "./utils/genericUtils";
|
||||
import { shortCategoryName } from "./utils/categoryUtils";
|
||||
const utils = new Utils();
|
||||
|
||||
interface MessageListener {
|
||||
@@ -474,15 +475,15 @@ async function runThePopup(messageListener?: MessageListener): Promise<void> {
|
||||
extraInfo = " (" + chrome.i18n.getMessage("manuallyHidden") + ")";
|
||||
}
|
||||
|
||||
const name = segmentTimes[i].description || utils.shortCategoryName(category);
|
||||
const name = segmentTimes[i].description || shortCategoryName(category);
|
||||
const textNode = document.createTextNode(name + extraInfo);
|
||||
const segmentTimeFromToNode = document.createElement("div");
|
||||
if (segmentTimes[i].actionType === ActionType.Full) {
|
||||
segmentTimeFromToNode.innerText = chrome.i18n.getMessage("full");
|
||||
} else {
|
||||
segmentTimeFromToNode.innerText = utils.getFormattedTime(segmentTimes[i].segment[0], true) +
|
||||
segmentTimeFromToNode.innerText = GenericUtils.getFormattedTime(segmentTimes[i].segment[0], true) +
|
||||
(actionType !== ActionType.Poi
|
||||
? " " + chrome.i18n.getMessage("to") + " " + utils.getFormattedTime(segmentTimes[i].segment[1], true)
|
||||
? " " + chrome.i18n.getMessage("to") + " " + GenericUtils.getFormattedTime(segmentTimes[i].segment[1], true)
|
||||
: "");
|
||||
}
|
||||
|
||||
|
||||
52
src/utils.ts
52
src/utils.ts
@@ -298,24 +298,6 @@ export default class Utils {
|
||||
return permissionRegex;
|
||||
}
|
||||
|
||||
generateUserID(length = 36): string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
if (window.crypto && window.crypto.getRandomValues) {
|
||||
const values = new Uint32Array(length);
|
||||
window.crypto.getRandomValues(values);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += charset[values[i] % charset.length];
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to a custom server
|
||||
*
|
||||
@@ -412,40 +394,6 @@ export default class Utils {
|
||||
return url;
|
||||
}
|
||||
|
||||
getFormattedTime(seconds: number, precise?: boolean): string {
|
||||
seconds = Math.max(seconds, 0);
|
||||
|
||||
const hours = Math.floor(seconds / 60 / 60);
|
||||
const minutes = Math.floor(seconds / 60) % 60;
|
||||
let minutesDisplay = String(minutes);
|
||||
let secondsNum = seconds % 60;
|
||||
if (!precise) {
|
||||
secondsNum = Math.floor(secondsNum);
|
||||
}
|
||||
|
||||
let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
|
||||
|
||||
if (secondsNum < 10) {
|
||||
//add a zero
|
||||
secondsDisplay = "0" + secondsDisplay;
|
||||
}
|
||||
if (hours && minutes < 10) {
|
||||
//add a zero
|
||||
minutesDisplay = "0" + minutesDisplay;
|
||||
}
|
||||
if (isNaN(hours) || isNaN(minutes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
shortCategoryName(categoryName: string): string {
|
||||
return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName);
|
||||
}
|
||||
|
||||
isContentScript(): boolean {
|
||||
return window.location.protocol === "http:" || window.location.protocol === "https:";
|
||||
}
|
||||
|
||||
@@ -45,3 +45,7 @@ export function getCategorySuffix(category: Category): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function shortCategoryName(categoryName: string): string {
|
||||
return chrome.i18n.getMessage("category_" + categoryName + "_short") || chrome.i18n.getMessage("category_" + categoryName);
|
||||
}
|
||||
65
src/utils/exporter.ts
Normal file
65
src/utils/exporter.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../types";
|
||||
import { shortCategoryName } from "./categoryUtils";
|
||||
import { GenericUtils } from "./genericUtils";
|
||||
|
||||
export function exportTimes(segments: SponsorTime[]): string {
|
||||
let result = "";
|
||||
for (const segment of segments) {
|
||||
if (![ActionType.Full, ActionType.Mute].includes(segment.actionType)
|
||||
&& segment.source !== SponsorSourceType.YouTube) {
|
||||
result += exportTime(segment) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function exportTime(segment: SponsorTime): string {
|
||||
const name = segment.description || shortCategoryName(segment.category);
|
||||
|
||||
return `${GenericUtils.getFormattedTime(segment.segment[0])}${
|
||||
segment.segment[1] && segment.segment[0] !== segment.segment[1]
|
||||
? ` - ${GenericUtils.getFormattedTime(segment.segment[1])}` : ""} ${name}`;
|
||||
}
|
||||
|
||||
export function importTimes(data: string, videoDuration: number): SponsorTime[] {
|
||||
const lines = data.split("\n");
|
||||
const result: SponsorTime[] = [];
|
||||
for (const line of lines) {
|
||||
const match = line.match(/(?:(\d+:\d+)+(?:\.\d+)?)|(?:\d+(?=s| second))/g);
|
||||
if (match) {
|
||||
const startTime = GenericUtils.getFormattedTimeToSeconds(match[0]);
|
||||
if (startTime) {
|
||||
const specialCharsMatcher = /^(?:\s+seconds?)?[:()-\s]*|(?:\s+at)?[:()-\s]+$/g
|
||||
const titleLeft = line.split(match[0])[0].replace(specialCharsMatcher, "");
|
||||
let titleRight = null;
|
||||
const split2 = line.split(match[1] || match[0]);
|
||||
titleRight = split2[split2.length - 1].replace(specialCharsMatcher, "");
|
||||
|
||||
const title = titleLeft?.length > titleRight?.length ? titleLeft : titleRight;
|
||||
if (title) {
|
||||
const segment: SponsorTime = {
|
||||
segment: [startTime, GenericUtils.getFormattedTimeToSeconds(match[1])],
|
||||
category: "chapter" as Category,
|
||||
actionType: ActionType.Chapter,
|
||||
description: title,
|
||||
source: SponsorSourceType.Local,
|
||||
UUID: GenericUtils.generateUserID() as SegmentUUID
|
||||
};
|
||||
|
||||
if (result.length > 0 && result[result.length - 1].segment[1] === null) {
|
||||
result[result.length - 1].segment[1] = segment.segment[0];
|
||||
}
|
||||
|
||||
result.push(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length > 0 && result[result.length - 1].segment[1] === null) {
|
||||
result[result.length - 1].segment[1] = videoDuration;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -35,6 +35,36 @@ function getFormattedTimeToSeconds(formatted: string): number | null {
|
||||
return hours * 3600 + minutes * 60 + seconds;
|
||||
}
|
||||
|
||||
function getFormattedTime(seconds: number, precise?: boolean): string {
|
||||
seconds = Math.max(seconds, 0);
|
||||
|
||||
const hours = Math.floor(seconds / 60 / 60);
|
||||
const minutes = Math.floor(seconds / 60) % 60;
|
||||
let minutesDisplay = String(minutes);
|
||||
let secondsNum = seconds % 60;
|
||||
if (!precise) {
|
||||
secondsNum = Math.floor(secondsNum);
|
||||
}
|
||||
|
||||
let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
|
||||
|
||||
if (secondsNum < 10) {
|
||||
//add a zero
|
||||
secondsDisplay = "0" + secondsDisplay;
|
||||
}
|
||||
if (hours && minutes < 10) {
|
||||
//add a zero
|
||||
minutesDisplay = "0" + minutesDisplay;
|
||||
}
|
||||
if (isNaN(hours) || isNaN(minutes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error message in a nice string
|
||||
*
|
||||
@@ -80,9 +110,29 @@ function hexToRgb(hex: string): {r: number, g: number, b: number} {
|
||||
} : null;
|
||||
}
|
||||
|
||||
function generateUserID(length = 36): string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
if (window.crypto && window.crypto.getRandomValues) {
|
||||
const values = new Uint32Array(length);
|
||||
window.crypto.getRandomValues(values);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += charset[values[i] % charset.length];
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += charset[Math.floor(Math.random() * charset.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export const GenericUtils = {
|
||||
wait,
|
||||
getFormattedTime,
|
||||
getFormattedTimeToSeconds,
|
||||
getErrorMessage,
|
||||
getLuminance
|
||||
getLuminance,
|
||||
generateUserID
|
||||
}
|
||||
241
test/extractor.test.ts
Normal file
241
test/extractor.test.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import { ActionType, Category, SegmentUUID, SponsorSourceType, SponsorTime } from "../src/types";
|
||||
import { exportTimes, importTimes } from "../src/utils/exporter";
|
||||
|
||||
describe("Export segments", () => {
|
||||
it("Some segments", () => {
|
||||
const segments: SponsorTime[] = [{
|
||||
segment: [0, 10],
|
||||
category: "chapter" as Category,
|
||||
actionType: ActionType.Chapter,
|
||||
description: "Chapter 1",
|
||||
source: SponsorSourceType.Server,
|
||||
UUID: "1" as SegmentUUID
|
||||
}, {
|
||||
segment: [20, 20],
|
||||
category: "poi_highlight" as Category,
|
||||
actionType: ActionType.Poi,
|
||||
description: "Highlight",
|
||||
source: SponsorSourceType.Server,
|
||||
UUID: "2" as SegmentUUID
|
||||
}, {
|
||||
segment: [30, 40],
|
||||
category: "sponsor" as Category,
|
||||
actionType: ActionType.Skip,
|
||||
description: "Sponsor", // Force a description since chrome is not defined
|
||||
source: SponsorSourceType.Server,
|
||||
UUID: "3" as SegmentUUID
|
||||
}, {
|
||||
segment: [50, 60],
|
||||
category: "selfpromo" as Category,
|
||||
actionType: ActionType.Mute,
|
||||
description: "Selfpromo",
|
||||
source: SponsorSourceType.Server,
|
||||
UUID: "4" as SegmentUUID
|
||||
}, {
|
||||
segment: [0, 0],
|
||||
category: "selfpromo" as Category,
|
||||
actionType: ActionType.Full,
|
||||
description: "Selfpromo",
|
||||
source: SponsorSourceType.Server,
|
||||
UUID: "5" as SegmentUUID
|
||||
}, {
|
||||
segment: [80, 90],
|
||||
category: "interaction" as Category,
|
||||
actionType: ActionType.Skip,
|
||||
description: "Interaction",
|
||||
source: SponsorSourceType.YouTube,
|
||||
UUID: "6" as SegmentUUID
|
||||
}];
|
||||
|
||||
const result = exportTimes(segments);
|
||||
|
||||
expect(result).toBe(
|
||||
"0:00 - 0:10 Chapter 1\n" +
|
||||
"0:20 Highlight\n" +
|
||||
"0:30 - 0:40 Sponsor\n"
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Import segments", () => {
|
||||
it("1:20 to 1:21 thing", () => {
|
||||
const input = ` 1:20 to 1:21 thing
|
||||
1:25 to 1:28 another thing`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 81],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 88],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("thing 1:20 to 1:21", () => {
|
||||
const input = ` thing 1:20 to 1:21
|
||||
another thing 1:25 to 1:28 ext`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 81],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 88],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("1:20 - 1:21 thing", () => {
|
||||
const input = ` 1:20 - 1:21 thing
|
||||
1:25 - 1:28 another thing`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 81],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 88],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("1:20 1:21 thing", () => {
|
||||
const input = ` 1:20 1:21 thing
|
||||
1:25 1:28 another thing`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 81],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 88],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("1:20 thing", () => {
|
||||
const input = ` 1:20 thing
|
||||
1:25 another thing`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 85],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("1:20: thing", () => {
|
||||
const input = ` 1:20: thing
|
||||
1:25: another thing`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 85],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("1:20 (thing)", () => {
|
||||
const input = ` 1:20 (thing)
|
||||
1:25 (another thing)`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 85],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("thing 1:20", () => {
|
||||
const input = ` thing 1:20
|
||||
another thing 1:25`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 85],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("thing at 1:20", () => {
|
||||
const input = ` thing at 1:20
|
||||
another thing at 1:25`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [80, 85],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [85, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("thing at 1s", () => {
|
||||
const input = ` thing at 1s
|
||||
another thing at 5s`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [1, 5],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [5, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
|
||||
it("thing at 1 second", () => {
|
||||
const input = ` thing at 1 second
|
||||
another thing at 5 seconds`;
|
||||
|
||||
const result = importTimes(input, 120);
|
||||
expect(result).toMatchObject([{
|
||||
segment: [1, 5],
|
||||
description: "thing",
|
||||
category: "chapter" as Category
|
||||
}, {
|
||||
segment: [5, 120],
|
||||
description: "another thing",
|
||||
category: "chapter" as Category
|
||||
}]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user