diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index d36a91a..530edd9 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -4,7 +4,8 @@ import { config } from '../config'; import { db, privateDB } from '../databases/databases'; import { skipSegmentsHashKey, skipSegmentsKey } from '../middleware/redisKeys'; import { SBRecord } from '../types/lib.model'; -import { Category, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; +import { Category, CategoryActionType, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; +import { getCategoryActionType } from '../utils/categoryInfo'; import { getHash } from '../utils/getHash'; import { getIP } from '../utils/getIP'; import { Logger } from '../utils/logger'; @@ -40,7 +41,8 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category: const filteredSegments = segments.filter((_, index) => shouldFilter[index]); - return chooseSegments(filteredSegments).map((chosenSegment) => ({ + const maxSegments = getCategoryActionType(category) === CategoryActionType.Skippable ? 32 : 1 + return chooseSegments(filteredSegments, maxSegments).map((chosenSegment) => ({ category, segment: [chosenSegment.startTime, chosenSegment.endTime], UUID: chosenSegment.UUID, @@ -206,7 +208,7 @@ function getWeightedRandomChoice(choices: T[], amountOf //Only one similar time will be returned, randomly generated based on the sqrt of votes. //This allows new less voted items to still sometimes appear to give them a chance at getting votes. //Segments with less than -1 votes are already ignored before this function is called -function chooseSegments(segments: DBSegment[]): DBSegment[] { +function chooseSegments(segments: DBSegment[], max: number): DBSegment[] { //Create groups of segments that are similar to eachother //Segments must be sorted by their startTime so that we can build groups chronologically: //1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group @@ -240,8 +242,8 @@ function chooseSegments(segments: DBSegment[]): DBSegment[] { } }); - //if there are too many groups, find the best 8 - return getWeightedRandomChoice(overlappingSegmentsGroups, 32).map( + //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 group => getWeightedRandomChoice(group.segments, 1)[0], ); diff --git a/src/routes/postSkipSegments.ts b/src/routes/postSkipSegments.ts index 2e0ba6c..0923182 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -13,8 +13,9 @@ import {dispatchEvent} from '../utils/webhookUtils'; import {Request, Response} from 'express'; import { skipSegmentsHashKey, skipSegmentsKey } from '../middleware/redisKeys'; import redis from '../utils/redis'; -import { Category, IncomingSegment, Segment, SegmentUUID, Service, VideoDuration, VideoID } from '../types/segments.model'; +import { Category, CategoryActionType, IncomingSegment, Segment, SegmentUUID, Service, VideoDuration, VideoID } from '../types/segments.model'; import { deleteNoSegments } from './deleteNoSegments'; +import { getCategoryActionType } from '../utils/categoryInfo'; interface APIVideoInfo { err: string | boolean, @@ -426,7 +427,8 @@ export async function postSkipSegments(req: Request, res: Response) { if (isNaN(startTime) || isNaN(endTime) || startTime === Infinity || endTime === Infinity || startTime < 0 || startTime > endTime - || (segments[i].category !== "highlight" && startTime === endTime) || (segments[i].category === "highlight" && startTime !== endTime)) { + || (getCategoryActionType(segments[i].category) === CategoryActionType.Skippable && startTime === endTime) + || (getCategoryActionType(segments[i].category) === CategoryActionType.POI && startTime !== endTime)) { //invalid request res.status(400).send("One of your segments times are invalid (too short, startTime before endTime, etc.)"); return; diff --git a/src/routes/voteOnSponsorTime.ts b/src/routes/voteOnSponsorTime.ts index 361e02f..f2e1539 100644 --- a/src/routes/voteOnSponsorTime.ts +++ b/src/routes/voteOnSponsorTime.ts @@ -13,7 +13,8 @@ import {config} from '../config'; import { UserID } from '../types/user.model'; import redis from '../utils/redis'; import { skipSegmentsHashKey, skipSegmentsKey } from '../middleware/redisKeys'; -import { Category, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash } from '../types/segments.model'; +import { Category, CategoryActionType, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash } from '../types/segments.model'; +import { getCategoryActionType } from '../utils/categoryInfo'; const voteTypes = { normal: 0, @@ -170,7 +171,7 @@ async function categoryVote(UUID: SegmentUUID, userID: UserID, isVIP: boolean, i res.status(400).send("Category doesn't exist."); return; } - if (category === "highlight") { + if (getCategoryActionType(category) !== CategoryActionType.Skippable) { res.status(400).send("Cannot vote for this category"); return; } diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index 329c41a..c1606a9 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -72,4 +72,9 @@ export interface VideoData { export interface SegmentCache { shadowHiddenSegmentIPs: SBRecord, userHashedIP?: HashedIP +} + +export enum CategoryActionType { + Skippable, + POI } \ No newline at end of file diff --git a/src/utils/categoryInfo.ts b/src/utils/categoryInfo.ts new file mode 100644 index 0000000..cd8b745 --- /dev/null +++ b/src/utils/categoryInfo.ts @@ -0,0 +1,10 @@ +import { Category, CategoryActionType } from "../types/segments.model"; + +export function getCategoryActionType(category: Category): CategoryActionType { + switch (category) { + case "highlight": + return CategoryActionType.POI; + default: + return CategoryActionType.Skippable; + } +} diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 23d9d49..c6c3414 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -19,6 +19,9 @@ describe('getSegmentsByHash', () => { await db.prepare("run", startOfQuery + "('getSegmentsByHash-noMatchHash', 40, 50, 2, 'getSegmentsByHash-noMatchHash', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, 'fdaffnoMatchHash')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910 await db.prepare("run", startOfQuery + "('getSegmentsByHash-1', 60, 70, 2, 'getSegmentsByHash-1', 'testman', 0, 50, 'sponsor', 'YouTube', 0, 0, '" + getHash('getSegmentsByHash-1', 1) + "')"); // hash = 3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b await db.prepare("run", startOfQuery + "('onlyHidden', 60, 70, 2, 'onlyHidden', 'testman', 0, 50, 'sponsor', 'YouTube', 1, 0, '" + getHash('onlyHidden', 1) + "')"); // hash = f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3 + await db.prepare("run", startOfQuery + "('highlightVid', 60, 60, 2, 'highlightVid-1', 'testman', 0, 50, 'highlight', 'YouTube', 0, 0, '" + getHash('highlightVid', 1) + "')"); // hash = c962d387a9e50170c9118405d20b1081cee8659cd600b856b511f695b91455cb + await db.prepare("run", startOfQuery + "('highlightVid', 70, 70, 2, 'highlightVid-2', 'testman', 0, 50, 'highlight', 'YouTube', 0, 0, '" + getHash('highlightVid', 1) + "')"); // hash = c962d387a9e50170c9118405d20b1081cee8659cd600b856b511f695b91455cb + }); it('Should be able to get a 200', (done: Done) => { @@ -158,7 +161,7 @@ describe('getSegmentsByHash', () => { if (res.status !== 200) done("non 200 status code, was " + res.status); else { const body = await res.json(); - if (body.length !== 1) done("expected 2 videos, got " + body.length); + if (body.length !== 1) done("expected 1 video, got " + body.length); else if (body[0].segments.length !== 1) done("expected 1 segments for first video, got " + body[0].segments.length); else if (body[0].segments[0].UUID !== 'getSegmentsByHash-0-0-1') done("both segments are not sponsor"); else done(); @@ -167,6 +170,20 @@ describe('getSegmentsByHash', () => { .catch(err => done("Couldn't call endpoint")); }); + it('Should only return one segment when fetching highlight segments', (done: Done) => { + fetch(getbaseURL() + '/api/skipSegments/c962?category=highlight') + .then(async res => { + if (res.status !== 200) done("non 200 status code, was " + res.status); + else { + const body = await res.json(); + if (body.length !== 1) done("expected 1 video, got " + body.length); + else if (body[0].segments.length !== 1) done("expected 1 segment, got " + body[0].segments.length); + else done(); + } + }) + .catch(err => done("Couldn't call endpoint")); + }); + it('Should be able to post a segment and get it using endpoint', (done: Done) => { let testID = 'abc123goodVideo'; fetch(getbaseURL() + "/api/postVideoSponsorTimes", { diff --git a/test/utils.ts b/test/utils.ts index c76065c..2ff13ae 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -7,4 +7,4 @@ export function getbaseURL() { /** * Duplicated from Mocha types. TypeScript doesn't infer that type by itself for some reason. */ -export type Done = (err?: any) => void; +export type Done = (err?: any) => void; \ No newline at end of file