From 6ece94453665f61b25580ae4591c3b0dc8a76db3 Mon Sep 17 00:00:00 2001 From: Ajay Date: Wed, 19 Jan 2022 17:48:09 -0500 Subject: [PATCH 1/2] Highlight category now has it's own action type --- databases/_upgrade_sponsorTimes_30.sql | 7 ++++++ src/routes/getSkipSegments.ts | 31 ++++++++++++++++++++++++-- src/types/segments.model.ts | 3 ++- test/cases/getSkipSegmentsByHash.ts | 20 +++++++++++++++-- 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 databases/_upgrade_sponsorTimes_30.sql diff --git a/databases/_upgrade_sponsorTimes_30.sql b/databases/_upgrade_sponsorTimes_30.sql new file mode 100644 index 0000000..f9b9a39 --- /dev/null +++ b/databases/_upgrade_sponsorTimes_30.sql @@ -0,0 +1,7 @@ +BEGIN TRANSACTION; + +UPDATE "sponsorTimes" SET "actionType" = 'poi' WHERE "category" = 'poi_highlight'; + +UPDATE "config" SET value = 30 WHERE key = 'version'; + +COMMIT; \ No newline at end of file diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index 81c0af2..6f3f898 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -66,6 +66,12 @@ async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: actionTypes: ActionType[], requiredSegments: SegmentUUID[], service: Service): Promise { const cache: SegmentCache = { shadowHiddenSegmentIPs: {} }; + // For old clients + const forcePoiAsSkip = !actionTypes.includes(ActionType.Poi) && categories.includes("poi_highlight" as Category); + if (forcePoiAsSkip) { + actionTypes.push(ActionType.Poi); + } + try { categories = categories.filter((category) => !/[^a-z|_|-]/.test(category)); if (categories.length === 0) return null; @@ -77,9 +83,17 @@ async function getSegmentsByVideoID(req: Request, videoID: VideoID, categories: }, {}); const canUseCache = requiredSegments.length === 0; - const processedSegments: Segment[] = await prepareCategorySegments(req, videoID, service, segments, cache, canUseCache); + let processedSegments: Segment[] = (await prepareCategorySegments(req, videoID, service, segments, cache, canUseCache)) + .filter((segment: Segment) => categories.includes(segment?.category) && (actionTypes.includes(segment?.actionType))); - return processedSegments.filter((segment: Segment) => categories.includes(segment?.category) && actionTypes.includes(segment?.actionType)); + if (forcePoiAsSkip) { + processedSegments = processedSegments.map((segment) => ({ + ...segment, + actionType: ActionType.Skip + })); + } + + return processedSegments; } catch (err) { if (err) { Logger.error(err as string); @@ -93,6 +107,12 @@ async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, const cache: SegmentCache = { shadowHiddenSegmentIPs: {} }; const segments: SBRecord = {}; + // For old clients + const forcePoiAsSkip = !actionTypes.includes(ActionType.Poi) && categories.includes("poi_highlight" as Category); + if (forcePoiAsSkip) { + actionTypes.push(ActionType.Poi); + } + try { type SegmentWithHashPerVideoID = SBRecord; @@ -123,6 +143,13 @@ async function getSegmentsByHash(req: Request, hashedVideoIDPrefix: VideoIDHash, data.segments = (await prepareCategorySegments(req, videoID as VideoID, service, videoData.segments, cache, canUseCache)) .filter((segment: Segment) => categories.includes(segment?.category) && actionTypes.includes(segment?.actionType)); + if (forcePoiAsSkip) { + data.segments = data.segments.map((segment) => ({ + ...segment, + actionType: ActionType.Skip + })); + } + if (data.segments.length > 0) { segments[videoID] = data; } diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index 04c5d1a..f2e6103 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -14,7 +14,8 @@ export enum ActionType { Skip = "skip", Mute = "mute", Chapter = "chapter", - Full = "full" + Full = "full", + Poi = "poi" } // Uncomment as needed diff --git a/test/cases/getSkipSegmentsByHash.ts b/test/cases/getSkipSegmentsByHash.ts index 0161906..4d18a0a 100644 --- a/test/cases/getSkipSegmentsByHash.ts +++ b/test/cases/getSkipSegmentsByHash.ts @@ -28,8 +28,8 @@ describe("getSkipSegmentsByHash", () => { await db.prepare("run", query, ["getSegmentsByHash-noMatchHash", 40, 50, 2, 0, "getSegmentsByHash-noMatchHash", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "fdaffnoMatchHash", ""]); await db.prepare("run", query, ["getSegmentsByHash-1", 60, 70, 2, 0, "getSegmentsByHash-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, "3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b", ""]); await db.prepare("run", query, ["onlyHidden", 60, 70, 2, 0, "onlyHidden", "testman", 0, 50, "sponsor", "skip", "YouTube", 1, 0, "f3a199e1af001d716cdc6599360e2b062c2d2b3fa2885f6d9d2fd741166cbbd3", ""]); - await db.prepare("run", query, ["highlightVid", 60, 60, 2, 0, "highlightVid-1", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); - await db.prepare("run", query, ["highlightVid", 70, 70, 2, 0, "highlightVid-2", "testman", 0, 50, "poi_highlight", "skip", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); + await db.prepare("run", query, ["highlightVid", 60, 60, 2, 0, "highlightVid-1", "testman", 0, 50, "poi_highlight", "poi", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); + await db.prepare("run", query, ["highlightVid", 70, 70, 2, 0, "highlightVid-2", "testman", 0, 50, "poi_highlight", "poi", "YouTube", 0, 0, getHash("highlightVid", 1), ""]); await db.prepare("run", query, ["requiredSegmentVid", 60, 70, 2, 0, "requiredSegmentVid-1", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); await db.prepare("run", query, ["requiredSegmentVid", 60, 70, -2, 0, "requiredSegmentVid-2", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); await db.prepare("run", query, ["requiredSegmentVid", 80, 90, -2, 0, "requiredSegmentVid-3", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, requiredSegmentVidHash, ""]); @@ -276,12 +276,28 @@ describe("getSkipSegmentsByHash", () => { }); it("Should only return one segment when fetching highlight segments", (done) => { + client.get(`${endpoint}/c962`, { params: { category: "poi_highlight", actionType: "poi" } }) + .then(res => { + assert.strictEqual(res.status, 200); + const data = res.data; + assert.strictEqual(data.length, 1); + assert.strictEqual(data[0].segments.length, 1); + assert.strictEqual(data[0].segments[0].category, "poi_highlight"); + assert.strictEqual(data[0].segments[0].actionType, "poi"); + done(); + }) + .catch(err => done(err)); + }); + + it("Should return skip actionType for highlight for old clients", (done) => { client.get(`${endpoint}/c962`, { params: { category: "poi_highlight" } }) .then(res => { assert.strictEqual(res.status, 200); const data = res.data; assert.strictEqual(data.length, 1); assert.strictEqual(data[0].segments.length, 1); + assert.strictEqual(data[0].segments[0].category, "poi_highlight"); + assert.strictEqual(data[0].segments[0].actionType, "skip"); done(); }) .catch(err => done(err)); From d367998d399558a21c4727dfcea2c044856382a1 Mon Sep 17 00:00:00 2001 From: Ajay Date: Wed, 19 Jan 2022 18:11:01 -0500 Subject: [PATCH 2/2] Remove use of "CategoryActionType" --- src/config.ts | 2 +- src/routes/getSkipSegments.ts | 5 ++--- src/routes/postSkipSegments.ts | 14 +++++++++----- src/routes/voteOnSponsorTime.ts | 7 +++---- src/types/segments.model.ts | 5 ----- src/utils/categoryInfo.ts | 9 --------- 6 files changed, 15 insertions(+), 27 deletions(-) delete mode 100644 src/utils/categoryInfo.ts diff --git a/src/config.ts b/src/config.ts index 97ca67d..f4e6175 100644 --- a/src/config.ts +++ b/src/config.ts @@ -30,7 +30,7 @@ addDefaults(config, { preview: ["skip", "mute"], filler: ["skip", "mute"], music_offtopic: ["skip"], - poi_highlight: ["skip"], + poi_highlight: ["poi"], chapter: ["chapter"] }, maxNumberOfActiveWarnings: 1, diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index 6f3f898..69b3105 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -4,8 +4,7 @@ import { config } from "../config"; import { db, privateDB } from "../databases/databases"; import { skipSegmentsHashKey, skipSegmentsKey, skipSegmentGroupsKey } from "../utils/redisKeys"; import { SBRecord } from "../types/lib.model"; -import { ActionType, Category, CategoryActionType, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, SegmentUUID, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; -import { getCategoryActionType } from "../utils/categoryInfo"; +import { ActionType, Category, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, SegmentUUID, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model"; import { getHashCache } from "../utils/getHashCache"; import { getIP } from "../utils/getIP"; import { Logger } from "../utils/logger"; @@ -263,7 +262,7 @@ async function chooseSegments(videoID: VideoID, service: Service, segments: DBSe // Filter for only 1 item for POI categories and Full video let chosenGroups = getWeightedRandomChoice(groups, 1, true, (choice) => choice.segments[0].actionType === ActionType.Full); - chosenGroups = getWeightedRandomChoice(chosenGroups, 1, true, (choice) => getCategoryActionType(choice.segments[0].category) === CategoryActionType.POI); + chosenGroups = getWeightedRandomChoice(chosenGroups, 1, true, (choice) => choice.segments[0].actionType === ActionType.Poi); return chosenGroups.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 39b79a2..568199d 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -9,9 +9,8 @@ import { getIP } from "../utils/getIP"; import { getFormattedTime } from "../utils/getFormattedTime"; import { dispatchEvent } from "../utils/webhookUtils"; import { Request, Response } from "express"; -import { ActionType, Category, CategoryActionType, IncomingSegment, IPAddress, SegmentUUID, Service, VideoDuration, VideoID } from "../types/segments.model"; +import { ActionType, Category, IncomingSegment, IPAddress, SegmentUUID, Service, VideoDuration, VideoID } from "../types/segments.model"; import { deleteLockCategories } from "./deleteLockCategories"; -import { getCategoryActionType } from "../utils/categoryInfo"; import { QueryCacher } from "../utils/queryCacher"; import { getReputation } from "../utils/reputation"; import { APIVideoData, APIVideoInfo } from "../types/youtubeApi.model"; @@ -375,6 +374,11 @@ async function checkEachSegmentValid(rawIP: IPAddress, paramUserID: UserID, user }; } + // For old clients + if (segments[i].category === "poi_highlight" && segments[i].actionType !== ActionType.Poi) { + segments[i].actionType = ActionType.Poi; + } + if (!config.categorySupport[segments[i].category]?.includes(segments[i].actionType)) { return { pass: false, errorMessage: "ActionType is not supported with this category.", errorCode: 400 }; } @@ -384,16 +388,16 @@ async function checkEachSegmentValid(rawIP: IPAddress, paramUserID: UserID, user if (isNaN(startTime) || isNaN(endTime) || startTime === Infinity || endTime === Infinity || startTime < 0 || startTime > endTime - || (getCategoryActionType(segments[i].category) === CategoryActionType.Skippable + || (segments[i].actionType !== ActionType.Poi && segments[i].actionType !== ActionType.Full && startTime === endTime) - || (getCategoryActionType(segments[i].category) === CategoryActionType.POI && startTime !== endTime) + || (segments[i].actionType === ActionType.Poi && startTime !== endTime) || (segments[i].actionType === ActionType.Full && (startTime !== 0 || endTime !== 0))) { //invalid request return { pass: false, errorMessage: "One of your segments times are invalid (too short, endTime before startTime, etc.)", errorCode: 400 }; } // Check for POI segments before some seconds - if (!isVIP && getCategoryActionType(segments[i].category) === CategoryActionType.POI && startTime < config.poiMinimumStartTime) { + if (!isVIP && segments[i].actionType === ActionType.Poi && startTime < config.poiMinimumStartTime) { return { pass: false, errorMessage: `POI cannot be that early`, errorCode: 400 }; } diff --git a/src/routes/voteOnSponsorTime.ts b/src/routes/voteOnSponsorTime.ts index e9585af..e0bdb58 100644 --- a/src/routes/voteOnSponsorTime.ts +++ b/src/routes/voteOnSponsorTime.ts @@ -10,8 +10,7 @@ import { getIP } from "../utils/getIP"; import { getHashCache } from "../utils/getHashCache"; import { config } from "../config"; import { HashedUserID, UserID } from "../types/user.model"; -import { Category, CategoryActionType, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, Visibility, VideoDuration, ActionType } from "../types/segments.model"; -import { getCategoryActionType } from "../utils/categoryInfo"; +import { Category, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, Visibility, VideoDuration, ActionType } from "../types/segments.model"; import { QueryCacher } from "../utils/queryCacher"; import axios from "axios"; import redis from "../utils/redis"; @@ -211,8 +210,8 @@ async function categoryVote(UUID: SegmentUUID, userID: UserID, isVIP: boolean, i if (!config.categoryList.includes(category)) { return { status: 400, message: "Category doesn't exist." }; } - if (getCategoryActionType(category) !== CategoryActionType.Skippable) { - return { status: 400, message: "Cannot vote for this category" }; + if (videoInfo.actionType === ActionType.Poi) { + return { status: 400, message: "Not allowed to change category for single point segments" }; } // Ignore vote if the next category is locked diff --git a/src/types/segments.model.ts b/src/types/segments.model.ts index f2e6103..5edb3e0 100644 --- a/src/types/segments.model.ts +++ b/src/types/segments.model.ts @@ -103,11 +103,6 @@ export interface SegmentCache { userHashedIP?: HashedIP } -export enum CategoryActionType { - Skippable, - POI -} - export interface DBLock { videoID: VideoID, userID: HashedUserID, diff --git a/src/utils/categoryInfo.ts b/src/utils/categoryInfo.ts deleted file mode 100644 index d573128..0000000 --- a/src/utils/categoryInfo.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Category, CategoryActionType } from "../types/segments.model"; - -export function getCategoryActionType(category: Category): CategoryActionType { - if (category.startsWith("poi_")) { - return CategoryActionType.POI; - } else { - return CategoryActionType.Skippable; - } -}