DeArrow downvotes

This commit is contained in:
Ajay 2024-01-03 01:13:35 -05:00
parent 33dad0a5e4
commit b04e0dcd97
6 changed files with 389 additions and 56 deletions

View file

@ -0,0 +1,11 @@
BEGIN TRANSACTION;
ALTER TABLE "titleVotes" ADD "downvotes" INTEGER default 0;
ALTER TABLE "titleVotes" ADD "removed" INTEGER default 0;
ALTER TABLE "thumbnailVotes" ADD "downvotes" INTEGER default 0;
ALTER TABLE "thumbnailVotes" ADD "removed" INTEGER default 0;
UPDATE "config" SET value = 39 WHERE key = 'version';
COMMIT;

View file

@ -24,18 +24,18 @@ enum BrandingSubmissionType {
export async function getVideoBranding(res: Response, videoID: VideoID, service: Service, ip: IPAddress, returnUserID: boolean): Promise<BrandingResult> {
const getTitles = () => db.prepare(
"all",
`SELECT "titles"."title", "titles"."original", "titleVotes"."votes", "titleVotes"."locked", "titleVotes"."shadowHidden", "titles"."UUID", "titles"."videoID", "titles"."hashedVideoID", "titleVotes"."verification", "titles"."userID"
`SELECT "titles"."title", "titles"."original", "titleVotes"."votes", "titleVotes"."downvotes", "titleVotes"."locked", "titleVotes"."shadowHidden", "titles"."UUID", "titles"."videoID", "titles"."hashedVideoID", "titleVotes"."verification", "titles"."userID"
FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID"
WHERE "titles"."videoID" = ? AND "titles"."service" = ? AND "titleVotes"."votes" > -1`,
WHERE "titles"."videoID" = ? AND "titles"."service" = ? AND "titleVotes"."votes" > -1 AND "titleVotes"."votes" - "titleVotes"."downvotes" > -2 AND "titleVotes"."removed" = 0`,
[videoID, service],
{ useReplica: true }
) as Promise<TitleDBResult[]>;
const getThumbnails = () => db.prepare(
"all",
`SELECT "thumbnailTimestamps"."timestamp", "thumbnails"."original", "thumbnailVotes"."votes", "thumbnailVotes"."locked", "thumbnailVotes"."shadowHidden", "thumbnails"."UUID", "thumbnails"."videoID", "thumbnails"."hashedVideoID", "thumbnails"."userID"
`SELECT "thumbnailTimestamps"."timestamp", "thumbnails"."original", "thumbnailVotes"."votes", "thumbnailVotes"."downvotes", "thumbnailVotes"."locked", "thumbnailVotes"."shadowHidden", "thumbnails"."UUID", "thumbnails"."videoID", "thumbnails"."hashedVideoID", "thumbnails"."userID"
FROM "thumbnails" LEFT JOIN "thumbnailVotes" ON "thumbnails"."UUID" = "thumbnailVotes"."UUID" LEFT JOIN "thumbnailTimestamps" ON "thumbnails"."UUID" = "thumbnailTimestamps"."UUID"
WHERE "thumbnails"."videoID" = ? AND "thumbnails"."service" = ? AND "thumbnailVotes"."votes" > -2
WHERE "thumbnails"."videoID" = ? AND "thumbnails"."service" = ? AND "thumbnailVotes"."votes" - "thumbnailVotes"."downvotes" > -2 AND "thumbnailVotes"."removed" = 0
ORDER BY "thumbnails"."timeSubmitted" ASC`,
[videoID, service],
{ useReplica: true }
@ -89,18 +89,18 @@ export async function getVideoBranding(res: Response, videoID: VideoID, service:
export async function getVideoBrandingByHash(videoHashPrefix: VideoIDHash, service: Service, ip: IPAddress, returnUserID: boolean): Promise<Record<VideoID, BrandingResult>> {
const getTitles = () => db.prepare(
"all",
`SELECT "titles"."title", "titles"."original", "titleVotes"."votes", "titleVotes"."locked", "titleVotes"."shadowHidden", "titles"."UUID", "titles"."videoID", "titles"."hashedVideoID", "titleVotes"."verification"
`SELECT "titles"."title", "titles"."original", "titleVotes"."votes", "titleVotes"."downvotes", "titleVotes"."locked", "titleVotes"."shadowHidden", "titles"."UUID", "titles"."videoID", "titles"."hashedVideoID", "titleVotes"."verification"
FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID"
WHERE "titles"."hashedVideoID" LIKE ? AND "titles"."service" = ? AND "titleVotes"."votes" > -2`,
WHERE "titles"."hashedVideoID" LIKE ? AND "titles"."service" = ? AND "titleVotes"."votes" > -1 AND "titleVotes"."votes" - "titleVotes"."downvotes" > -2 AND "titleVotes"."removed" = 0`,
[`${videoHashPrefix}%`, service],
{ useReplica: true }
) as Promise<TitleDBResult[]>;
const getThumbnails = () => db.prepare(
"all",
`SELECT "thumbnailTimestamps"."timestamp", "thumbnails"."original", "thumbnailVotes"."votes", "thumbnailVotes"."locked", "thumbnailVotes"."shadowHidden", "thumbnails"."UUID", "thumbnails"."videoID", "thumbnails"."hashedVideoID"
`SELECT "thumbnailTimestamps"."timestamp", "thumbnails"."original", "thumbnailVotes"."votes", "thumbnailVotes"."downvotes", "thumbnailVotes"."locked", "thumbnailVotes"."shadowHidden", "thumbnails"."UUID", "thumbnails"."videoID", "thumbnails"."hashedVideoID"
FROM "thumbnails" LEFT JOIN "thumbnailVotes" ON "thumbnails"."UUID" = "thumbnailVotes"."UUID" LEFT JOIN "thumbnailTimestamps" ON "thumbnails"."UUID" = "thumbnailTimestamps"."UUID"
WHERE "thumbnails"."hashedVideoID" LIKE ? AND "thumbnails"."service" = ? AND "thumbnailVotes"."votes" > -2
WHERE "thumbnails"."hashedVideoID" LIKE ? AND "thumbnails"."service" = ? AND "thumbnailVotes"."votes" - "thumbnailVotes"."downvotes" > -2 AND "thumbnailVotes"."removed" = 0
ORDER BY "thumbnails"."timeSubmitted" ASC`,
[`${videoHashPrefix}%`, service],
{ useReplica: true }
@ -176,7 +176,7 @@ async function filterAndSortBranding(videoID: VideoID, returnUserID: boolean, db
.map((r) => ({
title: r.title,
original: r.original === 1,
votes: r.votes + r.verification,
votes: r.votes + r.verification - r.downvotes,
locked: r.locked === 1,
UUID: r.UUID,
userID: returnUserID ? r.userID : undefined
@ -191,7 +191,7 @@ async function filterAndSortBranding(videoID: VideoID, returnUserID: boolean, db
.map((r) => ({
timestamp: r.timestamp,
original: r.original === 1,
votes: r.votes,
votes: r.votes - r.downvotes,
locked: r.locked === 1,
UUID: r.UUID,
userID: returnUserID ? r.userID : undefined

View file

@ -24,6 +24,11 @@ enum BrandingType {
Thumbnail
}
enum BrandingVoteType {
Upvote = 1,
Downvote = 2
}
interface ExistingVote {
UUID: BrandingUUID;
type: number;
@ -31,7 +36,7 @@ interface ExistingVote {
}
export async function postBranding(req: Request, res: Response) {
const { videoID, userID, title, thumbnail, autoLock } = req.body as BrandingSubmission;
const { videoID, userID, title, thumbnail, autoLock, downvote } = req.body as BrandingSubmission;
const service = getService(req.body.service);
if (!videoID || !userID || userID.length < 30 || !service
@ -57,7 +62,7 @@ export async function postBranding(req: Request, res: Response) {
}
const now = Date.now();
const voteType = 1;
const voteType: BrandingVoteType = downvote ? BrandingVoteType.Downvote : BrandingVoteType.Upvote;
if (title && !isVip && title.title.length > config.maxTitleLength) {
lock.unlock();
@ -74,10 +79,14 @@ export async function postBranding(req: Request, res: Response) {
if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users
const UUID = existingUUID || crypto.randomUUID();
const existingVote = await handleExistingVotes(BrandingType.Title, videoID, hashedUserID, UUID, hashedIP, voteType);
await handleExistingVotes(BrandingType.Title, videoID, hashedUserID, UUID, hashedIP, voteType);
if (existingUUID) {
await updateVoteTotals(BrandingType.Title, existingVote, UUID, shouldLock);
await updateVoteTotals(BrandingType.Title, UUID, shouldLock, !!downvote);
} else {
if (downvote) {
throw new Error("Title submission doesn't exist");
}
await db.prepare("run", `INSERT INTO "titles" ("videoID", "title", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[videoID, title.title, title.original ? 1 : 0, hashedUserID, service, hashedVideoID, now, UUID]);
@ -111,10 +120,14 @@ export async function postBranding(req: Request, res: Response) {
if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users
const UUID = existingUUID || crypto.randomUUID();
const existingVote = await handleExistingVotes(BrandingType.Thumbnail, videoID, hashedUserID, UUID, hashedIP, voteType);
await handleExistingVotes(BrandingType.Thumbnail, videoID, hashedUserID, UUID, hashedIP, voteType);
if (existingUUID) {
await updateVoteTotals(BrandingType.Thumbnail, existingVote, UUID, shouldLock);
await updateVoteTotals(BrandingType.Thumbnail, UUID, shouldLock, !!downvote);
} else {
if (downvote) {
throw new Error("Thumbnail submission doesn't exist");
}
await db.prepare("run", `INSERT INTO "thumbnails" ("videoID", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?)`,
[videoID, thumbnail.original ? 1 : 0, hashedUserID, service, hashedVideoID, now, UUID]);
@ -152,38 +165,54 @@ export async function postBranding(req: Request, res: Response) {
* If no existing vote, it adds one.
*/
async function handleExistingVotes(type: BrandingType, videoID: VideoID,
hashedUserID: HashedUserID, UUID: BrandingUUID, hashedIP: HashedIP, voteType: number): Promise<ExistingVote> {
hashedUserID: HashedUserID, UUID: BrandingUUID, hashedIP: HashedIP, voteType: BrandingVoteType) {
const table = type === BrandingType.Title ? `"titleVotes"` : `"thumbnailVotes"`;
const existingVote = await privateDB.prepare("get", `SELECT "id", "UUID", "type" from ${table} where "videoID" = ? AND "userID" = ?`, [videoID, hashedUserID]);
if (existingVote && existingVote.UUID !== UUID) {
if (existingVote.type === 1) {
// Either votes of the same type, or on the same submission (undo a downvote)
const existingVotes = await privateDB.prepare("all", `SELECT "id", "UUID", "type" from ${table} where "videoID" = ? AND "userID" = ? AND ("type" = ? OR "UUID" = ?)`, [videoID, hashedUserID, voteType, UUID]) as ExistingVote[];
if (existingVotes.length > 0) {
// Only one upvote per video
if (voteType === BrandingVoteType.Upvote) {
for (const existingVote of existingVotes) {
switch (existingVote.type) {
case BrandingVoteType.Upvote:
await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]);
await privateDB.prepare("run", `UPDATE ${table} SET "type" = ?, "UUID" = ? WHERE "id" = ?`, [voteType, UUID, existingVote.id]);
break;
case BrandingVoteType.Downvote:
// Undoing a downvote now that it is being upvoted
await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" - 1 WHERE "UUID" = ?`, [existingVote.UUID]);
await privateDB.prepare("run", `DELETE FROM ${table} WHERE "id" = ?`, [existingVote.id]);
break;
}
}
}
await privateDB.prepare("run", `UPDATE ${table} SET "type" = ?, "UUID" = ? WHERE "id" = ?`, [voteType, UUID, existingVote.id]);
} else if (!existingVote) {
} else {
await privateDB.prepare("run", `INSERT INTO ${table} ("videoID", "UUID", "userID", "hashedIP", "type") VALUES (?, ?, ?, ?, ?)`,
[videoID, UUID, hashedUserID, hashedIP, voteType]);
}
return existingVote;
}
/**
* Only called if an existing vote exists.
* Will update public vote totals and locked status.
*/
async function updateVoteTotals(type: BrandingType, existingVote: ExistingVote, UUID: BrandingUUID, shouldLock: boolean): Promise<void> {
async function updateVoteTotals(type: BrandingType, UUID: BrandingUUID, shouldLock: boolean, downvote: boolean): Promise<void> {
const table = type === BrandingType.Title ? `"titleVotes"` : `"thumbnailVotes"`;
// Don't upvote if we vote on the same submission
if (!existingVote || existingVote.UUID !== UUID) {
if (downvote) {
await db.prepare("run", `UPDATE ${table} SET "downvotes" = "downvotes" + 1 WHERE "UUID" = ?`, [UUID]);
} else {
await db.prepare("run", `UPDATE ${table} SET "votes" = "votes" + 1 WHERE "UUID" = ?`, [UUID]);
}
if (shouldLock) {
await db.prepare("run", `UPDATE ${table} SET "locked" = 1 WHERE "UUID" = ?`, [UUID]);
if (downvote) {
await db.prepare("run", `UPDATE ${table} SET "removed" = 1 WHERE "UUID" = ?`, [UUID]);
} else {
await db.prepare("run", `UPDATE ${table} SET "locked" = 1, "removed" = 0 WHERE "UUID" = ?`, [UUID]);
}
}
}

View file

@ -17,6 +17,7 @@ export interface TitleDBResult extends BrandingDBSubmission {
title: string,
original: number,
votes: number,
downvotes: number,
locked: number,
verification: number,
userID: UserID
@ -35,6 +36,7 @@ export interface ThumbnailDBResult extends BrandingDBSubmission {
timestamp?: number,
original: number,
votes: number,
downvotes: number,
locked: number,
userID: UserID
}
@ -84,6 +86,7 @@ export interface BrandingSubmission {
userID: UserID;
service: Service;
autoLock: boolean | undefined;
downvote: boolean | undefined;
}
export interface BrandingSegmentDBResult {

View file

@ -36,29 +36,34 @@ describe("getBranding", () => {
before(async () => {
const titleQuery = `INSERT INTO "titles" ("videoID", "title", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
const titleVotesQuery = `INSERT INTO "titleVotes" ("UUID", "votes", "locked", "shadowHidden", "verification") VALUES (?, ?, ?, ?, ?)`;
const titleVotesQuery = `INSERT INTO "titleVotes" ("UUID", "votes", "locked", "shadowHidden", "verification", "downvotes", "removed") VALUES (?, ?, ?, ?, ?, ?, ?)`;
const thumbnailQuery = `INSERT INTO "thumbnails" ("videoID", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?)`;
const thumbnailTimestampsQuery = `INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)`;
const thumbnailVotesQuery = `INSERT INTO "thumbnailVotes" ("UUID", "votes", "locked", "shadowHidden") VALUES (?, ?, ?, ?)`;
const thumbnailVotesQuery = `INSERT INTO "thumbnailVotes" ("UUID", "votes", "locked", "shadowHidden", "downvotes", "removed") VALUES (?, ?, ?, ?, ?, ?)`;
await Promise.all([
db.prepare("run", titleQuery, [videoID1, "title1", 0, "userID1", Service.YouTube, videoID1Hash, 1, "UUID1"]),
db.prepare("run", titleQuery, [videoID1, "title2", 0, "userID2", Service.YouTube, videoID1Hash, 1, "UUID2"]),
db.prepare("run", titleQuery, [videoID1, "title3", 1, "userID3", Service.YouTube, videoID1Hash, 1, "UUID3"]),
db.prepare("run", titleQuery, [videoID1, "title4removed", 0, "userID4", Service.YouTube, videoID1Hash, 1, "UUID4"]),
db.prepare("run", thumbnailQuery, [videoID1, 0, "userID1", Service.YouTube, videoID1Hash, 1, "UUID1T"]),
db.prepare("run", thumbnailQuery, [videoID1, 1, "userID2", Service.YouTube, videoID1Hash, 1, "UUID2T"]),
db.prepare("run", thumbnailQuery, [videoID1, 0, "userID3", Service.YouTube, videoID1Hash, 1, "UUID3T"]),
db.prepare("run", thumbnailQuery, [videoID1, 0, "userID4", Service.YouTube, videoID1Hash, 1, "UUID4T"]),
]);
await Promise.all([
db.prepare("run", titleVotesQuery, ["UUID1", 3, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID2", 2, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID3", 1, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID1", 3, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID2", 3, 0, 0, 0, 1, 0]),
db.prepare("run", titleVotesQuery, ["UUID3", 1, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID4", 5, 0, 0, 0, 0, 1]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID1T", 1]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID3T", 3]),
db.prepare("run", thumbnailVotesQuery, ["UUID1T", 3, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID2T", 2, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID3T", 1, 0, 0])
db.prepare("run", thumbnailTimestampsQuery, ["UUID4T", 18]),
db.prepare("run", thumbnailVotesQuery, ["UUID1T", 3, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID2T", 3, 0, 0, 1, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID3T", 1, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID4T", 5, 0, 0, 0, 1])
]);
await Promise.all([
@ -71,15 +76,15 @@ describe("getBranding", () => {
]);
await Promise.all([
db.prepare("run", titleVotesQuery, ["UUID11", 3, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID21", 2, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID31", 1, 1, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID11", 3, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID21", 2, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID31", 1, 1, 0, 0, 0, 0]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID11T", 1]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID31T", 3]),
db.prepare("run", thumbnailVotesQuery, ["UUID11T", 3, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID21T", 2, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID31T", 1, 1, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID11T", 3, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID21T", 2, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID31T", 1, 1, 0, 0, 0]),
]);
await Promise.all([
@ -92,14 +97,14 @@ describe("getBranding", () => {
]);
await Promise.all([
db.prepare("run", titleVotesQuery, ["UUID12", 3, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID22", 2, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID32", 1, 0, 1, 0]),
db.prepare("run", titleVotesQuery, ["UUID12", 3, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID22", 2, 0, 0, 0, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID32", 1, 0, 1, 0, 0, 0]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID12T", 1]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID32T", 3]),
db.prepare("run", thumbnailVotesQuery, ["UUID12T", 3, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID22T", 2, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID32T", 1, 0, 1])
db.prepare("run", thumbnailVotesQuery, ["UUID12T", 3, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID22T", 2, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID32T", 1, 0, 1, 0, 0])
]);
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
@ -116,14 +121,14 @@ describe("getBranding", () => {
]);
await Promise.all([
db.prepare("run", titleVotesQuery, ["UUID-uv-1", 3, 0, 0, -1]),
db.prepare("run", titleVotesQuery, ["UUID-uv-2", 2, 0, 0, -1]),
db.prepare("run", titleVotesQuery, ["UUID-uv-3", 0, 0, 0, -1]),
db.prepare("run", titleVotesQuery, ["UUID-uv-1", 3, 0, 0, -1, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID-uv-2", 2, 0, 0, -1, 0, 0]),
db.prepare("run", titleVotesQuery, ["UUID-uv-3", 0, 0, 0, -1, 0, 0]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID-uv-1T", 1]),
db.prepare("run", thumbnailTimestampsQuery, ["UUID-uv-3T", 3]),
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-1T", 3, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-2T", 2, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-3T", 1, 0, 0])
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-1T", 3, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-2T", 2, 0, 0, 0, 0]),
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-3T", 1, 0, 0, 0, 0])
]);
});

View file

@ -54,6 +54,21 @@ describe("postBranding", () => {
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandLocked1", 0, 1, 0]);
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandLocked2", 0, 1, 0]);
// Testing vip submission removal
await db.prepare("run", insertTitleQuery, ["postBrandRemoved1", "Some title", 0, getHash(userID1), Service.YouTube, getHash("postBrandRemoved1"), Date.now(), "postBrandRemoved1"]);
await db.prepare("run", insertTitleVotesQuery, ["postBrandRemoved1", 0, 1, 0, 0]);
await db.prepare("run", insertTitleQuery, ["postBrandRemoved1", "Some other title", 0, getHash(userID1), Service.YouTube, getHash("postBrandRemoved1"), Date.now(), "postBrandRemoved2"]);
await db.prepare("run", insertTitleVotesQuery, ["postBrandRemoved2", 0, 1, 0, 0]);
// Testing vip submission removal
const insertThumbnailTimestampQuery = 'INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)';
await db.prepare("run", insertThumbnailQuery, ["postBrandRemoved1", 0, getHash(userID3), Service.YouTube, getHash("postBrandRemoved1"), Date.now(), "postBrandRemoved1"]);
await db.prepare("run", insertThumbnailTimestampQuery, ["postBrandRemoved1", 12.34]);
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandRemoved1", 0, 1, 0]);
await db.prepare("run", insertThumbnailQuery, ["postBrandRemoved1", 0, getHash(userID3), Service.YouTube, getHash("postBrandRemoved1"), Date.now(), "postBrandRemoved2"]);
await db.prepare("run", insertThumbnailTimestampQuery, ["postBrandRemoved2", 13.34]);
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandRemoved2", 0, 1, 0]);
// Verified through title submissions
await db.prepare("run", insertTitleQuery, ["postBrandVerified1", "Some title", 0, getHash(userID7), Service.YouTube, getHash("postBrandVerified1"), Date.now(), "postBrandVerified1"]);
await db.prepare("run", insertTitleQuery, ["postBrandVerified2", "Some title", 1, getHash(userID7), Service.YouTube, getHash("postBrandVerified2"), Date.now(), "postBrandVerified2"]);
@ -75,7 +90,6 @@ describe("postBranding", () => {
await db.prepare("run", insertThumbnailQuery, ["postBrandBannedOriginalVote", 1, getHash(userID1), Service.YouTube, getHash("postBrandBannedOriginalVote"), Date.now(), "postBrandBannedOriginalVote"]);
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandBannedCustomVote", 0, 0, 0]);
await db.prepare("run", insertThumbnailVotesQuery, ["postBrandBannedOriginalVote", 0, 0, 0]);
const insertThumbnailTimestampQuery = 'INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)';
await db.prepare("run", insertThumbnailTimestampQuery, ["postBrandBannedCustomVote", 12.34]);
});
@ -127,6 +141,7 @@ describe("postBranding", () => {
assert.strictEqual(dbTitle.original, title.original ? 1 : 0);
assert.strictEqual(dbVotes.votes, 0);
assert.strictEqual(dbVotes.downvotes, 0);
assert.strictEqual(dbVotes.locked, 0);
assert.strictEqual(dbVotes.shadowHidden, 0);
});
@ -223,6 +238,177 @@ describe("postBranding", () => {
assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);
});
it("Submit another title and thumbnail", async () => {
const videoID = "postBrand5";
const title = {
title: "Some other title",
original: false
};
const thumbnail = {
timestamp: 13.42,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: userID4,
service: Service.YouTube,
videoID
});
assert.strictEqual(res.status, 200);
const dbTitle = await queryTitleByVideo(videoID);
const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID);
const dbThumbnail = await queryThumbnailByVideo(videoID);
const dbThumbnailTimestamps = await queryThumbnailTimestampsByUUID(dbThumbnail.UUID);
const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID);
assert.strictEqual(dbTitle.title, title.title);
assert.strictEqual(dbTitle.original, title.original ? 1 : 0);
assert.strictEqual(dbTitleVotes.votes, 0);
assert.strictEqual(dbTitleVotes.locked, 0);
assert.strictEqual(dbTitleVotes.shadowHidden, 0);
assert.strictEqual(dbThumbnailTimestamps.timestamp, thumbnail.timestamp);
assert.strictEqual(dbThumbnail.original, thumbnail.original ? 1 : 0);
assert.strictEqual(dbThumbnailVotes.votes, 0);
assert.strictEqual(dbThumbnailVotes.locked, 0);
assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);
});
it("Downvote title and thumbnail", async () => {
const videoID = "postBrand5";
const title = {
title: "Some other title",
original: false
};
const thumbnail = {
timestamp: 13.42,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: userID6,
service: Service.YouTube,
videoID,
downvote: true
});
assert.strictEqual(res.status, 200);
const dbTitles = await queryTitleByVideo(videoID, true);
for (const dbTitle of dbTitles) {
if (dbTitle.title === title.title) {
const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID);
assert.strictEqual(dbTitleVotes.votes, 0);
assert.strictEqual(dbTitleVotes.downvotes, 1);
assert.strictEqual(dbTitleVotes.locked, 0);
assert.strictEqual(dbTitleVotes.shadowHidden, 0);
}
}
const dbThumbnails = await queryThumbnailByVideo(videoID, true);
for (const dbThumbnail of dbThumbnails) {
if (dbThumbnail.timestamp === thumbnail.timestamp) {
const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID);
assert.strictEqual(dbThumbnailVotes.votes, 0);
assert.strictEqual(dbThumbnailVotes.downvotes, 1);
assert.strictEqual(dbThumbnailVotes.locked, 0);
assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);
}
}
});
it("Downvote another title and thumbnail", async () => {
const videoID = "postBrand5";
const title = {
title: "Some title",
original: false
};
const thumbnail = {
timestamp: 12.42,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: userID6,
service: Service.YouTube,
videoID,
downvote: true
});
assert.strictEqual(res.status, 200);
const dbTitles = await queryTitleByVideo(videoID, true);
for (const dbTitle of dbTitles) {
const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID);
assert.strictEqual(dbTitleVotes.votes, 0);
assert.strictEqual(dbTitleVotes.downvotes, 1);
assert.strictEqual(dbTitleVotes.locked, 0);
assert.strictEqual(dbTitleVotes.shadowHidden, 0);
}
const dbThumbnails = await queryThumbnailByVideo(videoID, true);
for (const dbThumbnail of dbThumbnails) {
const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID);
assert.strictEqual(dbThumbnailVotes.votes, 0);
assert.strictEqual(dbThumbnailVotes.downvotes, 1);
assert.strictEqual(dbThumbnailVotes.locked, 0);
assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);
}
});
it("Upvote after downvoting title and thumbnail", async () => {
const videoID = "postBrand5";
const title = {
title: "Some other title",
original: false
};
const thumbnail = {
timestamp: 13.42,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: userID6,
service: Service.YouTube,
videoID
});
assert.strictEqual(res.status, 200);
const dbTitles = await queryTitleByVideo(videoID, true);
for (const dbTitle of dbTitles) {
if (dbTitle.title === title.title) {
const dbTitleVotes = await queryTitleVotesByUUID(dbTitle.UUID);
assert.strictEqual(dbTitleVotes.votes, 1);
assert.strictEqual(dbTitleVotes.downvotes, 0);
assert.strictEqual(dbTitleVotes.locked, 0);
assert.strictEqual(dbTitleVotes.shadowHidden, 0);
}
}
const dbThumbnails = await queryThumbnailByVideo(videoID, true);
for (const dbThumbnail of dbThumbnails) {
if (dbThumbnail.timestamp === thumbnail.timestamp) {
const dbThumbnailVotes = await queryThumbnailVotesByUUID(dbThumbnail.UUID);
assert.strictEqual(dbThumbnailVotes.votes, 1);
assert.strictEqual(dbThumbnailVotes.downvotes, 0);
assert.strictEqual(dbThumbnailVotes.locked, 0);
assert.strictEqual(dbThumbnailVotes.shadowHidden, 0);
}
}
});
it("Submit title and thumbnail as VIP", async () => {
const videoID = "postBrand6";
const title = {
@ -350,6 +536,104 @@ describe("postBranding", () => {
assert.strictEqual(dbThumbnailVotes.locked, 0);
});
it("Downvote title and thumbnail as VIP", async () => {
const videoID = "postBrandRemoved1";
const title = {
title: "Some title",
original: false
};
const thumbnail = {
timestamp: 12.34,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: vipUser,
service: Service.YouTube,
videoID,
downvote: true
});
assert.strictEqual(res.status, 200);
const otherSegmentTitleVotes1 = await queryTitleVotesByUUID("postBrandRemoved1");
const otherSegmentTitleVotes2 = await queryTitleVotesByUUID("postBrandRemoved2");
const otherSegmentThumbnailVotes1 = await queryThumbnailVotesByUUID("postBrandRemoved1");
const otherSegmentThumbnailVotes2 = await queryThumbnailVotesByUUID("postBrandRemoved2");
assert.strictEqual(otherSegmentTitleVotes1.removed, 1);
assert.strictEqual(otherSegmentTitleVotes2.removed, 0);
assert.strictEqual(otherSegmentThumbnailVotes1.removed, 1);
assert.strictEqual(otherSegmentThumbnailVotes2.removed, 0);
});
it("Downvote another title and thumbnail as VIP", async () => {
const videoID = "postBrandRemoved1";
const title = {
title: "Some other title",
original: false
};
const thumbnail = {
timestamp: 13.34,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: vipUser,
service: Service.YouTube,
videoID,
downvote: true
});
assert.strictEqual(res.status, 200);
const otherSegmentTitleVotes1 = await queryTitleVotesByUUID("postBrandRemoved1");
const otherSegmentTitleVotes2 = await queryTitleVotesByUUID("postBrandRemoved2");
const otherSegmentThumbnailVotes1 = await queryThumbnailVotesByUUID("postBrandRemoved1");
const otherSegmentThumbnailVotes2 = await queryThumbnailVotesByUUID("postBrandRemoved2");
assert.strictEqual(otherSegmentTitleVotes1.removed, 1);
assert.strictEqual(otherSegmentTitleVotes2.removed, 1);
assert.strictEqual(otherSegmentThumbnailVotes1.removed, 1);
assert.strictEqual(otherSegmentThumbnailVotes2.removed, 1);
});
it("Remove downvote on title and thumbnail as VIP", async () => {
const videoID = "postBrandRemoved1";
const title = {
title: "Some title",
original: false
};
const thumbnail = {
timestamp: 12.34,
original: false
};
const res = await postBranding({
title,
thumbnail,
userID: vipUser,
service: Service.YouTube,
videoID
});
assert.strictEqual(res.status, 200);
const otherSegmentTitleVotes1 = await queryTitleVotesByUUID("postBrandRemoved1");
const otherSegmentTitleVotes2 = await queryTitleVotesByUUID("postBrandRemoved2");
const otherSegmentThumbnailVotes1 = await queryThumbnailVotesByUUID("postBrandRemoved1");
const otherSegmentThumbnailVotes2 = await queryThumbnailVotesByUUID("postBrandRemoved2");
assert.strictEqual(otherSegmentTitleVotes1.removed, 0);
assert.strictEqual(otherSegmentTitleVotes2.removed, 1);
assert.strictEqual(otherSegmentThumbnailVotes1.removed, 0);
assert.strictEqual(otherSegmentThumbnailVotes2.removed, 1);
});
it("Vote the same title again", async () => {
const videoID = "postBrand1";
const title = {
@ -516,6 +800,7 @@ describe("postBranding", () => {
assert.strictEqual(dbTitle.original, title.original ? 1 : 0);
assert.strictEqual(dbVotes.votes, 1);
assert.strictEqual(dbVotes.downvotes, 0);
assert.strictEqual(dbVotes.locked, 0);
assert.strictEqual(dbVotes.shadowHidden, 0);
});