mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-06 11:36:58 +03:00
@@ -44,6 +44,7 @@
|
||||
| shadowHidden | INTEGER | not null |
|
||||
| hashedVideoID | TEXT | not null, default '', sha256 |
|
||||
| userAgent | TEXT | not null, default '' |
|
||||
| description | TEXT | not null, default '' |
|
||||
|
||||
| index | field |
|
||||
| -- | :--: |
|
||||
|
||||
1
ci.json
1
ci.json
@@ -46,7 +46,6 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "poi_highlight"],
|
||||
"maxNumberOfActiveWarnings": 3,
|
||||
"hoursAfterWarningExpires": 24,
|
||||
"rateLimit": {
|
||||
|
||||
@@ -25,6 +25,11 @@ CREATE INDEX IF NOT EXISTS "sponsorTimes_videoID"
|
||||
("videoID" COLLATE pg_catalog."default" ASC NULLS LAST, service COLLATE pg_catalog."default" ASC NULLS LAST, category COLLATE pg_catalog."default" ASC NULLS LAST, "timeSubmitted" ASC NULLS LAST)
|
||||
TABLESPACE pg_default;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "sponsorTimes_description_gin"
|
||||
ON public."sponsorTimes" USING gin
|
||||
("description" COLLATE pg_catalog."default" gin_trgm_ops, category COLLATE pg_catalog."default" gin_trgm_ops)
|
||||
TABLESPACE pg_default;
|
||||
|
||||
-- userNames
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "userNames_userID"
|
||||
|
||||
8
databases/_upgrade_sponsorTimes_27.sql
Normal file
8
databases/_upgrade_sponsorTimes_27.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
ALTER TABLE "sponsorTimes" ADD "description" TEXT NOT NULL default '';
|
||||
ALTER TABLE "archivedSponsorTimes" ADD "description" TEXT NOT NULL default '';
|
||||
|
||||
UPDATE "config" SET value = 27 WHERE key = 'version';
|
||||
|
||||
COMMIT;
|
||||
@@ -42,6 +42,7 @@ import { getUserStats } from "./routes/getUserStats";
|
||||
import ExpressPromiseRouter from "express-promise-router";
|
||||
import { Server } from "http";
|
||||
import { youtubeApiProxy } from "./routes/youtubeApiProxy";
|
||||
import { getChapterNames } from "./routes/getChapterNames";
|
||||
|
||||
export function createServer(callback: () => void): Server {
|
||||
// Create a service (the app object is just a callback).
|
||||
@@ -172,6 +173,9 @@ function setupRoutes(router: Router) {
|
||||
// get all segments that match a search
|
||||
router.get("/api/searchSegments", getSearchSegments);
|
||||
|
||||
// autocomplete chapter names
|
||||
router.get("/api/chapterNames", getChapterNames);
|
||||
|
||||
// get status
|
||||
router.get("/api/status/:value", getStatus);
|
||||
router.get("/api/status", getStatus);
|
||||
|
||||
@@ -19,7 +19,7 @@ addDefaults(config, {
|
||||
privateDBSchema: "./databases/_private.db.sql",
|
||||
readOnly: false,
|
||||
webhooks: [],
|
||||
categoryList: ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler", "poi_highlight"],
|
||||
categoryList: ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler", "poi_highlight", "chapter"],
|
||||
categorySupport: {
|
||||
sponsor: ["skip", "mute"],
|
||||
selfpromo: ["skip", "mute"],
|
||||
@@ -30,6 +30,7 @@ addDefaults(config, {
|
||||
filler: ["skip", "mute"],
|
||||
music_offtopic: ["skip"],
|
||||
poi_highlight: ["skip"],
|
||||
chapter: ["chapter"]
|
||||
},
|
||||
maxNumberOfActiveWarnings: 1,
|
||||
hoursAfterWarningExpires: 24,
|
||||
|
||||
@@ -68,7 +68,7 @@ export class Postgres implements IDatabase {
|
||||
}
|
||||
case "all": {
|
||||
const values = queryResult.rows;
|
||||
Logger.debug(`result (postgres): ${values}`);
|
||||
Logger.debug(`result (postgres): ${JSON.stringify(values)}`);
|
||||
return values;
|
||||
}
|
||||
case "run": {
|
||||
|
||||
46
src/routes/getChapterNames.ts
Normal file
46
src/routes/getChapterNames.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Logger } from "../utils/logger";
|
||||
import { Request, Response } from "express";
|
||||
import { db } from "../databases/databases";
|
||||
import { Postgres } from "../databases/Postgres";
|
||||
|
||||
export async function getChapterNames(req: Request, res: Response): Promise<Response> {
|
||||
const description = req.query.description as string;
|
||||
const channelID = req.query.channelID as string;
|
||||
|
||||
if (!description || typeof(description) !== "string"
|
||||
|| !channelID || typeof(channelID) !== "string") {
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
|
||||
if (!(db instanceof Postgres)) {
|
||||
return res.sendStatus(500).json({
|
||||
message: "Not supported on this instance"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const descriptions = await db.prepare("all", `
|
||||
SELECT "description"
|
||||
FROM "sponsorTimes"
|
||||
WHERE ("votes" > 0 OR ("views" > 100 AND "votes" >= 0)) AND "videoID" IN (
|
||||
SELECT "videoID"
|
||||
FROM "videoInfo"
|
||||
WHERE "channelID" = ?
|
||||
) AND "description" != ''
|
||||
GROUP BY "description"
|
||||
ORDER BY SUM("votes"), similarity("description", ?) DESC
|
||||
LIMIT 5;`
|
||||
, [channelID, description]) as { description: string }[];
|
||||
|
||||
if (descriptions?.length > 0) {
|
||||
return res.status(200).json(descriptions.map(d => ({
|
||||
description: d.description
|
||||
})));
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(err as string);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
|
||||
return res.status(404).json([]);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category:
|
||||
|
||||
const filteredSegments = segments.filter((_, index) => shouldFilter[index]);
|
||||
|
||||
const maxSegments = getCategoryActionType(category) === CategoryActionType.Skippable ? 32 : 1;
|
||||
const maxSegments = getCategoryActionType(category) === CategoryActionType.Skippable ? Infinity : 1;
|
||||
return (await chooseSegments(filteredSegments, maxSegments)).map((chosenSegment) => ({
|
||||
category: chosenSegment.category,
|
||||
actionType: chosenSegment.actionType,
|
||||
@@ -55,6 +55,7 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category:
|
||||
votes: chosenSegment.votes,
|
||||
videoDuration: chosenSegment.videoDuration,
|
||||
userID: chosenSegment.userID,
|
||||
description: chosenSegment.description
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ async function getSegmentsFromDBByHash(hashedVideoIDPrefix: VideoIDHash, service
|
||||
const fetchFromDB = () => db
|
||||
.prepare(
|
||||
"all",
|
||||
`SELECT "videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "videoDuration", "reputation", "shadowHidden", "hashedVideoID", "timeSubmitted" FROM "sponsorTimes"
|
||||
`SELECT "videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "videoDuration", "reputation", "shadowHidden", "hashedVideoID", "timeSubmitted", "description" FROM "sponsorTimes"
|
||||
WHERE "hashedVideoID" LIKE ? AND "service" = ? AND "hidden" = 0 ORDER BY "startTime"`,
|
||||
[`${hashedVideoIDPrefix}%`, service]
|
||||
) as Promise<DBSegment[]>;
|
||||
@@ -155,7 +156,7 @@ async function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): P
|
||||
const fetchFromDB = () => db
|
||||
.prepare(
|
||||
"all",
|
||||
`SELECT "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "videoDuration", "reputation", "shadowHidden", "timeSubmitted" FROM "sponsorTimes"
|
||||
`SELECT "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "videoDuration", "reputation", "shadowHidden", "timeSubmitted", "description" FROM "sponsorTimes"
|
||||
WHERE "videoID" = ? AND "service" = ? AND "hidden" = 0 ORDER BY "startTime"`,
|
||||
[videoID, service]
|
||||
) as Promise<DBSegment[]>;
|
||||
@@ -219,7 +220,7 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise<DBSeg
|
||||
//1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group
|
||||
//2. If a segment starts after the end of the currentGroup (> cursor), no other segment will ever fall
|
||||
// inside that group (because they're sorted) so we can create a new one
|
||||
const overlappingSegmentsGroups: OverlappingSegmentGroup[] = [];
|
||||
let overlappingSegmentsGroups: OverlappingSegmentGroup[] = [];
|
||||
let currentGroup: OverlappingSegmentGroup;
|
||||
let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created
|
||||
for (const segment of segments) {
|
||||
@@ -261,6 +262,8 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise<DBSeg
|
||||
group.reputation = group.reputation / group.segments.length;
|
||||
});
|
||||
|
||||
overlappingSegmentsGroups = splitPercentOverlap(overlappingSegmentsGroups);
|
||||
|
||||
//if there are too many groups, find the best ones
|
||||
return getWeightedRandomChoice(overlappingSegmentsGroups, max).map(
|
||||
//randomly choose 1 good segment per group and return them
|
||||
@@ -268,6 +271,37 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise<DBSeg
|
||||
);
|
||||
}
|
||||
|
||||
function splitPercentOverlap(groups: OverlappingSegmentGroup[]): OverlappingSegmentGroup[] {
|
||||
return groups.flatMap((group) => {
|
||||
const result: OverlappingSegmentGroup[] = [];
|
||||
group.segments.forEach((segment) => {
|
||||
const bestGroup = result.find((group) => {
|
||||
// At least one segment in the group must have high % overlap or the same action type
|
||||
return group.segments.some((compareSegment) => {
|
||||
const overlap = Math.min(segment.endTime, compareSegment.endTime) - Math.max(segment.startTime, compareSegment.startTime);
|
||||
const overallDuration = Math.max(segment.endTime, compareSegment.endTime) - Math.min(segment.startTime, compareSegment.startTime);
|
||||
const overlapPercent = overlap / overallDuration;
|
||||
return (overlapPercent > 0 && segment.actionType === compareSegment.actionType && segment.actionType !== ActionType.Chapter)
|
||||
|| overlapPercent >= 0.6
|
||||
|| (overlapPercent >= 0.8 && segment.actionType === ActionType.Chapter && compareSegment.actionType === ActionType.Chapter);
|
||||
});
|
||||
});
|
||||
|
||||
if (bestGroup) {
|
||||
bestGroup.segments.push(segment);
|
||||
bestGroup.votes += segment.votes;
|
||||
bestGroup.reputation += segment.reputation;
|
||||
bestGroup.locked ||= segment.locked;
|
||||
bestGroup.required ||= segment.required;
|
||||
} else {
|
||||
result.push({ segments: [segment], votes: segment.votes, reputation: segment.reputation, locked: segment.locked, required: segment.required });
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns what would be sent to the client.
|
||||
|
||||
@@ -299,7 +299,7 @@ async function checkUserActiveWarning(userID: string): Promise<CheckResult> {
|
||||
return CHECK_PASS;
|
||||
}
|
||||
|
||||
function checkInvalidFields(videoID: any, userID: any, segments: Array<any>): CheckResult {
|
||||
function checkInvalidFields(videoID: VideoID, userID: UserID, segments: IncomingSegment[]): CheckResult {
|
||||
const invalidFields = [];
|
||||
const errors = [];
|
||||
if (typeof videoID !== "string") {
|
||||
@@ -320,6 +320,12 @@ function checkInvalidFields(videoID: any, userID: any, segments: Array<any>): Ch
|
||||
(typeof endTime === "string" && endTime.includes(":"))) {
|
||||
invalidFields.push("segment time");
|
||||
}
|
||||
|
||||
if (typeof segmentPair.description !== "string"
|
||||
|| (segmentPair.description.length > 60 && segmentPair.actionType === ActionType.Chapter)
|
||||
|| (segmentPair.description.length !== 0 && segmentPair.actionType !== ActionType.Chapter)) {
|
||||
invalidFields.push("segment description");
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidFields.length !== 0) {
|
||||
@@ -541,7 +547,8 @@ function preprocessInput(req: Request) {
|
||||
segments = [{
|
||||
segment: [req.query.startTime as string, req.query.endTime as string],
|
||||
category: req.query.category as Category,
|
||||
actionType: (req.query.actionType as ActionType) ?? ActionType.Skip
|
||||
actionType: (req.query.actionType as ActionType) ?? ActionType.Skip,
|
||||
description: req.query.description as string || "",
|
||||
}];
|
||||
}
|
||||
// Add default action type
|
||||
@@ -550,6 +557,7 @@ function preprocessInput(req: Request) {
|
||||
segment.actionType = ActionType.Skip;
|
||||
}
|
||||
|
||||
segment.description ??= "";
|
||||
segment.segment = segment.segment.map((time) => typeof segment.segment[0] === "string" ? time?.replace(",", ".") : time);
|
||||
});
|
||||
|
||||
@@ -633,9 +641,10 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
const startingLocked = isVIP ? 1 : 0;
|
||||
try {
|
||||
await db.prepare("run", `INSERT INTO "sponsorTimes"
|
||||
("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "reputation", "shadowHidden", "hashedVideoID", "userAgent")
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
||||
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, startingLocked, UUID, userID, timeSubmitted, 0, segmentInfo.category, segmentInfo.actionType, service, videoDuration, reputation, shadowBanRow.userCount, hashedVideoID, userAgent
|
||||
("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "reputation", "shadowHidden", "hashedVideoID", "userAgent", "description")
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
||||
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, startingLocked, UUID, userID, timeSubmitted, 0
|
||||
, segmentInfo.category, segmentInfo.actionType, service, videoDuration, reputation, shadowBanRow.userCount, hashedVideoID, userAgent, segmentInfo.description
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { UserID } from "./user.model";
|
||||
export type SegmentUUID = string & { __segmentUUIDBrand: unknown };
|
||||
export type VideoID = string & { __videoIDBrand: unknown };
|
||||
export type VideoDuration = number & { __videoDurationBrand: unknown };
|
||||
export type Category = ("sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "poi_highlight") & { __categoryBrand: unknown };
|
||||
export type Category = ("sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "poi_highlight" | "chapter") & { __categoryBrand: unknown };
|
||||
export type VideoIDHash = VideoID & HashedValue;
|
||||
export type IPAddress = string & { __ipAddressBrand: unknown };
|
||||
export type HashedIP = IPAddress & HashedValue;
|
||||
@@ -13,6 +13,7 @@ export type HashedIP = IPAddress & HashedValue;
|
||||
export enum ActionType {
|
||||
Skip = "skip",
|
||||
Mute = "mute",
|
||||
Chapter = "chapter"
|
||||
}
|
||||
|
||||
// Uncomment as needed
|
||||
@@ -30,6 +31,7 @@ export interface IncomingSegment {
|
||||
category: Category;
|
||||
actionType: ActionType;
|
||||
segment: string[];
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface Segment {
|
||||
@@ -65,6 +67,7 @@ export interface DBSegment {
|
||||
timeSubmitted: number;
|
||||
userAgent: string;
|
||||
service: Service;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface OverlappingSegmentGroup {
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"categoryList": ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "poi_highlight"],
|
||||
"maxNumberOfActiveWarnings": 3,
|
||||
"hoursAfterWarningExpires": 24,
|
||||
"rateLimit": {
|
||||
|
||||
61
test/cases/getChapterNames.ts
Normal file
61
test/cases/getChapterNames.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import assert from "assert";
|
||||
import { db } from "../../src/databases/databases";
|
||||
import { Postgres } from "../../src/databases/Postgres";
|
||||
import { client } from "../utils/httpClient";
|
||||
import { partialDeepEquals } from "../utils/partialDeepEquals";
|
||||
|
||||
// Only works with Postgres
|
||||
if (db instanceof Postgres) {
|
||||
|
||||
describe("getChapterNames", function () {
|
||||
const endpoint = "/api/chapterNames";
|
||||
|
||||
const chapterNamesVid1 = "chapterNamesVid";
|
||||
const chapterChannelID = "chapterChannelID";
|
||||
|
||||
before(async () => {
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
await db.prepare("run", query, [chapterNamesVid1, 60, 80, 2, 0, "chapterNamesVid-1", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Weird name"]);
|
||||
await db.prepare("run", query, [chapterNamesVid1, 70, 75, 2, 0, "chapterNamesVid-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "A different one"]);
|
||||
await db.prepare("run", query, [chapterNamesVid1, 71, 76, 2, 0, "chapterNamesVid-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Something else"]);
|
||||
|
||||
await db.prepare("run", `INSERT INTO "videoInfo" ("videoID", "channelID", "title", "published", "genreUrl")
|
||||
SELECT ?, ?, ?, ?, ?`, [
|
||||
chapterNamesVid1, chapterChannelID, "", 0, ""
|
||||
]);
|
||||
});
|
||||
|
||||
it("Search for 'weird'", async () => {
|
||||
const result = await client.get(`${endpoint}?description=weird&channelID=${chapterChannelID}`);
|
||||
const expected = [{
|
||||
description: "Weird name",
|
||||
}];
|
||||
|
||||
assert.strictEqual(result.status, 200);
|
||||
assert.strictEqual(result.data.length, 3);
|
||||
assert.ok(partialDeepEquals(result.data, expected));
|
||||
});
|
||||
|
||||
it("Search for 'different'", async () => {
|
||||
const result = await client.get(`${endpoint}?description=different&channelID=${chapterChannelID}`);
|
||||
const expected = [{
|
||||
description: "A different one",
|
||||
}];
|
||||
|
||||
assert.strictEqual(result.status, 200);
|
||||
assert.strictEqual(result.data.length, 3);
|
||||
assert.ok(partialDeepEquals(result.data, expected));
|
||||
});
|
||||
|
||||
it("Search for 'something'", async () => {
|
||||
const result = await client.get(`${endpoint}?description=something&channelID=${chapterChannelID}`);
|
||||
const expected = [{
|
||||
description: "Something else",
|
||||
}];
|
||||
|
||||
assert.strictEqual(result.status, 200);
|
||||
assert.strictEqual(result.data.length, 3);
|
||||
assert.ok(partialDeepEquals(result.data, expected));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -108,7 +108,9 @@ describe("getLockReason", () => {
|
||||
{ category: "outro", locked: 1, reason: "outro-reason", userID: vipUserID2, userName: vipUserName2 },
|
||||
{ category: "preview", locked: 1, reason: "preview-reason", userID: vipUserID1, userName: vipUserName1 },
|
||||
{ category: "music_offtopic", locked: 1, reason: "nonmusic-reason", userID: vipUserID1, userName: vipUserName1 },
|
||||
{ category: "poi_highlight", locked: 0, reason: "", userID: "", userName: "" }
|
||||
{ category: "filler", locked: 0, reason: "", userID: "", userName: "" },
|
||||
{ category: "poi_highlight", locked: 0, reason: "", userID: "", userName: "" },
|
||||
{ category: "chapter", locked: 0, reason: "", userID: "", userName: "" }
|
||||
];
|
||||
assert.deepStrictEqual(res.data, expected);
|
||||
done();
|
||||
|
||||
@@ -6,23 +6,26 @@ import { client } from "../utils/httpClient";
|
||||
describe("getSkipSegments", () => {
|
||||
const endpoint = "/api/skipSegments";
|
||||
before(async () => {
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 1, 11, 1, 0, "uuid01", "testman", 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 12, 14, 2, 0, "uuid02", "testman", 0, 50, "sponsor", "mute", "YouTube", 100, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 20, 33, 2, 0, "uuid03", "testman", 0, 50, "intro", "skip", "YouTube", 101, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID1", 1, 11, 2, 0, "uuid10", "testman", 0, 50, "sponsor", "skip", "PeerTube", 120, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID2", 1, 11, 2, 1, "uuid20", "testman", 0, 50, "sponsor", "skip", "YouTube", 140, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID3", 1, 11, 2, 0, "uuid30", "testman", 0, 50, "sponsor", "skip", "YouTube", 200, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID3", 7, 22, -3, 0, "uuid31", "testman", 0, 50, "sponsor", "skip", "YouTube", 300, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentMultiple", 1, 11, 2, 0, "uuid40", "testman", 0, 50, "intro", "skip", "YouTube", 400, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentMultiple", 20, 33, 2, 0, "uuid41", "testman", 0, 50, "intro", "skip", "YouTube", 500, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentLocked", 20, 33, 2, 1, "uuid50", "testman", 0, 50, "intro", "skip", "YouTube", 230, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentLocked", 20, 34, 100000, 0, "uuid51", "testman", 0, 50, "intro", "skip", "YouTube", 190, 0, 0]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID6", 20, 34, 100000, 0, "uuid60", "testman", 0, 50, "sponsor", "skip", "YouTube", 190, 1, 0]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, 0, "requiredSegmentVid4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0]);
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 1, 11, 1, 0, "uuid01", "testman", 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 12, 14, 2, 0, "uuid02", "testman", 0, 50, "sponsor", "mute", "YouTube", 100, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID0", 20, 33, 2, 0, "uuid03", "testman", 0, 50, "intro", "skip", "YouTube", 101, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID1", 1, 11, 2, 0, "uuid10", "testman", 0, 50, "sponsor", "skip", "PeerTube", 120, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID2", 1, 11, 2, 1, "uuid20", "testman", 0, 50, "sponsor", "skip", "YouTube", 140, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID3", 1, 11, 2, 0, "uuid30", "testman", 0, 50, "sponsor", "skip", "YouTube", 200, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID3", 7, 22, -3, 0, "uuid31", "testman", 0, 50, "sponsor", "skip", "YouTube", 300, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentMultiple", 1, 11, 2, 0, "uuid40", "testman", 0, 50, "intro", "skip", "YouTube", 400, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentMultiple", 20, 33, 2, 0, "uuid41", "testman", 0, 50, "intro", "skip", "YouTube", 500, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentLocked", 20, 33, 2, 1, "uuid50", "testman", 0, 50, "intro", "skip", "YouTube", 230, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentLocked", 20, 34, 100000, 0, "uuid51", "testman", 0, 50, "intro", "skip", "YouTube", 190, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["getSkipSegmentID6", 20, 34, 100000, 0, "uuid60", "testman", 0, 50, "sponsor", "skip", "YouTube", 190, 1, 0, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, 0, "requiredSegmentVid4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
|
||||
await db.prepare("run", query, ["chapterVid", 60, 80, 2, 0, "chapterVid-1", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 1"]);
|
||||
await db.prepare("run", query, ["chapterVid", 70, 75, 2, 0, "chapterVid-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 2"]);
|
||||
await db.prepare("run", query, ["chapterVid", 71, 76, 2, 0, "chapterVid-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 3"]);
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -388,6 +391,33 @@ describe("getSkipSegments", () => {
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get overlapping chapter segments if very different", (done) => {
|
||||
client.get(`${endpoint}?videoID=chapterVid&category=chapter&actionType=chapter`)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
assert.strictEqual(data.length, 2);
|
||||
const expected = [{
|
||||
UUID: "chapterVid-1",
|
||||
description: "Chapter 1"
|
||||
}, {
|
||||
UUID: "chapterVid-2",
|
||||
description: "Chapter 2"
|
||||
}];
|
||||
const expected2 = [{
|
||||
UUID: "chapterVid-1",
|
||||
description: "Chapter 1"
|
||||
}, {
|
||||
UUID: "chapterVid-3",
|
||||
description: "Chapter 3"
|
||||
}];
|
||||
|
||||
assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2));
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get 400 if no videoID passed in", (done) => {
|
||||
client.get(endpoint)
|
||||
.then(res => {
|
||||
|
||||
@@ -16,20 +16,31 @@ describe("getSkipSegmentsByHash", () => {
|
||||
const getSegmentsByHash0Hash = "fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910";
|
||||
const requiredSegmentVidHash = "d51822c3f681e07aef15a8855f52ad12db9eb9cf059e65b16b64c43359557f61";
|
||||
before(async () => {
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "service", "hidden", "shadowHidden", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, "getSegmentsByHash-01", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, "getSegmentsByHash-02", "testman", 0, 50, "sponsor", "skip", "PeerTube", 0, 0, getSegmentsByHash0Hash]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 20, 30, 2, "getSegmentsByHash-03", "testman", 100, 150, "intro", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 40, 50, 2, "getSegmentsByHash-04", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getSegmentsByHash0Hash]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-noMatchHash", 40, 50, 2, "getSegmentsByHash-noMatchHash", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "fdaffnoMatchHash"]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-1", 60, 70, 2, "getSegmentsByHash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b"]);
|
||||
await db.prepare("run", query, ["onlyHidden", 60, 70, 2, "onlyHidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 1, 0, "f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3"]);
|
||||
await db.prepare("run", query, ["highlightVid", 60, 60, 2, "highlightVid-1", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1)]);
|
||||
await db.prepare("run", query, ["highlightVid", 70, 70, 2, "highlightVid-2", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1)]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, "requiredSegmentVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, "requiredSegmentVid-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, "requiredSegmentVid-3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, "requiredSegmentVid-4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash]);
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "UUID", "userID", "timeSubmitted", views, category, "actionType", "service", "hidden", "shadowHidden", "hashedVideoID", "description") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, "getSegmentsByHash-01", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 1, 10, 2, "getSegmentsByHash-02", "testman", 0, 50, "sponsor", "skip", "PeerTube", 0, 0, getSegmentsByHash0Hash, ""]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 20, 30, 2, "getSegmentsByHash-03", "testman", 100, 150, "intro", "skip", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-0", 40, 50, 2, "getSegmentsByHash-04", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getSegmentsByHash0Hash, ""]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-noMatchHash", 40, 50, 2, "getSegmentsByHash-noMatchHash", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "fdaffnoMatchHash", ""]);
|
||||
await db.prepare("run", query, ["getSegmentsByHash-1", 60, 70, 2, "getSegmentsByHash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b", ""]);
|
||||
await db.prepare("run", query, ["onlyHidden", 60, 70, 2, "onlyHidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 1, 0, "f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3", ""]);
|
||||
await db.prepare("run", query, ["highlightVid", 60, 60, 2, "highlightVid-1", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1), ""]);
|
||||
await db.prepare("run", query, ["highlightVid", 70, 70, 2, "highlightVid-2", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1), ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, "requiredSegmentVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, "requiredSegmentVid-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, "requiredSegmentVid-3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]);
|
||||
await db.prepare("run", query, ["requiredSegmentVid", 80, 90, 2, "requiredSegmentVid-4", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]);
|
||||
await db.prepare("run", query, ["chapterVid-hash", 60, 80, 2, "chapterVid-hash-1", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 1"]); //7258
|
||||
await db.prepare("run", query, ["chapterVid-hash", 70, 75, 2, "chapterVid-hash-2", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 2"]); //7258
|
||||
await db.prepare("run", query, ["chapterVid-hash", 71, 76, 2, "chapterVid-hash-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, getHash("chapterVid-hash", 1), "Chapter 3"]); //7258
|
||||
await db.prepare("run", query, ["longMuteVid-hash", 40, 45, 2, "longMuteVid-hash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613
|
||||
await db.prepare("run", query, ["longMuteVid-hash", 30, 35, 2, "longMuteVid-hash-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613
|
||||
await db.prepare("run", query, ["longMuteVid-hash", 2, 80, 2, "longMuteVid-hash-3", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613
|
||||
await db.prepare("run", query, ["longMuteVid-hash", 3, 78, 2, "longMuteVid-hash-4", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-hash", 1), ""]); //6613
|
||||
await db.prepare("run", query, ["longMuteVid-2-hash", 1, 15, 2, "longMuteVid-2-hash-1", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c
|
||||
await db.prepare("run", query, ["longMuteVid-2-hash", 30, 35, 2, "longMuteVid-2-hash-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c
|
||||
await db.prepare("run", query, ["longMuteVid-2-hash", 2, 80, 2, "longMuteVid-2-hash-3", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c
|
||||
await db.prepare("run", query, ["longMuteVid-2-hash", 3, 78, 2, "longMuteVid-2-hash-4", "testman", 0, 50, "sponsor", "mute", "YouTube", 0, 0, getHash("longMuteVid-2-hash", 1), ""]); //ab0c
|
||||
});
|
||||
|
||||
it("Should be able to get a 200", (done) => {
|
||||
@@ -356,4 +367,115 @@ describe("getSkipSegmentsByHash", () => {
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get overlapping chapter segments if very different", (done) => {
|
||||
client.get(`${endpoint}/7258?category=chapter&actionType=chapter`)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
assert.strictEqual(data.length, 1);
|
||||
const expected = [{
|
||||
segments: [{
|
||||
UUID: "chapterVid-hash-1",
|
||||
description: "Chapter 1"
|
||||
}, {
|
||||
UUID: "chapterVid-hash-2",
|
||||
description: "Chapter 2"
|
||||
}]
|
||||
}];
|
||||
const expected2 = [{
|
||||
segments: [{
|
||||
UUID: "chapterVid-hash-1",
|
||||
description: "Chapter 1"
|
||||
}, {
|
||||
UUID: "chapterVid-hash-3",
|
||||
description: "Chapter 3"
|
||||
}]
|
||||
}];
|
||||
|
||||
assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2));
|
||||
assert.strictEqual(data[0].segments.length, 2);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get mute segment with small skip segment in middle", (done) => {
|
||||
client.get(`${endpoint}/6613?actionType=skip&actionType=mute`)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
assert.strictEqual(data.length, 1);
|
||||
const expected = [{
|
||||
segments: [{
|
||||
UUID: "longMuteVid-hash-3",
|
||||
actionType: "mute"
|
||||
}, {
|
||||
UUID: "longMuteVid-hash-2",
|
||||
actionType: "skip"
|
||||
}, {
|
||||
UUID: "longMuteVid-hash-1",
|
||||
actionType: "skip"
|
||||
}]
|
||||
}];
|
||||
const expected2 = [{
|
||||
segments: [{
|
||||
UUID: "longMuteVid-hash-4",
|
||||
actionType: "mute"
|
||||
}, {
|
||||
UUID: "longMuteVid-hash-2",
|
||||
actionType: "skip"
|
||||
}, {
|
||||
UUID: "longMuteVid-hash-1",
|
||||
actionType: "skip"
|
||||
}]
|
||||
}];
|
||||
|
||||
assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2));
|
||||
assert.strictEqual(data[0].segments.length, 3);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get mute segment with small skip segment in middle (2)", (done) => {
|
||||
client.get(`${endpoint}/ab0c?actionType=skip&actionType=mute`)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
assert.strictEqual(data.length, 1);
|
||||
const expected = [{
|
||||
segments: [{
|
||||
UUID: "longMuteVid-2-hash-1",
|
||||
actionType: "mute"
|
||||
}, {
|
||||
UUID: "longMuteVid-2-hash-2",
|
||||
actionType: "skip"
|
||||
}]
|
||||
}];
|
||||
const expected2 = [{
|
||||
segments: [{
|
||||
UUID: "longMuteVid-2-hash-3",
|
||||
actionType: "mute"
|
||||
}, {
|
||||
UUID: "longMuteVid-2-hash-2",
|
||||
actionType: "skip"
|
||||
}]
|
||||
}];
|
||||
const expected3 = [{
|
||||
segments: [{
|
||||
UUID: "longMuteVid-2-hash-4",
|
||||
actionType: "mute"
|
||||
}, {
|
||||
UUID: "longMuteVid-2-hash-2",
|
||||
actionType: "skip"
|
||||
}]
|
||||
}];
|
||||
|
||||
assert.ok(partialDeepEquals(data, expected, false) || partialDeepEquals(data, expected2) || partialDeepEquals(data, expected3));
|
||||
assert.strictEqual(data[0].segments.length, 2);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ describe("postSkipSegments", () => {
|
||||
|
||||
const queryDatabase = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
|
||||
const queryDatabaseActionType = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
|
||||
const queryDatabaseChapter = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType", "description" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
|
||||
const queryDatabaseDuration = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "videoDuration" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
|
||||
const queryDatabaseVideoInfo = (videoID: string) => db.prepare("get", `SELECT * FROM "videoInfo" WHERE "videoID" = ?`, [videoID]);
|
||||
|
||||
@@ -181,6 +182,34 @@ describe("postSkipSegments", () => {
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to submit a single chapter (JSON method)", (done) => {
|
||||
const videoID = "postSkipChapter1";
|
||||
postSkipSegmentJSON({
|
||||
userID: submitUserOne,
|
||||
videoID,
|
||||
segments: [{
|
||||
segment: [0, 10],
|
||||
category: "chapter",
|
||||
actionType: "chapter",
|
||||
description: "This is a chapter"
|
||||
}],
|
||||
})
|
||||
.then(async res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const row = await queryDatabaseChapter(videoID);
|
||||
const expected = {
|
||||
startTime: 0,
|
||||
endTime: 10,
|
||||
category: "chapter",
|
||||
actionType: "chapter",
|
||||
description: "This is a chapter"
|
||||
};
|
||||
assert.ok(partialDeepEquals(row, expected));
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should not be able to submit an music_offtopic with mute action type (JSON method)", (done) => {
|
||||
const videoID = "postSkip4";
|
||||
postSkipSegmentJSON({
|
||||
@@ -201,6 +230,46 @@ describe("postSkipSegments", () => {
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should not be able to submit a chapter with skip action type (JSON method)", (done) => {
|
||||
const videoID = "postSkipChapter2";
|
||||
postSkipSegmentJSON({
|
||||
userID: submitUserOne,
|
||||
videoID,
|
||||
segments: [{
|
||||
segment: [0, 10],
|
||||
category: "chapter",
|
||||
actionType: "skip"
|
||||
}],
|
||||
})
|
||||
.then(async res => {
|
||||
assert.strictEqual(res.status, 400);
|
||||
const row = await queryDatabaseActionType(videoID);
|
||||
assert.strictEqual(row, undefined);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should not be able to submit a sponsor with a description (JSON method)", (done) => {
|
||||
const videoID = "postSkipChapter3";
|
||||
postSkipSegmentJSON({
|
||||
userID: submitUserOne,
|
||||
videoID,
|
||||
segments: [{
|
||||
segment: [0, 10],
|
||||
category: "sponsor",
|
||||
description: "This is a sponsor"
|
||||
}],
|
||||
})
|
||||
.then(async res => {
|
||||
assert.strictEqual(res.status, 400);
|
||||
const row = await queryDatabaseActionType(videoID);
|
||||
assert.strictEqual(row, undefined);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to submit a single time with a duration from the YouTube API (JSON method)", (done) => {
|
||||
const videoID = "postSkip5";
|
||||
postSkipSegmentJSON({
|
||||
|
||||
@@ -14,8 +14,7 @@ export const partialDeepEquals = (actual: Record<string, any>, expected: Record<
|
||||
if (print) printActualExpected(actual, expected);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (actual?.[key] !== value) {
|
||||
} else if (actual?.[key] !== value) {
|
||||
if (print) printActualExpected(actual, expected);
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user