diff --git a/src/routes/getVideoLabel.ts b/src/routes/getVideoLabel.ts index c7784bc..84e6227 100644 --- a/src/routes/getVideoLabel.ts +++ b/src/routes/getVideoLabel.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import { db } from "../databases/databases"; import { videoLabelsHashKey, videoLabelsKey } from "../utils/redisKeys"; import { SBRecord } from "../types/lib.model"; -import { Category, DBSegment, Segment, Service, VideoData, VideoID, VideoIDHash } from "../types/segments.model"; +import { ActionType, Category, 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"; @@ -13,6 +13,7 @@ interface FullVideoSegment { interface FullVideoSegmentVideoData { segments: FullVideoSegment[]; + hasStartSegment: boolean; } function transformDBSegments(segments: DBSegment[]): FullVideoSegment[] { @@ -21,7 +22,7 @@ function transformDBSegments(segments: DBSegment[]): FullVideoSegment[] { })); } -async function getLabelsByVideoID(videoID: VideoID, service: Service): Promise { +async function getLabelsByVideoID(videoID: VideoID, service: Service): Promise { try { const segments: DBSegment[] = await getSegmentsFromDBByVideoID(videoID, service); return chooseSegment(segments); @@ -53,11 +54,13 @@ async function getLabelsByHash(hashedVideoIDPrefix: VideoIDHash, service: Servic }, {}); for (const [videoID, videoData] of Object.entries(segmentPerVideoID)) { + const result = chooseSegment(videoData.segments); const data: FullVideoSegmentVideoData = { - segments: chooseSegment(videoData.segments), + segments: result.segments, + hasStartSegment: result.hasStartSegment }; - if (data.segments.length > 0) { + if (data.segments.length > 0 || data.hasStartSegment) { segments[videoID] = data; } } @@ -74,7 +77,7 @@ async function getSegmentsFromDBByHash(hashedVideoIDPrefix: VideoIDHash, service .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`, + WHERE "hashedVideoID" LIKE ? AND "service" = ? AND "hidden" = 0 AND "shadowHidden" = 0`, [`${hashedVideoIDPrefix}%`, service] ) as Promise; @@ -90,22 +93,34 @@ async function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): P .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`, + WHERE "videoID" = ? AND "service" = ? AND "hidden" = 0 AND "shadowHidden" = 0`, [videoID, service] ) as Promise; return await QueryCacher.get(fetchFromDB, videoLabelsKey(videoID, service)); } -function chooseSegment(choices: T[]): FullVideoSegment[] { +function chooseSegment(choices: T[]): FullVideoSegmentVideoData { // filter out -2 segments choices = choices.filter((segment) => segment.votes > -2); + + const hasStartSegment = !!choices.some((segment) => segment.startTime < 5 + && (segment.actionType === ActionType.Skip || segment.actionType === ActionType.Mute)); + + choices = choices.filter((segment) => segment.actionType === ActionType.Full); + const results = []; // trivial decisions if (choices.length === 0) { - return []; + return { + segments: [], + hasStartSegment + }; } else if (choices.length === 1) { - return transformDBSegments(choices); + return { + segments: transformDBSegments(choices), + hasStartSegment + } } // if locked, only choose from locked const locked = choices.filter((segment) => segment.locked); @@ -114,7 +129,10 @@ function chooseSegment(choices: T[]): FullVideoSegment[] { } //no need to filter, just one label if (choices.length === 1) { - return transformDBSegments(choices); + return { + segments: transformDBSegments(choices), + hasStartSegment + }; } // sponsor > exclusive > selfpromo const findCategory = (category: string) => choices.find((segment) => segment.category === category); @@ -122,25 +140,36 @@ function chooseSegment(choices: T[]): FullVideoSegment[] { const categoryResult = findCategory("sponsor") ?? findCategory("exclusive_access") ?? findCategory("selfpromo"); if (categoryResult) results.push(categoryResult); - return transformDBSegments(results); + return { + segments: transformDBSegments(results), + hasStartSegment + }; } -async function handleGetLabel(req: Request, res: Response): Promise { +async function handleGetLabel(req: Request, res: Response): Promise { const videoID = req.query.videoID as VideoID; if (!videoID) { res.status(400).send("videoID not specified"); return false; } + const hasStartSegment = !!req.query.hasStartSegment; + const service = getService(req.query.service, req.body.service); - const segments = await getLabelsByVideoID(videoID, service); + const segmentData = await getLabelsByVideoID(videoID, service); + const segments = segmentData.segments; if (!segments || segments.length === 0) { res.sendStatus(404); return false; } - return segments; + if (hasStartSegment) { + return segmentData; + } else { + return segments; + } + } async function endpoint(req: Request, res: Response): Promise { diff --git a/src/routes/getVideoLabelByHash.ts b/src/routes/getVideoLabelByHash.ts index 23ba053..ae50906 100644 --- a/src/routes/getVideoLabelByHash.ts +++ b/src/routes/getVideoLabelByHash.ts @@ -21,6 +21,7 @@ export async function getVideoLabelsByHash(req: Request, res: Response): Promise const output = Object.entries(segments).map(([videoID, data]) => ({ videoID, segments: data.segments, + hasStartSegment: data.hasStartSegment })); return res.status(output.length === 0 ? 404 : 200).json(output); } diff --git a/src/utils/redisKeys.ts b/src/utils/redisKeys.ts index c5ced20..ce8e1d1 100644 --- a/src/utils/redisKeys.ts +++ b/src/utils/redisKeys.ts @@ -55,7 +55,7 @@ export const tempVIPKey = (userID: HashedUserID): string => `vip.temp.${userID}`; export const videoLabelsKey = (videoID: VideoID, service: Service): string => - `labels.v1.${service}.videoID.${videoID}`; + `labels.v2.${service}.videoID.${videoID}`; export function videoLabelsHashKey(hashedVideoIDPrefix: VideoIDHash, service: Service): string { hashedVideoIDPrefix = hashedVideoIDPrefix.substring(0, 3) as VideoIDHash; diff --git a/test/cases/getVideoLabelByHash.ts b/test/cases/getVideoLabelByHash.ts index dd95055..7b2c68d 100644 --- a/test/cases/getVideoLabelByHash.ts +++ b/test/cases/getVideoLabelByHash.ts @@ -6,26 +6,27 @@ 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]); + const query = 'INSERT INTO "sponsorTimes" ("videoID", "hashedVideoID", "votes", "locked", "UUID", "userID", "timeSubmitted", "category", "actionType", "hidden", "shadowHidden", "startTime", "endTime", "views") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0)'; + await db.prepare("run", query, ["getLabelHashSponsor" , getHash("getLabelHashSponsor", 1) , 2, 0, "labelhash01", "labeluser", 0, "sponsor", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashSponsor" , getHash("getLabelHashSponsor", 1) , 2, 0, "labelhash012", "labeluser", 0, "sponsor", "skip", 0, 0, 2]); + await db.prepare("run", query, ["getLabelHashEA" , getHash("getLabelHashEA", 1) , 2, 0, "labelhash02", "labeluser", 0, "exclusive_access", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashSelfpromo" , getHash("getLabelHashSelfpromo", 1) , 2, 0, "labelhash03", "labeluser", 0, "selfpromo", "full", 0, 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]); + await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash04", "labeluser", 0, "sponsor", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash05", "labeluser", 0, "exclusive_access", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash06", "labeluser", 0, "selfpromo", "full", 0, 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]); + await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 0, "labelhash07", "labeluser", 0, "sponsor", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 0, "labelhash08", "labeluser", 0, "exclusive_access", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 1, "labelhash09", "labeluser", 0, "selfpromo", "full", 0, 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]); + await db.prepare("run", query, ["getLabelHashDownvote" , getHash("getLabelHashDownvote", 1) , -2, 0, "labelhash10", "labeluser", 0, "selfpromo", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashHidden" , getHash("getLabelHashHidden", 1) , 2, 0, "labelhash11", "labeluser", 0, "selfpromo", "full", 1, 0, 0]); + await db.prepare("run", query, ["getLabelHashShHidden" , getHash("getLabelHashShHidden", 1) , 2, 0, "labelhash12", "labeluser", 0, "selfpromo", "full", 0, 1, 0]); // 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]); + await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , -2, 0, "labelhash13", "labeluser", 0, "sponsor", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , 2, 0, "labelhash14", "labeluser", 0, "exclusive_access", "full", 0, 0, 0]); + await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , 2, 0, "labelhash15", "labeluser", 0, "selfpromo", "full", 0, 0, 0]); return; }); @@ -46,6 +47,7 @@ describe("getVideoLabelHash", () => { validateLabel(data, videoID); const result = data[0].segments[0]; assert.strictEqual(result.category, "sponsor"); + assert.strictEqual(data[0].hasStartSegment, true); done(); }) .catch(err => done(err)); @@ -60,6 +62,7 @@ describe("getVideoLabelHash", () => { validateLabel(data, videoID); const result = data[0].segments[0]; assert.strictEqual(result.category, "exclusive_access"); + assert.strictEqual(data[0].hasStartSegment, false); done(); }) .catch(err => done(err));