mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-06 19:47:00 +03:00
Add hasStartSegment to video label
This commit is contained in:
@@ -2,7 +2,7 @@ import { Request, Response } from "express";
|
|||||||
import { db } from "../databases/databases";
|
import { db } from "../databases/databases";
|
||||||
import { videoLabelsHashKey, videoLabelsKey } from "../utils/redisKeys";
|
import { videoLabelsHashKey, videoLabelsKey } from "../utils/redisKeys";
|
||||||
import { SBRecord } from "../types/lib.model";
|
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 { Logger } from "../utils/logger";
|
||||||
import { QueryCacher } from "../utils/queryCacher";
|
import { QueryCacher } from "../utils/queryCacher";
|
||||||
import { getService } from "../utils/getService";
|
import { getService } from "../utils/getService";
|
||||||
@@ -13,6 +13,7 @@ interface FullVideoSegment {
|
|||||||
|
|
||||||
interface FullVideoSegmentVideoData {
|
interface FullVideoSegmentVideoData {
|
||||||
segments: FullVideoSegment[];
|
segments: FullVideoSegment[];
|
||||||
|
hasStartSegment: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformDBSegments(segments: DBSegment[]): FullVideoSegment[] {
|
function transformDBSegments(segments: DBSegment[]): FullVideoSegment[] {
|
||||||
@@ -21,7 +22,7 @@ function transformDBSegments(segments: DBSegment[]): FullVideoSegment[] {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLabelsByVideoID(videoID: VideoID, service: Service): Promise<FullVideoSegment[]> {
|
async function getLabelsByVideoID(videoID: VideoID, service: Service): Promise<FullVideoSegmentVideoData> {
|
||||||
try {
|
try {
|
||||||
const segments: DBSegment[] = await getSegmentsFromDBByVideoID(videoID, service);
|
const segments: DBSegment[] = await getSegmentsFromDBByVideoID(videoID, service);
|
||||||
return chooseSegment(segments);
|
return chooseSegment(segments);
|
||||||
@@ -53,11 +54,13 @@ async function getLabelsByHash(hashedVideoIDPrefix: VideoIDHash, service: Servic
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
for (const [videoID, videoData] of Object.entries(segmentPerVideoID)) {
|
for (const [videoID, videoData] of Object.entries(segmentPerVideoID)) {
|
||||||
|
const result = chooseSegment(videoData.segments);
|
||||||
const data: FullVideoSegmentVideoData = {
|
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;
|
segments[videoID] = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +77,7 @@ async function getSegmentsFromDBByHash(hashedVideoIDPrefix: VideoIDHash, service
|
|||||||
.prepare(
|
.prepare(
|
||||||
"all",
|
"all",
|
||||||
`SELECT "startTime", "endTime", "videoID", "votes", "locked", "UUID", "userID", "category", "actionType", "hashedVideoID", "description" FROM "sponsorTimes"
|
`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]
|
[`${hashedVideoIDPrefix}%`, service]
|
||||||
) as Promise<DBSegment[]>;
|
) as Promise<DBSegment[]>;
|
||||||
|
|
||||||
@@ -90,22 +93,34 @@ async function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): P
|
|||||||
.prepare(
|
.prepare(
|
||||||
"all",
|
"all",
|
||||||
`SELECT "startTime", "endTime", "votes", "locked", "UUID", "userID", "category", "actionType", "description" FROM "sponsorTimes"
|
`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]
|
[videoID, service]
|
||||||
) as Promise<DBSegment[]>;
|
) as Promise<DBSegment[]>;
|
||||||
|
|
||||||
return await QueryCacher.get(fetchFromDB, videoLabelsKey(videoID, service));
|
return await QueryCacher.get(fetchFromDB, videoLabelsKey(videoID, service));
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseSegment<T extends DBSegment>(choices: T[]): FullVideoSegment[] {
|
function chooseSegment<T extends DBSegment>(choices: T[]): FullVideoSegmentVideoData {
|
||||||
// filter out -2 segments
|
// filter out -2 segments
|
||||||
choices = choices.filter((segment) => segment.votes > -2);
|
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 = [];
|
const results = [];
|
||||||
// trivial decisions
|
// trivial decisions
|
||||||
if (choices.length === 0) {
|
if (choices.length === 0) {
|
||||||
return [];
|
return {
|
||||||
|
segments: [],
|
||||||
|
hasStartSegment
|
||||||
|
};
|
||||||
} else if (choices.length === 1) {
|
} else if (choices.length === 1) {
|
||||||
return transformDBSegments(choices);
|
return {
|
||||||
|
segments: transformDBSegments(choices),
|
||||||
|
hasStartSegment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if locked, only choose from locked
|
// if locked, only choose from locked
|
||||||
const locked = choices.filter((segment) => segment.locked);
|
const locked = choices.filter((segment) => segment.locked);
|
||||||
@@ -114,7 +129,10 @@ function chooseSegment<T extends DBSegment>(choices: T[]): FullVideoSegment[] {
|
|||||||
}
|
}
|
||||||
//no need to filter, just one label
|
//no need to filter, just one label
|
||||||
if (choices.length === 1) {
|
if (choices.length === 1) {
|
||||||
return transformDBSegments(choices);
|
return {
|
||||||
|
segments: transformDBSegments(choices),
|
||||||
|
hasStartSegment
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// sponsor > exclusive > selfpromo
|
// sponsor > exclusive > selfpromo
|
||||||
const findCategory = (category: string) => choices.find((segment) => segment.category === category);
|
const findCategory = (category: string) => choices.find((segment) => segment.category === category);
|
||||||
@@ -122,25 +140,36 @@ function chooseSegment<T extends DBSegment>(choices: T[]): FullVideoSegment[] {
|
|||||||
const categoryResult = findCategory("sponsor") ?? findCategory("exclusive_access") ?? findCategory("selfpromo");
|
const categoryResult = findCategory("sponsor") ?? findCategory("exclusive_access") ?? findCategory("selfpromo");
|
||||||
if (categoryResult) results.push(categoryResult);
|
if (categoryResult) results.push(categoryResult);
|
||||||
|
|
||||||
return transformDBSegments(results);
|
return {
|
||||||
|
segments: transformDBSegments(results),
|
||||||
|
hasStartSegment
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleGetLabel(req: Request, res: Response): Promise<FullVideoSegment[] | false> {
|
async function handleGetLabel(req: Request, res: Response): Promise<FullVideoSegmentVideoData | FullVideoSegment[] | false> {
|
||||||
const videoID = req.query.videoID as VideoID;
|
const videoID = req.query.videoID as VideoID;
|
||||||
if (!videoID) {
|
if (!videoID) {
|
||||||
res.status(400).send("videoID not specified");
|
res.status(400).send("videoID not specified");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasStartSegment = !!req.query.hasStartSegment;
|
||||||
|
|
||||||
const service = getService(req.query.service, req.body.service);
|
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) {
|
if (!segments || segments.length === 0) {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return segments;
|
if (hasStartSegment) {
|
||||||
|
return segmentData;
|
||||||
|
} else {
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function endpoint(req: Request, res: Response): Promise<Response> {
|
async function endpoint(req: Request, res: Response): Promise<Response> {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export async function getVideoLabelsByHash(req: Request, res: Response): Promise
|
|||||||
const output = Object.entries(segments).map(([videoID, data]) => ({
|
const output = Object.entries(segments).map(([videoID, data]) => ({
|
||||||
videoID,
|
videoID,
|
||||||
segments: data.segments,
|
segments: data.segments,
|
||||||
|
hasStartSegment: data.hasStartSegment
|
||||||
}));
|
}));
|
||||||
return res.status(output.length === 0 ? 404 : 200).json(output);
|
return res.status(output.length === 0 ? 404 : 200).json(output);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const tempVIPKey = (userID: HashedUserID): string =>
|
|||||||
`vip.temp.${userID}`;
|
`vip.temp.${userID}`;
|
||||||
|
|
||||||
export const videoLabelsKey = (videoID: VideoID, service: Service): string =>
|
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 {
|
export function videoLabelsHashKey(hashedVideoIDPrefix: VideoIDHash, service: Service): string {
|
||||||
hashedVideoIDPrefix = hashedVideoIDPrefix.substring(0, 3) as VideoIDHash;
|
hashedVideoIDPrefix = hashedVideoIDPrefix.substring(0, 3) as VideoIDHash;
|
||||||
|
|||||||
@@ -6,26 +6,27 @@ import { getHash } from "../../src/utils/getHash";
|
|||||||
describe("getVideoLabelHash", () => {
|
describe("getVideoLabelHash", () => {
|
||||||
const endpoint = "/api/videoLabels";
|
const endpoint = "/api/videoLabels";
|
||||||
before(async () => {
|
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)';
|
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]);
|
await db.prepare("run", query, ["getLabelHashSponsor" , getHash("getLabelHashSponsor", 1) , 2, 0, "labelhash01", "labeluser", 0, "sponsor", "full", 0, 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, ["getLabelHashSponsor" , getHash("getLabelHashSponsor", 1) , 2, 0, "labelhash012", "labeluser", 0, "sponsor", "skip", 0, 0, 2]);
|
||||||
await db.prepare("run", query, ["getLabelHashSelfpromo" , getHash("getLabelHashSelfpromo", 1) , 2, 0, "labelhash03", "labeluser", 0, "selfpromo", "full", 0, 0]);
|
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
|
// 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, "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]);
|
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]);
|
await db.prepare("run", query, ["getLabelHashPriority" , getHash("getLabelHashPriority", 1) , 2, 0, "labelhash06", "labeluser", 0, "selfpromo", "full", 0, 0, 0]);
|
||||||
// locked only
|
// 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, "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]);
|
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]);
|
await db.prepare("run", query, ["getLabelHashLocked" , getHash("getLabelHashLocked", 1) , 2, 1, "labelhash09", "labeluser", 0, "selfpromo", "full", 0, 0, 0]);
|
||||||
// hidden segments
|
// 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, ["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]);
|
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]);
|
await db.prepare("run", query, ["getLabelHashShHidden" , getHash("getLabelHashShHidden", 1) , 2, 0, "labelhash12", "labeluser", 0, "selfpromo", "full", 0, 1, 0]);
|
||||||
// priority override2
|
// 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, "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]);
|
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]);
|
await db.prepare("run", query, ["getLabelHashPriority2" , getHash("getLabelHashPriority2", 1) , 2, 0, "labelhash15", "labeluser", 0, "selfpromo", "full", 0, 0, 0]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
@@ -46,6 +47,7 @@ describe("getVideoLabelHash", () => {
|
|||||||
validateLabel(data, videoID);
|
validateLabel(data, videoID);
|
||||||
const result = data[0].segments[0];
|
const result = data[0].segments[0];
|
||||||
assert.strictEqual(result.category, "sponsor");
|
assert.strictEqual(result.category, "sponsor");
|
||||||
|
assert.strictEqual(data[0].hasStartSegment, true);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
@@ -60,6 +62,7 @@ describe("getVideoLabelHash", () => {
|
|||||||
validateLabel(data, videoID);
|
validateLabel(data, videoID);
|
||||||
const result = data[0].segments[0];
|
const result = data[0].segments[0];
|
||||||
assert.strictEqual(result.category, "exclusive_access");
|
assert.strictEqual(result.category, "exclusive_access");
|
||||||
|
assert.strictEqual(data[0].hasStartSegment, false);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(err => done(err));
|
.catch(err => done(err));
|
||||||
|
|||||||
Reference in New Issue
Block a user