api: add DURATION_LIMIT env variable

duration limit is now in seconds and customizable across instances
This commit is contained in:
wukko 2024-05-16 20:57:48 +06:00
parent b5c81084c8
commit d1e8929ee2
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
12 changed files with 29 additions and 29 deletions

View file

@ -1,6 +1,5 @@
{ {
"streamLifespan": 90000, "streamLifespan": 90000,
"maxVideoDuration": 10800000,
"genericUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36", "genericUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"authorInfo": { "authorInfo": {
"support": { "support": {

View file

@ -44,6 +44,8 @@ const
rateLimitWindow: (process.env.RATELIMIT_WINDOW && parseInt(process.env.RATELIMIT_WINDOW)) || 60, rateLimitWindow: (process.env.RATELIMIT_WINDOW && parseInt(process.env.RATELIMIT_WINDOW)) || 60,
rateLimitMax: (process.env.RATELIMIT_MAX && parseInt(process.env.RATELIMIT_MAX)) || 20, rateLimitMax: (process.env.RATELIMIT_MAX && parseInt(process.env.RATELIMIT_MAX)) || 20,
durationLimit: (process.env.DURATION_LIMIT && parseInt(process.env.DURATION_LIMIT)) || 10800,
processingPriority: process.platform !== 'win32' processingPriority: process.platform !== 'win32'
&& process.env.PROCESSING_PRIORITY && process.env.PROCESSING_PRIORITY
&& parseInt(process.env.PROCESSING_PRIORITY) && parseInt(process.env.PROCESSING_PRIORITY)
@ -54,7 +56,6 @@ export const
audioIgnore = servicesConfigJson.audioIgnore, audioIgnore = servicesConfigJson.audioIgnore,
version = packageJson.version, version = packageJson.version,
streamLifespan = config.streamLifespan, streamLifespan = config.streamLifespan,
maxVideoDuration = config.maxVideoDuration,
genericUserAgent = config.genericUserAgent, genericUserAgent = config.genericUserAgent,
repo = packageJson.bugs.url.replace('/issues', ''), repo = packageJson.bugs.url.replace('/issues', ''),
authorInfo = config.authorInfo, authorInfo = config.authorInfo,

View file

@ -1,4 +1,4 @@
import { genericUserAgent, maxVideoDuration } from "../../config.js"; import { genericUserAgent, env } from "../../config.js";
// TO-DO: higher quality downloads (currently requires an account) // TO-DO: higher quality downloads (currently requires an account)
@ -39,8 +39,8 @@ async function com_download(id) {
} }
let streamData = JSON.parse(html.split('<script>window.__playinfo__=')[1].split('</script>')[0]); let streamData = JSON.parse(html.split('<script>window.__playinfo__=')[1].split('</script>')[0]);
if (streamData.data.timelength > maxVideoDuration) { if (streamData.data.timelength > env.durationLimit * 1000) {
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
} }
const [ video, audio ] = extractBestQuality(streamData.data.dash); const [ video, audio ] = extractBestQuality(streamData.data.dash);
@ -79,8 +79,8 @@ async function tv_download(id) {
return { error: 'ErrorEmptyDownload' }; return { error: 'ErrorEmptyDownload' };
} }
if (video.duration > maxVideoDuration) { if (video.duration > env.durationLimit * 1000) {
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
} }
return { return {

View file

@ -1,5 +1,5 @@
import HLSParser from 'hls-parser'; import HLSParser from 'hls-parser';
import { maxVideoDuration } from '../../config.js'; import { env } from '../../config.js';
let _token; let _token;
@ -73,8 +73,8 @@ export default async function({ id }) {
return { error: 'ErrorEmptyDownload' } return { error: 'ErrorEmptyDownload' }
} }
if (media.duration * 1000 > maxVideoDuration) { if (media.duration > env.durationLimit) {
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
} }
const manifest = await fetch(media.hlsURL).then(r => r.text()).catch(() => {}); const manifest = await fetch(media.hlsURL).then(r => r.text()).catch(() => {});

View file

@ -1,4 +1,4 @@
import { genericUserAgent, maxVideoDuration } from "../../config.js"; import { genericUserAgent, env } from "../../config.js";
import { cleanString } from "../../sub/utils.js"; import { cleanString } from "../../sub/utils.js";
const resolutions = { const resolutions = {
@ -29,7 +29,7 @@ export default async function(o) {
if (videoData.provider !== "UPLOADED_ODKL") return { error: 'ErrorUnsupported' }; if (videoData.provider !== "UPLOADED_ODKL") return { error: 'ErrorUnsupported' };
if (videoData.movie.is_live) return { error: 'ErrorLiveVideo' }; if (videoData.movie.is_live) return { error: 'ErrorLiveVideo' };
if (videoData.movie.duration > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; if (videoData.movie.duration > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let videos = videoData.videos.filter(v => !v.disallowed); let videos = videoData.videos.filter(v => !v.disallowed);
let bestVideo = videos.find(v => resolutions[v.name] === quality) || videos[videos.length - 1]; let bestVideo = videos.find(v => resolutions[v.name] === quality) || videos[videos.length - 1];

View file

@ -1,4 +1,4 @@
import { genericUserAgent, maxVideoDuration } from "../../config.js"; import { genericUserAgent, env } from "../../config.js";
import { getCookie, updateCookieValues } from "../cookie/manager.js"; import { getCookie, updateCookieValues } from "../cookie/manager.js";
async function getAccessToken() { async function getAccessToken() {
@ -79,8 +79,8 @@ export default async function(obj) {
if (!data.secure_media?.reddit_video) if (!data.secure_media?.reddit_video)
return { error: 'ErrorEmptyDownload' }; return { error: 'ErrorEmptyDownload' };
if (data.secure_media?.reddit_video?.duration * 1000 > maxVideoDuration) if (data.secure_media?.reddit_video?.duration > env.durationLimit)
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let audio = false, let audio = false,
video = data.secure_media?.reddit_video?.fallback_url?.split('?')[0], video = data.secure_media?.reddit_video?.fallback_url?.split('?')[0],

View file

@ -1,6 +1,6 @@
import HLS from 'hls-parser'; import HLS from 'hls-parser';
import { maxVideoDuration } from "../../config.js"; import { env } from "../../config.js";
import { cleanString } from '../../sub/utils.js'; import { cleanString } from '../../sub/utils.js';
async function requestJSON(url) { async function requestJSON(url) {
@ -35,8 +35,8 @@ export default async function(obj) {
if (play.detail || !play.video_balancer) return { error: 'ErrorEmptyDownload' }; if (play.detail || !play.video_balancer) return { error: 'ErrorEmptyDownload' };
if (play.live_streams?.hls) return { error: 'ErrorLiveVideo' }; if (play.live_streams?.hls) return { error: 'ErrorLiveVideo' };
if (play.duration > maxVideoDuration) if (play.duration > env.durationLimit * 1000)
return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let m3u8 = await fetch(play.video_balancer.m3u8) let m3u8 = await fetch(play.video_balancer.m3u8)
.then(r => r.text()) .then(r => r.text())

View file

@ -1,4 +1,4 @@
import { maxVideoDuration } from "../../config.js"; import { env } from "../../config.js";
import { cleanString } from "../../sub/utils.js"; import { cleanString } from "../../sub/utils.js";
const cachedID = { const cachedID = {
@ -77,8 +77,8 @@ export default async function(obj) {
if (fileUrl.substring(0, 54) !== "https://api-v2.soundcloud.com/media/soundcloud:tracks:") return { error: 'ErrorEmptyDownload' }; if (fileUrl.substring(0, 54) !== "https://api-v2.soundcloud.com/media/soundcloud:tracks:") return { error: 'ErrorEmptyDownload' };
if (json.duration > maxVideoDuration) if (json.duration > env.durationLimit * 1000)
return { error: ['ErrorLengthAudioConvert', maxVideoDuration / 60000] }; return { error: ['ErrorLengthAudioConvert', env.durationLimit / 60] };
let file = await fetch(fileUrl).then(async (r) => { return (await r.json()).url }).catch(() => {}); let file = await fetch(fileUrl).then(async (r) => { return (await r.json()).url }).catch(() => {});
if (!file) return { error: 'ErrorCouldntFetch' }; if (!file) return { error: 'ErrorCouldntFetch' };

View file

@ -1,4 +1,4 @@
import { maxVideoDuration } from "../../config.js"; import { env } from "../../config.js";
import { cleanString } from '../../sub/utils.js'; import { cleanString } from '../../sub/utils.js';
const gqlURL = "https://gql.twitch.tv/gql"; const gqlURL = "https://gql.twitch.tv/gql";
@ -34,7 +34,7 @@ export default async function (obj) {
let clipMetadata = req_metadata.data.clip; let clipMetadata = req_metadata.data.clip;
if (clipMetadata.durationSeconds > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; if (clipMetadata.durationSeconds > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
if (!clipMetadata.videoQualities || !clipMetadata.broadcaster) return { error: 'ErrorEmptyDownload' }; if (!clipMetadata.videoQualities || !clipMetadata.broadcaster) return { error: 'ErrorEmptyDownload' };
let req_token = await fetch(gqlURL, { let req_token = await fetch(gqlURL, {

View file

@ -1,4 +1,4 @@
import { maxVideoDuration } from "../../config.js"; import { env } from "../../config.js";
import { cleanString } from '../../sub/utils.js'; import { cleanString } from '../../sub/utils.js';
const resolutionMatch = { const resolutionMatch = {
@ -63,7 +63,7 @@ export default async function(obj) {
} }
} }
if (api.video.duration > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; if (api.video.duration > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let masterJSONURL = api["request"]["files"]["dash"]["cdns"]["akfire_interconnect_quic"]["url"]; let masterJSONURL = api["request"]["files"]["dash"]["cdns"]["akfire_interconnect_quic"]["url"];
let masterJSON = await fetch(masterJSONURL).then((r) => { return r.json() }).catch(() => { return false }); let masterJSON = await fetch(masterJSONURL).then((r) => { return r.json() }).catch(() => { return false });

View file

@ -1,4 +1,4 @@
import { genericUserAgent, maxVideoDuration } from "../../config.js"; import { genericUserAgent, env } from "../../config.js";
import { cleanString } from "../../sub/utils.js"; import { cleanString } from "../../sub/utils.js";
const resolutions = ["2160", "1440", "1080", "720", "480", "360", "240"]; const resolutions = ["2160", "1440", "1080", "720", "480", "360", "240"];
@ -21,7 +21,7 @@ export default async function(o) {
let js = JSON.parse('{"lang":' + html.split(`{"lang":`)[1].split(']);')[0]); let js = JSON.parse('{"lang":' + html.split(`{"lang":`)[1].split(']);')[0]);
if (Number(js.mvData.is_active_live) !== 0) return { error: 'ErrorLiveVideo' }; if (Number(js.mvData.is_active_live) !== 0) return { error: 'ErrorLiveVideo' };
if (js.mvData.duration > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; if (js.mvData.duration > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
for (let i in resolutions) { for (let i in resolutions) {
if (js.player.params[0][`url${resolutions[i]}`]) { if (js.player.params[0][`url${resolutions[i]}`]) {

View file

@ -1,5 +1,5 @@
import { Innertube, Session } from 'youtubei.js'; import { Innertube, Session } from 'youtubei.js';
import { maxVideoDuration } from '../../config.js'; import { env } from '../../config.js';
import { cleanString } from '../../sub/utils.js'; import { cleanString } from '../../sub/utils.js';
import { fetch } from 'undici' import { fetch } from 'undici'
@ -92,7 +92,7 @@ export default async function(o) {
if (bestQuality) bestQuality = qual(bestQuality); if (bestQuality) bestQuality = qual(bestQuality);
if (!bestQuality && !o.isAudioOnly || !hasAudio) return { error: 'ErrorYTTryOtherCodec' }; if (!bestQuality && !o.isAudioOnly || !hasAudio) return { error: 'ErrorYTTryOtherCodec' };
if (info.basic_info.duration > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; if (info.basic_info.duration > env.durationLimit) return { error: ['ErrorLengthLimit', env.durationLimit / 60] };
let checkBestAudio = (i) => (i.has_audio && !i.has_video), let checkBestAudio = (i) => (i.has_audio && !i.has_video),
audio = adaptive_formats.find(i => checkBestAudio(i) && !i.is_dubbed); audio = adaptive_formats.find(i => checkBestAudio(i) && !i.is_dubbed);