mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2026-03-14 14:32:42 +03:00
Merge pull request #463 from mchangrh/fullVideoLabels
Full Video Labels endpoint
This commit is contained in:
@@ -45,6 +45,8 @@ import { youtubeApiProxy } from "./routes/youtubeApiProxy";
|
||||
import { getChapterNames } from "./routes/getChapterNames";
|
||||
import { getTopCategoryUsers } from "./routes/getTopCategoryUsers";
|
||||
import { addUserAsTempVIP } from "./routes/addUserAsTempVIP";
|
||||
import { endpoint as getVideoLabels } from "./routes/getVideoLabel";
|
||||
import { getVideoLabelsByHash } from "./routes/getVideoLabelByHash";
|
||||
import { addFeature } from "./routes/addFeature";
|
||||
import { generateTokenRequest } from "./routes/generateToken";
|
||||
import { verifyTokenRequest } from "./routes/verifyToken";
|
||||
@@ -200,6 +202,10 @@ function setupRoutes(router: Router) {
|
||||
router.get("/api/generateToken/:type", generateTokenRequest);
|
||||
router.get("/api/verifyToken", verifyTokenRequest);
|
||||
|
||||
// labels
|
||||
router.get("/api/videoLabels", getVideoLabels);
|
||||
router.get("/api/videoLabels/:prefix", getVideoLabelsByHash);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (config.postgres?.enabled) {
|
||||
router.get("/database", (req, res) => dumpDatabase(req, res, true));
|
||||
@@ -212,4 +218,4 @@ function setupRoutes(router: Router) {
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-misused-promises */
|
||||
/* eslint-enable @typescript-eslint/no-misused-promises */
|
||||
|
||||
167
src/routes/getVideoLabel.ts
Normal file
167
src/routes/getVideoLabel.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { Request, Response } from "express";
|
||||
import { db } from "../databases/databases";
|
||||
import { videoLabelsHashKey, videoLabelsKey } from "../utils/redisKeys";
|
||||
import { SBRecord } from "../types/lib.model";
|
||||
import { DBSegment, Segment, Service, VideoData, VideoID, VideoIDHash } from "../types/segments.model";
|
||||
import { Logger } from "../utils/logger";
|
||||
import { QueryCacher } from "../utils/queryCacher";
|
||||
import { getService } from "../utils/getService";
|
||||
|
||||
function transformDBSegments(segments: DBSegment[]): Segment[] {
|
||||
return segments.map((chosenSegment) => ({
|
||||
category: chosenSegment.category,
|
||||
actionType: chosenSegment.actionType,
|
||||
segment: [chosenSegment.startTime, chosenSegment.endTime],
|
||||
UUID: chosenSegment.UUID,
|
||||
locked: chosenSegment.locked,
|
||||
votes: chosenSegment.votes,
|
||||
videoDuration: chosenSegment.videoDuration,
|
||||
userID: chosenSegment.userID,
|
||||
description: chosenSegment.description
|
||||
}));
|
||||
}
|
||||
|
||||
async function getLabelsByVideoID(videoID: VideoID, service: Service): Promise<Segment[]> {
|
||||
try {
|
||||
const segments: DBSegment[] = await getSegmentsFromDBByVideoID(videoID, service);
|
||||
return chooseSegment(segments);
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
Logger.error(err as string);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getLabelsByHash(hashedVideoIDPrefix: VideoIDHash, service: Service): Promise<SBRecord<VideoID, VideoData>> {
|
||||
const segments: SBRecord<VideoID, VideoData> = {};
|
||||
|
||||
try {
|
||||
type SegmentWithHashPerVideoID = SBRecord<VideoID, { hash: VideoIDHash, segments: DBSegment[] }>;
|
||||
|
||||
const segmentPerVideoID: SegmentWithHashPerVideoID = (await getSegmentsFromDBByHash(hashedVideoIDPrefix, service))
|
||||
.reduce((acc: SegmentWithHashPerVideoID, segment: DBSegment) => {
|
||||
acc[segment.videoID] = acc[segment.videoID] || {
|
||||
hash: segment.hashedVideoID,
|
||||
segments: []
|
||||
};
|
||||
|
||||
acc[segment.videoID].segments ??= [];
|
||||
acc[segment.videoID].segments.push(segment);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
for (const [videoID, videoData] of Object.entries(segmentPerVideoID)) {
|
||||
const data: VideoData = {
|
||||
hash: videoData.hash,
|
||||
segments: chooseSegment(videoData.segments),
|
||||
};
|
||||
|
||||
if (data.segments.length > 0) {
|
||||
segments[videoID] = data;
|
||||
}
|
||||
}
|
||||
|
||||
return segments;
|
||||
} catch (err) {
|
||||
Logger.error(err as string);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getSegmentsFromDBByHash(hashedVideoIDPrefix: VideoIDHash, service: Service): Promise<DBSegment[]> {
|
||||
const fetchFromDB = () => db
|
||||
.prepare(
|
||||
"all",
|
||||
`SELECT "startTime", "endTime", "videoID", "votes", "locked", "UUID", "userID", "category", "actionType", "hashedVideoID", "description" FROM "sponsorTimes"
|
||||
WHERE "hashedVideoID" LIKE ? AND "service" = ? AND "actionType" = 'full' AND "hidden" = 0 AND "shadowHidden" = 0`,
|
||||
[`${hashedVideoIDPrefix}%`, service]
|
||||
) as Promise<DBSegment[]>;
|
||||
|
||||
if (hashedVideoIDPrefix.length === 4) {
|
||||
return await QueryCacher.get(fetchFromDB, videoLabelsHashKey(hashedVideoIDPrefix, service));
|
||||
}
|
||||
|
||||
return await fetchFromDB();
|
||||
}
|
||||
|
||||
async function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): Promise<DBSegment[]> {
|
||||
const fetchFromDB = () => db
|
||||
.prepare(
|
||||
"all",
|
||||
`SELECT "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "description" FROM "sponsorTimes"
|
||||
WHERE "videoID" = ? AND "service" = ? AND "actionType" = 'full' AND "hidden" = 0 AND "shadowHidden" = 0`,
|
||||
[videoID, service]
|
||||
) as Promise<DBSegment[]>;
|
||||
|
||||
return await QueryCacher.get(fetchFromDB, videoLabelsKey(videoID, service));
|
||||
}
|
||||
|
||||
function chooseSegment<T extends DBSegment>(choices: T[]): Segment[] {
|
||||
// filter out -2 segments
|
||||
choices = choices.filter((segment) => segment.votes > -2);
|
||||
const results = [];
|
||||
// trivial decisions
|
||||
if (choices.length === 0) {
|
||||
return [];
|
||||
} else if (choices.length === 1) {
|
||||
return transformDBSegments(choices);
|
||||
}
|
||||
// if locked, only choose from locked
|
||||
const locked = choices.filter((segment) => segment.locked);
|
||||
if (locked.length > 0) {
|
||||
choices = locked;
|
||||
}
|
||||
//no need to filter, just one label
|
||||
if (choices.length === 1) {
|
||||
return transformDBSegments(choices);
|
||||
}
|
||||
// sponsor > exclusive > selfpromo
|
||||
const findCategory = (category: string) => choices.find((segment) => segment.category === category);
|
||||
|
||||
const categoryResult = findCategory("sponsor") ?? findCategory("exclusive_access") ?? findCategory("selfpromo");
|
||||
if (categoryResult) results.push(categoryResult);
|
||||
|
||||
return transformDBSegments(results);
|
||||
}
|
||||
|
||||
async function handleGetLabel(req: Request, res: Response): Promise<Segment[] | false> {
|
||||
const videoID = req.query.videoID as VideoID;
|
||||
if (!videoID) {
|
||||
res.status(400).send("videoID not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
const service = getService(req.query.service, req.body.service);
|
||||
const segments = await getLabelsByVideoID(videoID, service);
|
||||
|
||||
if (!segments || segments.length === 0) {
|
||||
res.sendStatus(404);
|
||||
return false;
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
async function endpoint(req: Request, res: Response): Promise<Response> {
|
||||
try {
|
||||
const segments = await handleGetLabel(req, res);
|
||||
|
||||
// If false, res.send has already been called
|
||||
if (segments) {
|
||||
//send result
|
||||
return res.send(segments);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof SyntaxError) {
|
||||
return res.status(400).send("Categories parameter does not match format requirements.");
|
||||
} else return res.sendStatus(500);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getLabelsByVideoID,
|
||||
getLabelsByHash,
|
||||
endpoint
|
||||
};
|
||||
27
src/routes/getVideoLabelByHash.ts
Normal file
27
src/routes/getVideoLabelByHash.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { hashPrefixTester } from "../utils/hashPrefixTester";
|
||||
import { getLabelsByHash } from "./getVideoLabel";
|
||||
import { Request, Response } from "express";
|
||||
import { VideoIDHash, Service } from "../types/segments.model";
|
||||
import { getService } from "../utils/getService";
|
||||
|
||||
export async function getVideoLabelsByHash(req: Request, res: Response): Promise<Response> {
|
||||
let hashPrefix = req.params.prefix as VideoIDHash;
|
||||
if (!req.params.prefix || !hashPrefixTester(req.params.prefix)) {
|
||||
return res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix
|
||||
}
|
||||
hashPrefix = hashPrefix.toLowerCase() as VideoIDHash;
|
||||
|
||||
const service: Service = getService(req.query.service, req.body.service);
|
||||
|
||||
// Get all video id's that match hash prefix
|
||||
const segments = await getLabelsByHash(hashPrefix, service);
|
||||
|
||||
if (!segments) return res.status(404).json([]);
|
||||
|
||||
const output = Object.entries(segments).map(([videoID, data]) => ({
|
||||
videoID,
|
||||
hash: data.hash,
|
||||
segments: data.segments,
|
||||
}));
|
||||
return res.status(output.length === 0 ? 404 : 200).json(output);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { HashedUserID, 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" | "filler" | "poi_highlight" | "chapter") & { __categoryBrand: unknown };
|
||||
export type Category = ("sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "poi_highlight" | "chapter" | "filler" | "exclusive_access") & { __categoryBrand: unknown };
|
||||
export type VideoIDHash = VideoID & HashedValue;
|
||||
export type IPAddress = string & { __ipAddressBrand: unknown };
|
||||
export type HashedIP = IPAddress & HashedValue;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import redis from "../utils/redis";
|
||||
import { Logger } from "../utils/logger";
|
||||
import { skipSegmentsHashKey, skipSegmentsKey, reputationKey, ratingHashKey, skipSegmentGroupsKey, userFeatureKey } from "./redisKeys";
|
||||
import { skipSegmentsHashKey, skipSegmentsKey, reputationKey, ratingHashKey, skipSegmentGroupsKey, userFeatureKey, videoLabelsKey, videoLabelsHashKey } from "./redisKeys";
|
||||
import { Service, VideoID, VideoIDHash } from "../types/segments.model";
|
||||
import { Feature, HashedUserID, UserID } from "../types/user.model";
|
||||
import { config } from "../config";
|
||||
@@ -81,6 +81,8 @@ function clearSegmentCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoID
|
||||
redis.del(skipSegmentsKey(videoInfo.videoID, videoInfo.service)).catch((err) => Logger.error(err));
|
||||
redis.del(skipSegmentGroupsKey(videoInfo.videoID, videoInfo.service)).catch((err) => Logger.error(err));
|
||||
redis.del(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service)).catch((err) => Logger.error(err));
|
||||
redis.del(videoLabelsKey(videoInfo.hashedVideoID, videoInfo.service)).catch((err) => Logger.error(err));
|
||||
redis.del(videoLabelsHashKey(videoInfo.hashedVideoID, videoInfo.service)).catch((err) => Logger.error(err));
|
||||
if (videoInfo.userID) redis.del(reputationKey(videoInfo.userID)).catch((err) => Logger.error(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,16 @@ export function shaHashKey(singleIter: HashedValue): string {
|
||||
export const tempVIPKey = (userID: HashedUserID): string =>
|
||||
`vip.temp.${userID}`;
|
||||
|
||||
export const videoLabelsKey = (videoID: VideoID, service: Service): string =>
|
||||
`labels.v1.${service}.videoID.${videoID}`;
|
||||
|
||||
export function videoLabelsHashKey(hashedVideoIDPrefix: VideoIDHash, service: Service): string {
|
||||
hashedVideoIDPrefix = hashedVideoIDPrefix.substring(0, 4) as VideoIDHash;
|
||||
if (hashedVideoIDPrefix.length !== 4) Logger.warn(`Redis skip segment hash-prefix key is not length 4! ${hashedVideoIDPrefix}`);
|
||||
|
||||
return `labels.v1.${service}.${hashedVideoIDPrefix}`;
|
||||
}
|
||||
|
||||
export function userFeatureKey (userID: HashedUserID, feature: Feature): string {
|
||||
return `user.${userID}.feature.${feature}`;
|
||||
}
|
||||
184
test/cases/getVideoLabelByHash.ts
Normal file
184
test/cases/getVideoLabelByHash.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import { db } from "../../src/databases/databases";
|
||||
import assert from "assert";
|
||||
import { client } from "../utils/httpClient";
|
||||
import { getHash } from "../../src/utils/getHash";
|
||||
|
||||
describe("getVideoLabelHash", () => {
|
||||
const endpoint = "/api/videoLabels";
|
||||
before(async () => {
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "hashedVideoID", "votes", "locked", "UUID", "userID", "timeSubmitted", "category", "actionType", "hidden", "shadowHidden", "startTime", "endTime", "views") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0)';
|
||||
await db.prepare("run", query, ["getLabelHashSponsor" , getHash("getLabelHashSponsor", 1) , 2, 0, "labelhash01", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashEA" , getHash("getLabelHashEA", 1) , 2, 0, "labelhash02", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashSelfpromo" , getHash("getLabelHashSelfpromo", 1) , 2, 0, "labelhash03", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// priority override
|
||||
await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash04", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash05", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash06", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// locked only
|
||||
await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 0, "labelhash07", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 0, "labelhash08", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 1, "labelhash09", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// hidden segments
|
||||
await db.prepare("run", query, ["getLabelHashDownvote" , getHash("getLabelHashDownvote", 1) , -2, 0, "labelhash10", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashHidden" , getHash("getLabelHashHidden", 1) , 2, 0, "labelhash11", "labeluser", 0, "selfpromo", "full", 1, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashShHidden" , getHash("getLabelHashShHidden", 1) , 2, 0, "labelhash12", "labeluser", 0, "selfpromo", "full", 0, 1]);
|
||||
// priority override2
|
||||
await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , -2, 0, "labelhash13", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , 2, 0, "labelhash14", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , 2, 0, "labelhash15", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
function validateLabel(data: any, videoID: string) {
|
||||
assert.strictEqual(data[0].videoID, videoID);
|
||||
assert.strictEqual(data[0].segments.length, 1);
|
||||
assert.strictEqual(data[0].segments[0].segment[0], 0);
|
||||
assert.strictEqual(data[0].segments[0].segment[1], 0);
|
||||
assert.strictEqual(data[0].segments[0].actionType, "full");
|
||||
assert.strictEqual(data[0].segments[0].userID, "labeluser");
|
||||
}
|
||||
|
||||
const get = (videoID: string) => client.get(`${endpoint}/${getHash(videoID, 1).substring(0, 4)}`);
|
||||
|
||||
it("Should be able to get sponsor only label", (done) => {
|
||||
const videoID = "getLabelHashSponsor";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "sponsor");
|
||||
assert.strictEqual(result.UUID, "labelhash01");
|
||||
assert.strictEqual(result.locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get exclusive access only label", (done) => {
|
||||
const videoID = "getLabelHashEA";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "exclusive_access");
|
||||
assert.strictEqual(result.UUID, "labelhash02");
|
||||
assert.strictEqual(result.locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get selfpromo only label", (done) => {
|
||||
const videoID = "getLabelHashSelfpromo";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "selfpromo");
|
||||
assert.strictEqual(result.UUID, "labelhash03");
|
||||
assert.strictEqual(result.locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get only sponsor if multiple segments exist", (done) => {
|
||||
const videoID = "getLabelHashPriority";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "sponsor");
|
||||
assert.strictEqual(result.UUID, "labelhash04");
|
||||
assert.strictEqual(result.locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should override priority if locked", (done) => {
|
||||
const videoID = "getLabelHashLocked";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "selfpromo");
|
||||
assert.strictEqual(result.UUID, "labelhash09");
|
||||
assert.strictEqual(result.locked, 1);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get highest priority category", (done) => {
|
||||
const videoID = "getLabelHashPriority2";
|
||||
get(videoID)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data, videoID);
|
||||
const result = data[0].segments[0];
|
||||
assert.strictEqual(result.category, "exclusive_access");
|
||||
assert.strictEqual(result.UUID, "labelhash14");
|
||||
assert.strictEqual(result.locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are downvoted", (done) => {
|
||||
get("getLabelHashDownvote")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are hidden", (done) => {
|
||||
get("getLabelHashHidden")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are shadowhidden", (done) => {
|
||||
get("getLabelHashShHidden")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if no segment found", (done) => {
|
||||
get("notarealvideo")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get 400 if no videoID passed in", (done) => {
|
||||
client.get(endpoint)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 400);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
});
|
||||
170
test/cases/getVideoLabels.ts
Normal file
170
test/cases/getVideoLabels.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { db } from "../../src/databases/databases";
|
||||
import assert from "assert";
|
||||
import { client } from "../utils/httpClient";
|
||||
|
||||
describe("getVideoLabels", () => {
|
||||
const endpoint = "/api/videoLabels";
|
||||
before(async () => {
|
||||
const query = 'INSERT INTO "sponsorTimes" ("videoID", "votes", "locked", "UUID", "userID", "timeSubmitted", "category", "actionType", "hidden", "shadowHidden", "startTime", "endTime", "views") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, 0)';
|
||||
await db.prepare("run", query, ["getLabelSponsor" , 2, 0, "label01", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelEA" , 2, 0, "label02", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelSelfpromo" , 2, 0, "label03", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// priority override
|
||||
await db.prepare("run", query, ["getLabelPriority" , 2, 0, "label04", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelPriority" , 2, 0, "label05", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelPriority" , 2, 0, "label06", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// locked only
|
||||
await db.prepare("run", query, ["getLabelLocked" , 2, 0, "label07", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelLocked" , 2, 0, "label08", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelLocked" , 2, 1, "label09", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
// hidden segments
|
||||
await db.prepare("run", query, ["getLabelDownvote" ,-2, 0, "label10", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelHidden" ,2, 0, "label11", "labeluser", 0, "selfpromo", "full", 1, 0]);
|
||||
await db.prepare("run", query, ["getLabelShadowHidden",2, 0, "label12", "labeluser", 0, "selfpromo", "full", 0, 1]);
|
||||
// priority override2
|
||||
await db.prepare("run", query, ["getLabelPriority2" , -2, 0, "label13", "labeluser", 0, "sponsor", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelPriority2" , 2, 0, "label14", "labeluser", 0, "exclusive_access", "full", 0, 0]);
|
||||
await db.prepare("run", query, ["getLabelPriority2" , 2, 0, "label15", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
function validateLabel(result: any) {
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0].segment[0], 0);
|
||||
assert.strictEqual(result[0].segment[1], 0);
|
||||
assert.strictEqual(result[0].actionType, "full");
|
||||
assert.strictEqual(result[0].userID, "labeluser");
|
||||
}
|
||||
|
||||
const get = (videoID: string) => client.get(endpoint, { params: { videoID } });
|
||||
|
||||
it("Should be able to get sponsor only label", (done) => {
|
||||
get("getLabelSponsor")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "sponsor");
|
||||
assert.strictEqual(data[0].UUID, "label01");
|
||||
assert.strictEqual(data[0].locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get exclusive access only label", (done) => {
|
||||
get("getLabelEA")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "exclusive_access");
|
||||
assert.strictEqual(data[0].UUID, "label02");
|
||||
assert.strictEqual(data[0].locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should be able to get selfpromo only label", (done) => {
|
||||
get("getLabelSelfpromo")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "selfpromo");
|
||||
assert.strictEqual(data[0].UUID, "label03");
|
||||
assert.strictEqual(data[0].locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get only sponsor if multiple segments exist", (done) => {
|
||||
get("getLabelPriority")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "sponsor");
|
||||
assert.strictEqual(data[0].UUID, "label04");
|
||||
assert.strictEqual(data[0].locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should override priority if locked", (done) => {
|
||||
get("getLabelLocked")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "selfpromo");
|
||||
assert.strictEqual(data[0].UUID, "label09");
|
||||
assert.strictEqual(data[0].locked, 1);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get highest priority category", (done) => {
|
||||
get("getLabelPriority2")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 200);
|
||||
const data = res.data;
|
||||
validateLabel(data);
|
||||
assert.strictEqual(data[0].category, "exclusive_access");
|
||||
assert.strictEqual(data[0].UUID, "label14");
|
||||
assert.strictEqual(data[0].locked, 0);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are downvoted", (done) => {
|
||||
get("getLabelDownvote")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are hidden", (done) => {
|
||||
get("getLabelHidden")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if all submissions are shadowhidden", (done) => {
|
||||
get("getLabelShadowHidden")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should return 404 if no segment found", (done) => {
|
||||
get("notarealvideo")
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 404);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
it("Should get 400 if no videoID passed in", (done) => {
|
||||
client.get(endpoint)
|
||||
.then(res => {
|
||||
assert.strictEqual(res.status, 400);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user