mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-14 07:27:01 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlockServer
This commit is contained in:
@@ -1,25 +1,25 @@
|
||||
import {config} from '../config';
|
||||
import {Logger} from '../utils/logger';
|
||||
import {db, privateDB} from '../databases/databases';
|
||||
import {getMaxResThumbnail, YouTubeAPI} from '../utils/youtubeApi';
|
||||
import {getSubmissionUUID} from '../utils/getSubmissionUUID';
|
||||
import fetch from 'node-fetch';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {getIP} from '../utils/getIP';
|
||||
import {getFormattedTime} from '../utils/getFormattedTime';
|
||||
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
||||
import {dispatchEvent} from '../utils/webhookUtils';
|
||||
import {Request, Response} from 'express';
|
||||
import { ActionType, Category, CategoryActionType, IncomingSegment, 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';
|
||||
import { UserID } from '../types/user.model';
|
||||
import {config} from "../config";
|
||||
import {Logger} from "../utils/logger";
|
||||
import {db, privateDB} from "../databases/databases";
|
||||
import {getMaxResThumbnail, YouTubeAPI} from "../utils/youtubeApi";
|
||||
import {getSubmissionUUID} from "../utils/getSubmissionUUID";
|
||||
import fetch from "node-fetch";
|
||||
import {getHash} from "../utils/getHash";
|
||||
import {getIP} from "../utils/getIP";
|
||||
import {getFormattedTime} from "../utils/getFormattedTime";
|
||||
import {isUserTrustworthy} from "../utils/isUserTrustworthy";
|
||||
import {dispatchEvent} from "../utils/webhookUtils";
|
||||
import {Request, Response} from "express";
|
||||
import { ActionType, Category, CategoryActionType, IncomingSegment, 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";
|
||||
import { UserID } from "../types/user.model";
|
||||
|
||||
async function sendWebhookNotification(userID: string, videoID: string, UUID: string, submissionCount: number, youtubeData: APIVideoData, {submissionStart, submissionEnd}: { submissionStart: number; submissionEnd: number; }, segmentInfo: any) {
|
||||
const row = await db.prepare('get', `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
const row = await db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
const userName = row !== undefined ? row.userName : null;
|
||||
|
||||
let scopeName = "submissions.other";
|
||||
@@ -32,7 +32,7 @@ async function sendWebhookNotification(userID: string, videoID: string, UUID: st
|
||||
"id": videoID,
|
||||
"title": youtubeData?.title,
|
||||
"thumbnail": getMaxResThumbnail(youtubeData) || null,
|
||||
"url": "https://www.youtube.com/watch?v=" + videoID,
|
||||
"url": `https://www.youtube.com/watch?v=${videoID}`,
|
||||
},
|
||||
"submission": {
|
||||
"UUID": UUID,
|
||||
@@ -49,7 +49,7 @@ async function sendWebhookNotification(userID: string, videoID: string, UUID: st
|
||||
|
||||
async function sendWebhooks(apiVideoInfo: APIVideoInfo, userID: string, videoID: string, UUID: string, segmentInfo: any, service: Service) {
|
||||
if (apiVideoInfo && service == Service.YouTube) {
|
||||
const userSubmissionCountRow = await db.prepare('get', `SELECT count(*) as "submissionCount" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]);
|
||||
const userSubmissionCountRow = await db.prepare("get", `SELECT count(*) as "submissionCount" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]);
|
||||
|
||||
const {data, err} = apiVideoInfo;
|
||||
if (err) return;
|
||||
@@ -64,17 +64,17 @@ async function sendWebhooks(apiVideoInfo: APIVideoInfo, userID: string, videoID:
|
||||
// If it is a first time submission
|
||||
// Then send a notification to discord
|
||||
if (config.discordFirstTimeSubmissionsWebhookURL === null || userSubmissionCountRow.submissionCount > 1) return;
|
||||
|
||||
|
||||
fetch(config.discordFirstTimeSubmissionsWebhookURL, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"embeds": [{
|
||||
"title": data?.title,
|
||||
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (parseInt(startTime.toFixed(0)) - 2),
|
||||
"description": "Submission ID: " + UUID +
|
||||
"\n\nTimestamp: " +
|
||||
getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n\nCategory: " + segmentInfo.category,
|
||||
"url": `https://www.youtube.com/watch?v=${videoID}&t=${(parseInt(startTime.toFixed(0)) - 2)}`,
|
||||
"description": `Submission ID: ${UUID}\
|
||||
\n\nTimestamp: \
|
||||
${getFormattedTime(startTime)} to ${getFormattedTime(endTime)}\
|
||||
\n\nCategory: ${segmentInfo.category}`,
|
||||
"color": 10813440,
|
||||
"author": {
|
||||
"name": userID,
|
||||
@@ -85,55 +85,55 @@ async function sendWebhooks(apiVideoInfo: APIVideoInfo, userID: string, videoID:
|
||||
}],
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (res.status >= 400) {
|
||||
Logger.error("Error sending first time submission Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
.then(res => {
|
||||
if (res.status >= 400) {
|
||||
Logger.error("Error sending first time submission Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
Logger.error("\n");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Logger.error("Failed to send first time submission Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Logger.error("Failed to send first time submission Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function sendWebhooksNB(userID: string, videoID: string, UUID: string, startTime: number, endTime: number, category: string, probability: number, ytData: any) {
|
||||
const submissionInfoRow = await db.prepare('get', `SELECT
|
||||
const submissionInfoRow = await db.prepare("get", `SELECT
|
||||
(select count(1) from "sponsorTimes" where "userID" = ?) count,
|
||||
(select count(1) from "sponsorTimes" where "userID" = ? and "votes" <= -2) disregarded,
|
||||
coalesce((select "userName" FROM "userNames" WHERE "userID" = ?), ?) "userName"`,
|
||||
[userID, userID, userID, userID]);
|
||||
[userID, userID, userID, userID]);
|
||||
|
||||
let submittedBy: string;
|
||||
// If a userName was created then show both
|
||||
if (submissionInfoRow.userName !== userID) {
|
||||
submittedBy = submissionInfoRow.userName + "\n " + userID;
|
||||
submittedBy = `${submissionInfoRow.userName}\n${userID}`;
|
||||
} else {
|
||||
submittedBy = userID;
|
||||
}
|
||||
|
||||
// Send discord message
|
||||
if (config.discordNeuralBlockRejectWebhookURL === null) return;
|
||||
|
||||
|
||||
fetch(config.discordNeuralBlockRejectWebhookURL, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"embeds": [{
|
||||
"title": ytData.items[0].snippet.title,
|
||||
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (parseFloat(startTime.toFixed(0)) - 2),
|
||||
"description": "**Submission ID:** " + UUID +
|
||||
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n**Predicted Probability:** " + probability +
|
||||
"\n**Category:** " + category +
|
||||
"\n**Submitted by:** " + submittedBy +
|
||||
"\n**Total User Submissions:** " + submissionInfoRow.count +
|
||||
"\n**Ignored User Submissions:** " + submissionInfoRow.disregarded,
|
||||
"url": `https://www.youtube.com/watch?v=${videoID}&t=${(parseFloat(startTime.toFixed(0)) - 2)}`,
|
||||
"description": `**Submission ID:** ${UUID}\
|
||||
\n**Timestamp:** ${getFormattedTime(startTime)} to ${getFormattedTime(endTime)}\
|
||||
\n**Predicted Probability:** ${probability}\
|
||||
\n**Category:** ${category}\
|
||||
\n**Submitted by:** ${submittedBy}\
|
||||
\n**Total User Submissions:** ${submissionInfoRow.count}\
|
||||
\n**Ignored User Submissions:** ${submissionInfoRow.disregarded}`,
|
||||
"color": 10813440,
|
||||
"thumbnail": {
|
||||
"url": ytData.items[0].snippet.thumbnails.maxres ? ytData.items[0].snippet.thumbnails.maxres.url : "",
|
||||
@@ -141,21 +141,21 @@ async function sendWebhooksNB(userID: string, videoID: string, UUID: string, sta
|
||||
}],
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (res.status >= 400) {
|
||||
Logger.error("Error sending NeuralBlock Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
.then(res => {
|
||||
if (res.status >= 400) {
|
||||
Logger.error("Error sending NeuralBlock Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
Logger.error("\n");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Logger.error("Failed to send NeuralBlock Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Logger.error("Failed to send NeuralBlock Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// callback: function(reject: "String containing reason the submission was rejected")
|
||||
@@ -164,8 +164,8 @@ async function sendWebhooksNB(userID: string, videoID: string, UUID: string, sta
|
||||
// Looks like this was broken for no defined youtube key - fixed but IMO we shouldn't return
|
||||
// false for a pass - it was confusing and lead to this bug - any use of this function in
|
||||
// the future could have the same problem.
|
||||
async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
submission: { videoID: VideoID; userID: UserID; segments: IncomingSegment[] }) {
|
||||
async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
submission: { videoID: VideoID; userID: UserID; segments: IncomingSegment[] }) {
|
||||
if (apiVideoInfo) {
|
||||
const {err, data} = apiVideoInfo;
|
||||
if (err) return false;
|
||||
@@ -180,13 +180,13 @@ async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
} else {
|
||||
if (segments[i].category === "sponsor") {
|
||||
//Prepare timestamps to send to NB all at once
|
||||
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
||||
nbString = `${nbString}${segments[i].segment[0]},${segments[i].segment[1]};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all submissions for this user
|
||||
const allSubmittedByUser = await db.prepare('all', `SELECT "startTime", "endTime" FROM "sponsorTimes" WHERE "userID" = ? and "videoID" = ? and "votes" > -1`, [submission.userID, submission.videoID]);
|
||||
const allSubmittedByUser = await db.prepare("all", `SELECT "startTime", "endTime" FROM "sponsorTimes" WHERE "userID" = ? and "videoID" = ? and "votes" > -1`, [submission.userID, submission.videoID]);
|
||||
const allSegmentTimes = [];
|
||||
if (allSubmittedByUser !== undefined) {
|
||||
//add segments the user has previously submitted
|
||||
@@ -221,8 +221,8 @@ async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
// Check NeuralBlock
|
||||
const neuralBlockURL = config.neuralBlockURL;
|
||||
if (!neuralBlockURL) return false;
|
||||
const response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
||||
"&segments=" + nbString.substring(0, nbString.length - 1));
|
||||
const response = await fetch(`${neuralBlockURL}/api/checkSponsorSegments?vid=${submission.videoID}
|
||||
&segments=${nbString.substring(0, nbString.length - 1)}`);
|
||||
if (!response.ok) return false;
|
||||
|
||||
const nbPredictions = await response.json();
|
||||
@@ -270,7 +270,7 @@ async function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promi
|
||||
async function checkUserActiveWarning(userID: string): Promise<{ pass: boolean; errorMessage: string; }> {
|
||||
const MILLISECONDS_IN_HOUR = 3600000;
|
||||
const now = Date.now();
|
||||
const warnings = await db.prepare('all',
|
||||
const warnings = await db.prepare("all",
|
||||
`SELECT "reason"
|
||||
FROM warnings
|
||||
WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1
|
||||
@@ -278,30 +278,30 @@ async function checkUserActiveWarning(userID: string): Promise<{ pass: boolean;
|
||||
LIMIT ?`,
|
||||
[
|
||||
userID,
|
||||
Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR)),
|
||||
Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR)),
|
||||
config.maxNumberOfActiveWarnings
|
||||
],
|
||||
) as {reason: string}[];
|
||||
|
||||
if (warnings?.length >= config.maxNumberOfActiveWarnings) {
|
||||
const defaultMessage = 'Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?';
|
||||
const defaultMessage = "Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?";
|
||||
|
||||
return {
|
||||
pass: false,
|
||||
pass: false,
|
||||
errorMessage: warnings[0]?.reason?.length > 0 ? warnings[0].reason : defaultMessage
|
||||
};
|
||||
}
|
||||
|
||||
return {pass: true, errorMessage: ''};
|
||||
return {pass: true, errorMessage: ""};
|
||||
}
|
||||
|
||||
function proxySubmission(req: Request) {
|
||||
fetch(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, {
|
||||
method: 'POST',
|
||||
body: req.body,
|
||||
})
|
||||
fetch(`${config.proxySubmission}/api/skipSegments?userID=${req.query.userID}&videoID=${req.query.videoID}`, {
|
||||
method: "POST",
|
||||
body: req.body,
|
||||
})
|
||||
.then(async res => {
|
||||
Logger.debug('Proxy Submission: ' + res.status + ' (' + (await res.text()) + ')');
|
||||
Logger.debug(`Proxy Submission: ${res.status} (${(await res.text())})`);
|
||||
})
|
||||
.catch(() => {
|
||||
Logger.error("Proxy Submission: Failed to make call");
|
||||
@@ -334,27 +334,27 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
segments.forEach((segment) => {
|
||||
if (!Object.values(ActionType).some((val) => val === segment.actionType)){
|
||||
segment.actionType = ActionType.Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const invalidFields = [];
|
||||
const errors = [];
|
||||
if (typeof videoID !== 'string') {
|
||||
invalidFields.push('videoID');
|
||||
if (typeof videoID !== "string") {
|
||||
invalidFields.push("videoID");
|
||||
}
|
||||
if (typeof userID !== 'string' || userID?.length < 30) {
|
||||
invalidFields.push('userID');
|
||||
if (userID?.length < 30) errors.push(`userID must be at least 30 characters long`);
|
||||
if (typeof userID !== "string" || userID?.length < 30) {
|
||||
invalidFields.push("userID");
|
||||
if (userID?.length < 30) errors.push(`userID must be at least 30 characters long`);
|
||||
}
|
||||
if (!Array.isArray(segments) || segments.length < 1) {
|
||||
invalidFields.push('segments');
|
||||
invalidFields.push("segments");
|
||||
}
|
||||
|
||||
if (invalidFields.length !== 0) {
|
||||
// invalid request
|
||||
const formattedFields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, '');
|
||||
const formattedErrors = errors.reduce((p, c, i) => p + (i !== 0 ? '. ' : ' ') + c, '');
|
||||
return res.status(400).send(`No valid ${formattedFields} field(s) provided.${formattedErrors}`);
|
||||
// invalid request
|
||||
const formattedFields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ", " : "") + c, "");
|
||||
const formattedErrors = errors.reduce((p, c, i) => p + (i !== 0 ? ". " : " ") + c, "");
|
||||
return res.status(400).send(`No valid ${formattedFields} field(s) provided.${formattedErrors}`);
|
||||
}
|
||||
|
||||
//hash the userID
|
||||
@@ -365,14 +365,14 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
return res.status(403).send(warningResult.errorMessage);
|
||||
}
|
||||
|
||||
let lockedCategoryList = (await db.prepare('all', 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => list.category );
|
||||
let lockedCategoryList = (await db.prepare("all", 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => list.category );
|
||||
|
||||
//check if this user is on the vip list
|
||||
const isVIP = (await db.prepare("get", `SELECT count(*) as "userCount" FROM "vipUsers" WHERE "userID" = ?`, [userID])).userCount > 0;
|
||||
|
||||
const decreaseVotes = 0;
|
||||
|
||||
const previousSubmissions = await db.prepare('all',
|
||||
const previousSubmissions = await db.prepare("all",
|
||||
`SELECT "videoDuration", "UUID"
|
||||
FROM "sponsorTimes"
|
||||
WHERE "videoID" = ? AND "service" = ? AND
|
||||
@@ -398,7 +398,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
if (videoDurationChanged(videoDuration)) {
|
||||
// Hide all previous submissions
|
||||
for (const submission of previousSubmissions) {
|
||||
await db.prepare('run', `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "UUID" = ?`, [submission.UUID]);
|
||||
await db.prepare("run", `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "UUID" = ?`, [submission.UUID]);
|
||||
}
|
||||
|
||||
// Reset lock categories
|
||||
@@ -420,13 +420,13 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
// Reject segment if it's in the locked categories list
|
||||
if (!isVIP && lockedCategoryList.indexOf(segments[i].category) !== -1) {
|
||||
// TODO: Do something about the fradulent submission
|
||||
Logger.warn("Caught a no-segment submission. userID: '" + userID + "', videoID: '" + videoID + "', category: '" + segments[i].category + "'");
|
||||
Logger.warn(`Caught a no-segment submission. userID: '${userID}', videoID: '${videoID}', category: '${segments[i].category}'`);
|
||||
return res.status(403).send(
|
||||
"New submissions are not allowed for the following category: '"
|
||||
+ segments[i].category + "'. A moderator has decided that no new segments are needed and that all current segments of this category are timed perfectly.\n\n "
|
||||
+ (segments[i].category === "sponsor" ? "Maybe the segment you are submitting is a different category that you have not enabled and is not a sponsor. " +
|
||||
"Categories that aren't sponsor, such as self-promotion can be enabled in the options.\n\n" : "")
|
||||
+ "If you believe this is incorrect, please contact someone on discord.gg/SponsorBlock or matrix.to/#/+sponsorblock:ajay.app",
|
||||
`New submissions are not allowed for the following category: \
|
||||
'${segments[i].category}'. A moderator has decided that no new segments are needed and that all current segments of this category are timed perfectly.\n\n\
|
||||
${(segments[i].category === "sponsor" ? "Maybe the segment you are submitting is a different category that you have not enabled and is not a sponsor. "+
|
||||
"Categories that aren't sponsor, such as self-promotion can be enabled in the options.\n\n" : "")}\
|
||||
If you believe this is incorrect, please contact someone on discord.gg/SponsorBlock or matrix.to/#/+sponsorblock:ajay.app`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
|
||||
if (isNaN(startTime) || isNaN(endTime)
|
||||
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime > endTime
|
||||
|| (getCategoryActionType(segments[i].category) === CategoryActionType.Skippable && startTime === endTime)
|
||||
|| (getCategoryActionType(segments[i].category) === CategoryActionType.Skippable && startTime === endTime)
|
||||
|| (getCategoryActionType(segments[i].category) === CategoryActionType.POI && startTime !== endTime)) {
|
||||
//invalid request
|
||||
return res.status(400).send("One of your segments times are invalid (too short, startTime before endTime, etc.)");
|
||||
@@ -448,7 +448,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
}
|
||||
|
||||
//check if this info has already been submitted before
|
||||
const duplicateCheck2Row = await db.prepare('get', `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "startTime" = ?
|
||||
const duplicateCheck2Row = await db.prepare("get", `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "startTime" = ?
|
||||
and "endTime" = ? and "category" = ? and "videoID" = ? and "service" = ?`, [startTime, endTime, segments[i].category, videoID, service]);
|
||||
if (duplicateCheck2Row.count > 0) {
|
||||
return res.sendStatus(409);
|
||||
@@ -466,7 +466,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
//decreaseVotes = -2; //Disable for now
|
||||
} else if (autoModerateResult) {
|
||||
//Normal automod behavior
|
||||
return res.status(403).send("Request rejected by auto moderator: " + autoModerateResult + " If this is an issue, send a message on Discord.");
|
||||
return res.status(403).send(`Request rejected by auto moderator: ${autoModerateResult} If this is an issue, send a message on Discord.`);
|
||||
}
|
||||
}
|
||||
// Will be filled when submitting
|
||||
@@ -486,7 +486,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
//check to see if this ip has submitted too many sponsors today
|
||||
const rateLimitCheckRow = await privateDB.prepare('get', `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "hashedIP" = ? AND "videoID" = ? AND "timeSubmitted" > ?`, [hashedIP, videoID, yesterday]);
|
||||
const rateLimitCheckRow = await privateDB.prepare("get", `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "hashedIP" = ? AND "videoID" = ? AND "timeSubmitted" > ?`, [hashedIP, videoID, yesterday]);
|
||||
|
||||
if (rateLimitCheckRow.count >= 10) {
|
||||
//too many sponsors for the same video from the same ip address
|
||||
@@ -498,7 +498,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
//check to see if the user has already submitted sponsors for this video
|
||||
const duplicateCheckRow = await db.prepare('get', `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "userID" = ? and "videoID" = ?`, [userID, videoID]);
|
||||
const duplicateCheckRow = await db.prepare("get", `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "userID" = ? and "videoID" = ?`, [userID, videoID]);
|
||||
|
||||
if (duplicateCheckRow.count >= 16) {
|
||||
//too many sponsors for the same video from the same user
|
||||
@@ -507,7 +507,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
}
|
||||
|
||||
//check to see if this user is shadowbanned
|
||||
const shadowBanRow = await db.prepare('get', `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
|
||||
const shadowBanRow = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
|
||||
|
||||
let shadowBanned = shadowBanRow.userCount;
|
||||
|
||||
@@ -528,16 +528,16 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
|
||||
const startingLocked = isVIP ? 1 : 0;
|
||||
try {
|
||||
await db.prepare('run', `INSERT INTO "sponsorTimes"
|
||||
await db.prepare("run", `INSERT INTO "sponsorTimes"
|
||||
("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "reputation", "shadowHidden", "hashedVideoID")
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
||||
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, startingLocked, UUID, userID, timeSubmitted, 0, segmentInfo.category, segmentInfo.actionType, service, videoDuration, reputation, shadowBanned, hashedVideoID,
|
||||
],
|
||||
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, startingLocked, UUID, userID, timeSubmitted, 0, segmentInfo.category, segmentInfo.actionType, service, videoDuration, reputation, shadowBanned, hashedVideoID,
|
||||
],
|
||||
);
|
||||
|
||||
//add to private db as well
|
||||
await privateDB.prepare('run', `INSERT INTO "sponsorTimes" VALUES(?, ?, ?)`, [videoID, hashedIP, timeSubmitted]);
|
||||
|
||||
await privateDB.prepare("run", `INSERT INTO "sponsorTimes" VALUES(?, ?, ?)`, [videoID, hashedIP, timeSubmitted]);
|
||||
|
||||
// Clear redis cache for this video
|
||||
QueryCacher.clearVideoCache({
|
||||
videoID,
|
||||
@@ -547,8 +547,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
});
|
||||
} catch (err) {
|
||||
//a DB change probably occurred
|
||||
Logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
||||
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
||||
Logger.error(`Error when putting sponsorTime in the DB: ${videoID}, ${segmentInfo.segment[0]}, ${segmentInfo.segment[1]}, ${userID}, ${segmentInfo.category}. ${err}`);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
|
||||
@@ -570,18 +569,18 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
|
||||
return res.json(newSegments);
|
||||
}
|
||||
|
||||
// Takes an array of arrays:
|
||||
// ex)
|
||||
// Takes an array of arrays:
|
||||
// ex)
|
||||
// [
|
||||
// [3, 40],
|
||||
// [50, 70],
|
||||
// [60, 80],
|
||||
// [3, 40],
|
||||
// [50, 70],
|
||||
// [60, 80],
|
||||
// [100, 150]
|
||||
// ]
|
||||
// ]
|
||||
// => transforms to combining overlapping segments
|
||||
// [
|
||||
// [3, 40],
|
||||
// [50, 80],
|
||||
// [50, 80],
|
||||
// [100, 150]
|
||||
// ]
|
||||
function mergeTimeSegments(ranges: number[][]) {
|
||||
|
||||
Reference in New Issue
Block a user