change video limit with size limit
This commit is contained in:
parent
1ad4cd426a
commit
ed35dd7c19
|
|
@ -1,5 +1,4 @@
|
||||||
import { Elysia, t } from 'elysia';
|
import { Elysia, t } from 'elysia';
|
||||||
import { RedisRateLimiter } from 'rolling-rate-limiter'
|
|
||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
|
|
||||||
import { db } from '@/utils/database'
|
import { db } from '@/utils/database'
|
||||||
|
|
@ -15,12 +14,20 @@ import { parseSlop } from '@/utils/slop';
|
||||||
const app = new Elysia()
|
const app = new Elysia()
|
||||||
const videoIds: Record<string, string> = {}
|
const videoIds: Record<string, string> = {}
|
||||||
|
|
||||||
const limiter = new RedisRateLimiter({
|
const MB_LIMIT = 500
|
||||||
client: redis,
|
|
||||||
namespace: 'save:',
|
const checkMbLimit = async (hash: string, mb?: number): Promise<boolean> => {
|
||||||
interval: 24 * 60 * 60000, // 24h
|
const key = `save-mb:${hash}`
|
||||||
maxInInterval: 50
|
const current = parseInt(await redis.get(key) || '0')
|
||||||
})
|
if (!mb) return current >= MB_LIMIT
|
||||||
|
if (current + mb > MB_LIMIT) return true
|
||||||
|
|
||||||
|
const pipeline = redis.pipeline()
|
||||||
|
pipeline.incrby(key, mb)
|
||||||
|
pipeline.expire(key, 24 * 60 * 60)
|
||||||
|
await pipeline.exec()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const sendError = (ws: any, message: string, close: boolean = true) => {
|
const sendError = (ws: any, message: string, close: boolean = true) => {
|
||||||
ws.send(`ERROR - ${message}`);
|
ws.send(`ERROR - ${message}`);
|
||||||
|
|
@ -101,7 +108,7 @@ app.ws('/save', {
|
||||||
ws.close()
|
ws.close()
|
||||||
} else {
|
} else {
|
||||||
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
||||||
const isLimited = await limiter.limit(hash.toString())
|
const isLimited = await checkMbLimit(hash.toString())
|
||||||
if (isLimited) {
|
if (isLimited) {
|
||||||
return sendError(ws, 'You have been ratelimited. </br>Is this an urgent archive? Please email me: admin@preservetube.com');
|
return sendError(ws, 'You have been ratelimited. </br>Is this an urgent archive? Please email me: admin@preservetube.com');
|
||||||
}
|
}
|
||||||
|
|
@ -150,6 +157,16 @@ app.ws('/save', {
|
||||||
return sendError(ws, downloadResult.message);
|
return sendError(ws, downloadResult.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mbsUsed = Math.ceil(downloadResult.size / (1024 * 1024))
|
||||||
|
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
||||||
|
const isMbLimited = await checkMbLimit(hash.toString(), mbsUsed)
|
||||||
|
if (isMbLimited) {
|
||||||
|
const file = fs.readdirSync('./videos/').find(f => f.includes(`${videoId}.`))
|
||||||
|
if (file) fs.unlinkSync('./videos/' + file)
|
||||||
|
await cleanup(ws, videoId);
|
||||||
|
return sendError(ws, 'Daily storage limit reached. Is this an urgent archive? Please email me: admin@preservetube.com');
|
||||||
|
}
|
||||||
|
|
||||||
const uploadSuccess = await handleUpload(ws, videoId);
|
const uploadSuccess = await handleUpload(ws, videoId);
|
||||||
if (!uploadSuccess) await redis.del(videoId);
|
if (!uploadSuccess) await redis.del(videoId);
|
||||||
|
|
||||||
|
|
@ -200,6 +217,7 @@ app.ws('/savechannel', {
|
||||||
|
|
||||||
videoIds[ws.id] = `downloading-${channelId}`;
|
videoIds[ws.id] = `downloading-${channelId}`;
|
||||||
const videos = await getChannelVideos(channelId);
|
const videos = await getChannelVideos(channelId);
|
||||||
|
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
||||||
|
|
||||||
for (const video of videos.slice(0, 5)) {
|
for (const video of videos.slice(0, 5)) {
|
||||||
if (!video || (await redis.get(video.video_id)) || (await redis.get(`blacklist:${video.video_id}`))) continue;
|
if (!video || (await redis.get(video.video_id)) || (await redis.get(`blacklist:${video.video_id}`))) continue;
|
||||||
|
|
@ -211,7 +229,7 @@ app.ws('/savechannel', {
|
||||||
if (already) continue
|
if (already) continue
|
||||||
|
|
||||||
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
const hash = Bun.hash(getRateLimitKey(ws.data.headers['cf-connecting-ip'] || '0.0.0.0'))
|
||||||
const isLimited = await limiter.limit(hash.toString())
|
const isLimited = await checkMbLimit(hash.toString())
|
||||||
if (isLimited) {
|
if (isLimited) {
|
||||||
sendError(ws, 'You have been ratelimited. </br>Is this an urgent archive? Please email me: admin@preservetube.com', false);
|
sendError(ws, 'You have been ratelimited. </br>Is this an urgent archive? Please email me: admin@preservetube.com', false);
|
||||||
break;
|
break;
|
||||||
|
|
@ -231,7 +249,17 @@ app.ws('/savechannel', {
|
||||||
await redis.set(video.video_id, 'downloading', 'EX', 300);
|
await redis.set(video.video_id, 'downloading', 'EX', 300);
|
||||||
|
|
||||||
const downloadResult = await downloadVideo(ws, video.video_id);
|
const downloadResult = await downloadVideo(ws, video.video_id);
|
||||||
if (!downloadResult.fail) await handleUpload(ws, video.video_id, true);
|
if (!downloadResult.fail) {
|
||||||
|
const mbsUsed = Math.ceil(downloadResult.size / (1024 * 1024))
|
||||||
|
const isMbLimited = await checkMbLimit(hash.toString(), mbsUsed)
|
||||||
|
if (isMbLimited) {
|
||||||
|
const file = fs.readdirSync('./videos/').find(f => f.includes(`${video.video_id}.`))
|
||||||
|
if (file) fs.unlinkSync('./videos/' + file)
|
||||||
|
sendError(ws, 'Daily storage limit reached. Is this an urgent archive? Please email me: admin@preservetube.com', false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await handleUpload(ws, video.video_id, true);
|
||||||
|
}
|
||||||
|
|
||||||
await redis.del(video.video_id);
|
await redis.del(video.video_id);
|
||||||
ws.send(`DATA - Created video page for ${video.title.text}`)
|
ws.send(`DATA - Created video page for ${video.title.text}`)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
async function downloadVideo(ws: any, id: string): Promise<{ fail: boolean, message: string }> {
|
async function downloadVideo(ws: any, id: string): Promise<{ fail: boolean, message: string, size: number }> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let isDownloading = true
|
let isDownloading = true
|
||||||
const downloader = new WebSocket(`ws://${(process.env.METADATA!).replace('http://', '')}/download/${id}`)
|
const downloader = new WebSocket(`ws://${(process.env.METADATA!).replace('http://', '')}/download/${id}`)
|
||||||
|
let size: number = 0
|
||||||
|
|
||||||
downloader.on('message', async function message(data: any) {
|
downloader.on('message', async function message(data: any) {
|
||||||
const text = data.toString()
|
const text = data.toString()
|
||||||
if (text == 'done') {
|
if (text.startsWith('VIDEOSIZE-')) {
|
||||||
|
size = parseInt(text.replace('VIDEOSIZE-', ''))
|
||||||
|
} else if (text == 'done') {
|
||||||
isDownloading = false
|
isDownloading = false
|
||||||
return resolve({
|
return resolve({
|
||||||
fail: false,
|
fail: false,
|
||||||
message: ''
|
message: '',
|
||||||
|
size
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ws.send(`DATA - ${text}`)
|
ws.send(`DATA - ${text}`)
|
||||||
|
|
@ -23,7 +27,8 @@ async function downloadVideo(ws: any, id: string): Promise<{ fail: boolean, mess
|
||||||
|
|
||||||
return resolve({
|
return resolve({
|
||||||
fail: true,
|
fail: true,
|
||||||
message: 'The metadata server unexpectedly closed the websocket. Please try again.'
|
message: 'The metadata server unexpectedly closed the websocket. Please try again.',
|
||||||
|
size
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue