From 1a65b1c1907c78f1edbf64837251ce222311e836 Mon Sep 17 00:00:00 2001 From: localhost Date: Thu, 20 Aug 2026 17:07:01 +0200 Subject: [PATCH] feat(whitelist): add POST /whitelist endpoint --- bun.lock | 6 ++++-- config.json | 2 +- index.ts | 26 ++++++++++++++++++++++++++ package.json | 2 +- utils/youtube-channel.ts | 8 ++++---- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/bun.lock b/bun.lock index 54c0902..7b4b189 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ "fluent-ffmpeg": "^2.1.3", "googlevideo": "^4.0.4", "jsdom": "^26.1.0", - "youtubei.js": "^17.0.1", + "youtubei.js": "^17.2.0", }, "devDependencies": { "@types/bun": "latest", @@ -152,6 +152,8 @@ "express-ws": ["express-ws@5.0.2", "", { "dependencies": { "ws": "^7.4.6" }, "peerDependencies": { "express": "^4.0.0 || ^5.0.0-alpha.1" } }, "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ=="], + "fflate": ["fflate@0.8.3", "", {}, "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], "fluent-ffmpeg": ["fluent-ffmpeg@2.1.3", "", { "dependencies": { "async": "^0.2.9", "which": "^1.1.1" } }, "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q=="], @@ -316,7 +318,7 @@ "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], - "youtubei.js": ["youtubei.js@17.0.1", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0", "meriyah": "^6.1.4" } }, "sha512-1lO4b8UqMDzE0oh2qEGzbBOd4UYRdxn/4PdpRM7BGTHxM6ddsEsKZTu90jp8V9FHVgC2h1UirQyqoqLiKwl+Zg=="], + "youtubei.js": ["youtubei.js@17.2.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0", "fflate": "^0.8.2", "meriyah": "^6.1.4" } }, "sha512-XLNsgRKO1h7t4i9tIMWSQSeWdD7Ujkk5v1m5YCaumaHMhu/xuLqtO3M0Hq7CXNup9HlJ1NGrT1Y+HLIHnL6Ujg=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], diff --git a/config.json b/config.json index 2063931..832f913 100644 --- a/config.json +++ b/config.json @@ -3,4 +3,4 @@ "useCompanion": true, "maxVideoSize": 100, "companionIp": "" -} \ No newline at end of file +} diff --git a/index.ts b/index.ts index 80e947c..474b3d2 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ import express from 'express'; import * as cheerio from 'cheerio'; import * as fs from 'node:fs' +import path from 'node:path' import { EnabledTrackTypes } from 'googlevideo/utils'; import { @@ -16,6 +17,7 @@ import { fetchChannelVideos, getVideoThumbnails, parseViewCount, relativeTimeToT const ffmpeg = require('fluent-ffmpeg') const app = express(); +app.use(express.json()); require('express-ws')(app) ffmpeg.setFfmpegPath('/usr/local/bin/ffmpeg') @@ -152,6 +154,30 @@ app.get('/videos/:id', async (req, res) => { return res.json(false); } }) +app.post('/whitelist', async (req, res) => { + try { + const id = req.body?.id || req.body?.videoId || req.query?.id; + if (!id || typeof id !== 'string') { + return res.status(400).json({ success: false, message: 'Missing or invalid video id' }); + } + + const configPath = path.resolve(import.meta.dirname, 'config.json'); + const config: Config = await Bun.file(configPath).json(); + if (!Array.isArray(config.whitelist)) { + config.whitelist = []; + } + + if (!config.whitelist.includes(id)) { + config.whitelist.push(id); + await Bun.write(configPath, JSON.stringify(config, null, 2) + '\n'); + } + + return res.json({ success: true, message: `Added video ${id} to size whitelist`, whitelist: config.whitelist }); + } catch (error: any) { + return res.status(500).json({ success: false, message: `Failed to update whitelist: ${error.message}` }); + } +}); + interface Config { whitelist: string[] diff --git a/package.json b/package.json index 95ddb7e..7970a45 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,6 @@ "fluent-ffmpeg": "^2.1.3", "googlevideo": "^4.0.4", "jsdom": "^26.1.0", - "youtubei.js": "^17.0.1" + "youtubei.js": "^17.2.0" } } diff --git a/utils/youtube-channel.ts b/utils/youtube-channel.ts index b893a47..b763c76 100644 --- a/utils/youtube-channel.ts +++ b/utils/youtube-channel.ts @@ -45,7 +45,7 @@ export function serializeProto(obj: any): Uint8Array { continue; } const parts = key.split(':'); - const fieldNum = parseInt(parts[0], 10); + const fieldNum = parseInt(parts[0] || '0', 10); const type = parts[1]; if (isNaN(fieldNum)) { @@ -191,9 +191,9 @@ function decodeLengthSeconds(lengthText: string): number { let seconds = 0; if (parts.length === 2) { - seconds = parts[0] * 60 + parts[1]; + seconds = (parts[0] ?? 0) * 60 + (parts[1] ?? 0); } else if (parts.length === 3) { - seconds = parts[0] * 3600 + parts[1] * 60 + parts[2]; + seconds = (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0); } return seconds; } @@ -727,7 +727,7 @@ function shortTextToNumber(shortText: string): number { if (!shortText) return 0; const match = shortText.match(/(\d+(?:\.\d+)?)\s*([mMkKbB]?)/i); if (!match) return 0; - let number = parseFloat(match[1]); + let number = parseFloat(match[1] || '0'); const suffix = match[2]?.toLowerCase(); switch (suffix) { case 'k': number *= 1000; break;