2 Commits

Author SHA1 Message Date
Ajay
6d5b6dd3ae Add service test case for get skip segments 2025-10-14 03:46:27 -04:00
Ajay
0412386870 Add support for spotify service 2025-10-14 03:34:02 -04:00
5 changed files with 104 additions and 42 deletions

View File

@@ -1,16 +1,18 @@
import { db } from "../databases/databases";
import { Request, Response } from "express";
import { getService } from "../utils/getService";
export async function getSegmentID(req: Request, res: Response): Promise<Response> {
const partialUUID = req.query?.UUID;
const videoID = req.query?.videoID;
const service = getService(req.query?.service as string);
if (!partialUUID || !videoID) {
//invalid request
return res.sendStatus(400);
}
const data = await db.prepare("get", `SELECT "UUID" from "sponsorTimes" WHERE "UUID" LIKE ? AND "videoID" = ?`, [`${partialUUID}%`, videoID]);
const data = await db.prepare("get", `SELECT "UUID" from "sponsorTimes" WHERE "UUID" LIKE ? AND "videoID" = ? AND "service" = ?`, [`${partialUUID}%`, videoID, service]);
if (data) {
return res.status(200).send(data.UUID);

View File

@@ -141,8 +141,8 @@ async function autoModerateSubmission(apiVideoDetails: videoDetails,
.map(segment => [parseFloat(segment.segment[0]), parseFloat(segment.segment[1])]);
// add previous submissions by this user
const allSubmittedByUser = await db.prepare("all", `SELECT "startTime", "endTime" FROM "sponsorTimes" WHERE "userID" = ? AND "videoID" = ? AND "votes" > -1 AND "actionType" != 'chapter' AND "hidden" = 0`
, [submission.userID, submission.videoID]) as { startTime: string, endTime: string }[];
const allSubmittedByUser = await db.prepare("all", `SELECT "startTime", "endTime" FROM "sponsorTimes" WHERE "userID" = ? AND "videoID" = ? AND "service" = ? AND "votes" > -1 AND "actionType" != 'chapter' AND "hidden" = 0`
, [submission.userID, submission.videoID, submission.service]) as { startTime: string, endTime: string }[];
if (allSubmittedByUser) {
//add segments the user has previously submitted
@@ -195,13 +195,18 @@ async function checkInvalidFields(videoID: VideoID, userID: UserID, hashedUserID
if (typeof videoID !== "string" || videoID?.length == 0) {
invalidFields.push("videoID");
}
if (service === Service.YouTube && config.mode !== "test") {
if (service === Service.YouTube) {
if (config.mode !== "test") {
const sanitizedVideoID = youtubeID.validate(videoID) ? videoID : youtubeID.sanitize(videoID);
if (!youtubeID.validate(sanitizedVideoID)) {
invalidFields.push("videoID");
errors.push("YouTube videoID could not be extracted");
}
}
} else if (service !== Service.Spotify) {
invalidFields.push("service");
errors.push("Service is not supported");
}
const minLength = config.minUserIDLength;
if (typeof userID !== "string" || userID?.length < minLength) {
invalidFields.push("userID");
@@ -360,6 +365,15 @@ async function checkByAutoModerator(videoID: VideoID, userID: HashedUserID, segm
async function updateDataIfVideoDurationChange(videoID: VideoID, service: Service, videoDuration: VideoDuration, videoDurationParam: VideoDuration) {
let lockedCategoryList = await db.prepare("all", 'SELECT category, "actionType", reason from "lockCategories" where "videoID" = ? AND "service" = ?', [videoID, service]);
if (service === Service.Spotify) {
// Don't handle changed durations
return {
videoDuration,
apiVideoDetails: null,
lockedCategoryList
};
}
const previousSubmissions = await db.prepare("all",
`SELECT "videoDuration", "UUID"
FROM "sponsorTimes"
@@ -617,10 +631,12 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
//add to private db as well
await privateDB.prepare("run", `INSERT INTO "sponsorTimes" VALUES(?, ?, ?, ?)`, [videoID, hashedIP, timeSubmitted, service]);
if (service === Service.YouTube) {
await db.prepare("run", `INSERT INTO "videoInfo" ("videoID", "channelID", "title", "published")
SELECT ?, ?, ?, ?
WHERE NOT EXISTS (SELECT 1 FROM "videoInfo" WHERE "videoID" = ?)`, [
videoID, apiVideoDetails?.authorId || "", apiVideoDetails?.title || "", apiVideoDetails?.published || 0, videoID]);
}
// Clear redis cache for this video
QueryCacher.clearSegmentCache({

View File

@@ -22,7 +22,8 @@ export enum ActionType {
// Uncomment as needed
export enum Service {
YouTube = "YouTube",
PeerTube = "PeerTube",
Spotify = "Spotify",
PeerTube = "PeerTube"
// Twitch = 'Twitch',
// Nebula = 'Nebula',
// RSS = 'RSS',

View File

@@ -30,6 +30,7 @@ describe("getSkipSegments", () => {
await db.prepare("run", query, ["chapterVid", 71, 75, 2, 0, "chapterVid-3", "testman", 0, 50, "chapter", "chapter", "YouTube", 0, 0, 0, "Chapter 3"]);
await db.prepare("run", query, ["requiredSegmentHashVid", 10, 20, -2, 0, "1d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
await db.prepare("run", query, ["requiredSegmentHashVid", 20, 30, -2, 0, "1ebde8e8ae03096b6c866aa2c8cc7ee1d720ca1fca27bea3f39a6a1b876577e71", "testman", 0, 50, "sponsor", "skip", "YouTube", 0, 0, 0, ""]);
await db.prepare("run", query, ["getSkipSegmentID0", 1, 12, 1, 0, "uuid01-spot", "testman", 0, 50, "sponsor", "skip", "Spotify", 100, 0, 0, ""]);
return;
});
@@ -495,4 +496,22 @@ describe("getSkipSegments", () => {
})
.catch(err => done(err));
});
it("Should be able to get a time by category for spotify service", (done) => {
client.get(endpoint, { params: { videoID: "getSkipSegmentID0", category: "sponsor", service: "spotify" } })
.then(res => {
assert.strictEqual(res.status, 200);
const data = res.data;
assert.strictEqual(data.length, 1);
assert.strictEqual(data[0].segment[0], 1);
assert.strictEqual(data[0].segment[1], 12);
assert.strictEqual(data[0].category, "sponsor");
assert.strictEqual(data[0].UUID, "uuid01-spot");
assert.strictEqual(data[0].votes, 1);
assert.strictEqual(data[0].locked, 0);
assert.strictEqual(data[0].videoDuration, 100);
done();
})
.catch(err => done(err));
});
});

View File

@@ -46,8 +46,8 @@ describe("postSkipSegments", () => {
const submitVIPuser = `VIPPostSkipUser${".".repeat(16)}`;
const queryDatabase = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "votes", "userID", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
const queryDatabaseActionType = (videoID: string) => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
const queryDatabase = (videoID: string, service = "YouTube") => db.prepare("get", `SELECT "startTime", "endTime", "votes", "userID", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ? AND "service" = ?`, [videoID, service]);
const queryDatabaseActionType = (videoID: string, service = "YouTube") => db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "actionType" FROM "sponsorTimes" WHERE "videoID" = ? AND "service" = ?`, [videoID, service]);
const queryDatabaseVideoInfo = (videoID: string) => db.prepare("get", `SELECT * FROM "videoInfo" WHERE "videoID" = ?`, [videoID]);
before(async () => {
@@ -142,33 +142,6 @@ describe("postSkipSegments", () => {
.catch(err => done(err));
});
it("Should be able to submit a single time under a different service (JSON method)", (done) => {
const videoID = "postSkip7";
postSkipSegmentJSON({
userID: submitUserOne,
videoID,
service: "PeerTube",
segments: [{
segment: [0, 10],
category: "sponsor",
}],
})
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await db.prepare("get", `SELECT "startTime", "endTime", "locked", "category", "service" FROM "sponsorTimes" WHERE "videoID" = ?`, [videoID]);
const expected = {
startTime: 0,
endTime: 10,
locked: 0,
category: "sponsor",
service: "PeerTube",
};
assert.ok(partialDeepEquals(row, expected));
done();
})
.catch(err => done(err));
});
it("VIP submission should start locked", (done) => {
const videoID = "vipuserIDSubmission";
postSkipSegmentJSON({
@@ -374,4 +347,55 @@ describe("postSkipSegments", () => {
})
.catch(err => done(err));
});
it("Should be able to submit for spotify service", (done) => {
const videoID = "postSkipParamSingle";
postSkipSegmentParam({
videoID,
startTime: 23,
endTime: 105,
userID: submitUserOne,
category: "sponsor",
service: "Spotify"
})
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await queryDatabase(videoID, "Spotify");
const expected = {
startTime: 23,
endTime: 105,
category: "sponsor",
};
assert.ok(partialDeepEquals(row, expected));
done();
})
.catch(err => done(err));
});
it("Should be able to submit a time for spotify service (JSON method)", (done) => {
const videoID = "postSkipJSONSingle";
postSkipSegmentJSON({
userID: submitUserOne,
videoID,
segments: [{
segment: [22, 103],
category: "sponsor",
}],
service: "Spotify"
})
.then(async res => {
assert.strictEqual(res.status, 200);
const row = await queryDatabase(videoID, "Spotify");
const expected = {
startTime: 22,
endTime: 103,
locked: 0,
category: "sponsor",
};
assert.ok(partialDeepEquals(row, expected));
done();
})
.catch(err => done(err));
});
});