mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-08 20:47:02 +03:00
Add redis caching for 404s
This commit is contained in:
5
src/middleware/redisKeys.ts
Normal file
5
src/middleware/redisKeys.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Category, VideoID } from "../types/segments.model";
|
||||||
|
|
||||||
|
export function skipSegmentsKey(videoID: VideoID): string {
|
||||||
|
return "segments-" + videoID;
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
|
import { RedisClient } from 'redis';
|
||||||
import { config } from '../config';
|
import { config } from '../config';
|
||||||
import { db, privateDB } from '../databases/databases';
|
import { db, privateDB } from '../databases/databases';
|
||||||
|
import { skipSegmentsKey } from '../middleware/redisKeys';
|
||||||
import { SBRecord } from '../types/lib.model';
|
import { SBRecord } from '../types/lib.model';
|
||||||
import { Category, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model";
|
import { Category, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model";
|
||||||
import { getHash } from '../utils/getHash';
|
import { getHash } from '../utils/getHash';
|
||||||
import { getIP } from '../utils/getIP';
|
import { getIP } from '../utils/getIP';
|
||||||
import { Logger } from '../utils/logger';
|
import { Logger } from '../utils/logger';
|
||||||
|
import redis from '../utils/redis';
|
||||||
|
|
||||||
|
|
||||||
function prepareCategorySegments(req: Request, videoID: VideoID, category: Category, segments: DBSegment[], cache: SegmentCache = {shadowHiddenSegmentIPs: {}}): Segment[] {
|
function prepareCategorySegments(req: Request, videoID: VideoID, category: Category, segments: DBSegment[], cache: SegmentCache = {shadowHiddenSegmentIPs: {}}): Segment[] {
|
||||||
@@ -216,16 +219,29 @@ function chooseSegments(segments: DBSegment[]): DBSegment[] {
|
|||||||
*
|
*
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function handleGetSegments(req: Request, res: Response) {
|
async function handleGetSegments(req: Request, res: Response): Promise<Segment[] | false> {
|
||||||
const videoID = req.query.videoID as string;
|
const videoID = req.query.videoID as VideoID;
|
||||||
// Default to sponsor
|
// Default to sponsor
|
||||||
// If using params instead of JSON, only one category can be pulled
|
// If using params instead of JSON, only one category can be pulled
|
||||||
|
console.log(req.query.categories)
|
||||||
const categories = req.query.categories
|
const categories = req.query.categories
|
||||||
? JSON.parse(req.query.categories as string)
|
? JSON.parse(req.query.categories as string)
|
||||||
: req.query.category
|
: req.query.category
|
||||||
? [req.query.category]
|
? [req.query.category]
|
||||||
: ['sponsor'];
|
: ['sponsor'];
|
||||||
|
|
||||||
|
// Only 404s are cached at the moment
|
||||||
|
const redisResult = await redis.getAsync(skipSegmentsKey(videoID));
|
||||||
|
|
||||||
|
if (redisResult.reply) {
|
||||||
|
const redisSegments = JSON.parse(redisResult.reply);
|
||||||
|
if (redisSegments?.length === 0) {
|
||||||
|
res.sendStatus(404);
|
||||||
|
Logger.debug("Using segments from cache for " + videoID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const segments = getSegmentsByVideoID(req, videoID, categories);
|
const segments = getSegmentsByVideoID(req, videoID, categories);
|
||||||
|
|
||||||
if (segments === null || segments === undefined) {
|
if (segments === null || segments === undefined) {
|
||||||
@@ -235,19 +251,28 @@ function handleGetSegments(req: Request, res: Response) {
|
|||||||
|
|
||||||
if (segments.length === 0) {
|
if (segments.length === 0) {
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
|
|
||||||
|
// Save in cache
|
||||||
|
redis.setAsync(skipSegmentsKey(videoID), JSON.stringify(segments));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
function endpoint(req: Request, res: Response): void {
|
async function endpoint(req: Request, res: Response): Promise<void> {
|
||||||
let segments = handleGetSegments(req, res);
|
try {
|
||||||
|
const segments = await handleGetSegments(req, res);
|
||||||
|
|
||||||
|
// If false, res.send has already been called
|
||||||
if (segments) {
|
if (segments) {
|
||||||
//send result
|
//send result
|
||||||
res.send(segments);
|
res.send(segments);
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {handleGetSegments} from './getSkipSegments';
|
import {handleGetSegments} from './getSkipSegments';
|
||||||
import {Request, Response} from 'express';
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
export function oldGetVideoSponsorTimes(req: Request, res: Response) {
|
export async function oldGetVideoSponsorTimes(req: Request, res: Response): Promise<void> {
|
||||||
let segments = handleGetSegments(req, res);
|
let segments = await handleGetSegments(req, res);
|
||||||
|
|
||||||
if (segments) {
|
if (segments) {
|
||||||
// Convert to old outputs
|
// Convert to old outputs
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {getFormattedTime} from '../utils/getFormattedTime';
|
|||||||
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
||||||
import {dispatchEvent} from '../utils/webhookUtils';
|
import {dispatchEvent} from '../utils/webhookUtils';
|
||||||
import {Request, Response} from 'express';
|
import {Request, Response} from 'express';
|
||||||
|
import { skipSegmentsKey } from '../middleware/redisKeys';
|
||||||
|
import redis from '../utils/redis';
|
||||||
|
|
||||||
|
|
||||||
function sendWebhookNotification(userID: string, videoID: string, UUID: string, submissionCount: number, youtubeData: any, {submissionStart, submissionEnd}: { submissionStart: number; submissionEnd: number; }, segmentInfo: any) {
|
function sendWebhookNotification(userID: string, videoID: string, UUID: string, submissionCount: number, youtubeData: any, {submissionStart, submissionEnd}: { submissionStart: number; submissionEnd: number; }, segmentInfo: any) {
|
||||||
@@ -496,6 +498,9 @@ export async function postSkipSegments(req: Request, res: Response) {
|
|||||||
|
|
||||||
//add to private db as well
|
//add to private db as well
|
||||||
privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]);
|
privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]);
|
||||||
|
|
||||||
|
// Clear redis cache for this video
|
||||||
|
redis.delAsync(skipSegmentsKey(videoID));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//a DB change probably occurred
|
//a DB change probably occurred
|
||||||
res.sendStatus(502);
|
res.sendStatus(502);
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import {getIP} from '../utils/getIP';
|
|||||||
import {getHash} from '../utils/getHash';
|
import {getHash} from '../utils/getHash';
|
||||||
import {config} from '../config';
|
import {config} from '../config';
|
||||||
import { UserID } from '../types/user.model';
|
import { UserID } from '../types/user.model';
|
||||||
|
import redis from '../utils/redis';
|
||||||
|
import { skipSegmentsKey } from '../middleware/redisKeys';
|
||||||
|
import { VideoID } from '../types/segments.model';
|
||||||
|
|
||||||
const voteTypes = {
|
const voteTypes = {
|
||||||
normal: 0,
|
normal: 0,
|
||||||
@@ -335,7 +338,8 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
|
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
|
||||||
const row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
const row = db.prepare('get', "SELECT videoID, votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]) as
|
||||||
|
{videoID: VideoID, votes: number, views: number};
|
||||||
|
|
||||||
if (voteTypeEnum === voteTypes.normal) {
|
if (voteTypeEnum === voteTypes.normal) {
|
||||||
if ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
if ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
||||||
@@ -383,6 +387,9 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
|||||||
db.prepare('run', "UPDATE sponsorTimes SET locked = 0 WHERE UUID = ?", [UUID]);
|
db.prepare('run', "UPDATE sponsorTimes SET locked = 0 WHERE UUID = ?", [UUID]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear redis cache for this video
|
||||||
|
redis.delAsync(skipSegmentsKey(row.videoID));
|
||||||
|
|
||||||
//for each positive vote, see if a hidden submission can be shown again
|
//for each positive vote, see if a hidden submission can be shown again
|
||||||
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
|
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
|
||||||
//find the UUID that submitted the submission that was voted on
|
//find the UUID that submitted the submission that was voted on
|
||||||
|
|||||||
@@ -2,15 +2,33 @@ import {config} from '../config';
|
|||||||
import {Logger} from './logger';
|
import {Logger} from './logger';
|
||||||
import redis, {Callback} from 'redis';
|
import redis, {Callback} from 'redis';
|
||||||
|
|
||||||
let exportObject = {
|
interface RedisSB {
|
||||||
get: (key: string, callback?: Callback<string | null>) => callback(null, undefined),
|
get(key: string, callback?: Callback<string | null>): void;
|
||||||
set: (key: string, value: string, callback?: Callback<string | null>) => callback(null, undefined)
|
getAsync?(key: string): Promise<{err: Error | null, reply: string | null}>;
|
||||||
|
set(key: string, value: string, callback?: Callback<string | null>): void;
|
||||||
|
setAsync?(key: string, value: string): Promise<{err: Error | null, reply: string | null}>;
|
||||||
|
delAsync?(...keys: [string]): Promise<Error | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let exportObject: RedisSB = {
|
||||||
|
get: (key, callback?) => callback(null, undefined),
|
||||||
|
getAsync: (key) =>
|
||||||
|
new Promise((resolve) => resolve({err: null, reply: undefined})),
|
||||||
|
set: (key, value, callback) => callback(null, undefined),
|
||||||
|
setAsync: (key, value) =>
|
||||||
|
new Promise((resolve) => resolve({err: null, reply: undefined})),
|
||||||
|
delAsync: (...keys) =>
|
||||||
|
new Promise((resolve) => resolve(null)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.redis) {
|
if (config.redis) {
|
||||||
Logger.info('Connected to redis');
|
Logger.info('Connected to redis');
|
||||||
const client = redis.createClient(config.redis);
|
const client = redis.createClient(config.redis);
|
||||||
exportObject = client;
|
exportObject = client;
|
||||||
|
|
||||||
|
exportObject.getAsync = (key) => new Promise((resolve) => client.get(key, (err, reply) => resolve({err, reply})));
|
||||||
|
exportObject.setAsync = (key, value) => new Promise((resolve) => client.set(key, value, (err, reply) => resolve({err, reply})));
|
||||||
|
exportObject.delAsync = (...keys) => new Promise((resolve) => client.del(keys, (err) => resolve(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default exportObject;
|
export default exportObject;
|
||||||
|
|||||||
Reference in New Issue
Block a user