2024-05-15 17:39:44 +02:00
|
|
|
import ipaddr from "ipaddr.js";
|
|
|
|
|
2024-08-03 10:47:13 +02:00
|
|
|
import { normalizeURL } from "./url.js";
|
2024-05-15 17:39:44 +02:00
|
|
|
import { createStream } from "../stream/manage.js";
|
2024-08-03 10:47:13 +02:00
|
|
|
import { verifyLanguageCode } from "../misc/utils.js";
|
2024-05-15 17:39:44 +02:00
|
|
|
|
2024-08-03 19:06:32 +02:00
|
|
|
const apiRequest = {
|
|
|
|
option: {
|
|
|
|
audioFormat: ["best", "mp3", "ogg", "wav", "opus"],
|
|
|
|
downloadMode: ["auto", "audio", "mute"],
|
|
|
|
filenameStyle: ["classic", "pretty", "basic", "nerdy"],
|
|
|
|
videoQuality: ["max", "4320", "2160", "1440", "1080", "720", "480", "360", "240", "144"],
|
|
|
|
youtubeVideoCodec: ["h264", "av1", "vp9"],
|
2024-05-15 17:39:44 +02:00
|
|
|
},
|
2024-08-03 19:06:32 +02:00
|
|
|
boolean: [
|
2024-05-15 17:39:44 +02:00
|
|
|
"disableMetadata",
|
2024-08-03 19:06:32 +02:00
|
|
|
"tiktokFullAudio",
|
|
|
|
"tiktokH265",
|
2024-05-15 17:39:44 +02:00
|
|
|
"twitterGif",
|
2024-08-03 19:06:32 +02:00
|
|
|
"youtubeDubBrowserLang",
|
|
|
|
"youtubeDubLang"
|
2024-05-15 17:39:44 +02:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createResponse(responseType, responseData) {
|
2024-08-03 09:51:09 +02:00
|
|
|
const internalError = (code) => {
|
|
|
|
let error = code || "Internal Server Error";
|
2024-05-15 17:55:14 +02:00
|
|
|
return {
|
|
|
|
status: 500,
|
|
|
|
body: {
|
|
|
|
status: "error",
|
2024-08-03 09:51:09 +02:00
|
|
|
error: {
|
|
|
|
code: code || "Internal Server Error",
|
|
|
|
},
|
|
|
|
text: error, // temporary backwards compatibility
|
2024-05-15 17:55:14 +02:00
|
|
|
critical: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-15 17:39:44 +02:00
|
|
|
try {
|
|
|
|
let status = 200,
|
|
|
|
response = {};
|
2024-08-03 09:51:09 +02:00
|
|
|
|
2024-05-15 17:39:44 +02:00
|
|
|
switch(responseType) {
|
|
|
|
case "error":
|
|
|
|
status = 400;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "rate-limit":
|
|
|
|
status = 429;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (responseType) {
|
|
|
|
case "error":
|
2024-08-03 09:51:09 +02:00
|
|
|
response = {
|
|
|
|
error: {
|
|
|
|
code: responseData.code,
|
|
|
|
context: responseData?.context,
|
|
|
|
},
|
|
|
|
text: responseData.code, // temporary backwards compatibility
|
|
|
|
}
|
|
|
|
break;
|
2024-05-15 17:39:44 +02:00
|
|
|
case "success":
|
|
|
|
case "rate-limit":
|
|
|
|
response = {
|
2024-08-03 09:51:09 +02:00
|
|
|
text: responseData.t,
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "redirect":
|
|
|
|
response = {
|
2024-08-03 09:51:09 +02:00
|
|
|
url: responseData.u,
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "stream":
|
|
|
|
response = {
|
2024-08-03 09:51:09 +02:00
|
|
|
url: createStream(responseData),
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "picker":
|
|
|
|
let pickerType = "various",
|
|
|
|
audio = false;
|
|
|
|
|
|
|
|
if (responseData.service === "tiktok") {
|
2024-08-03 09:51:09 +02:00
|
|
|
audio = responseData.u;
|
|
|
|
pickerType = "images";
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
response = {
|
|
|
|
pickerType: pickerType,
|
|
|
|
picker: responseData.picker,
|
2024-08-03 09:51:09 +02:00
|
|
|
audio: audio,
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
break;
|
2024-08-03 09:51:09 +02:00
|
|
|
case "critical":
|
|
|
|
return internalError(responseData.code);
|
|
|
|
default:
|
2024-05-15 17:39:44 +02:00
|
|
|
throw "unreachable"
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
status,
|
|
|
|
body: {
|
|
|
|
status: responseType,
|
|
|
|
...response
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch {
|
2024-05-15 17:55:14 +02:00
|
|
|
return internalError()
|
2024-05-15 17:39:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-15 19:03:33 +02:00
|
|
|
export function normalizeRequest(request) {
|
2024-05-15 17:39:44 +02:00
|
|
|
try {
|
|
|
|
let template = {
|
2024-08-03 19:06:32 +02:00
|
|
|
audioFormat: "mp3",
|
2024-05-15 17:39:44 +02:00
|
|
|
url: normalizeURL(decodeURIComponent(request.url)),
|
2024-08-03 19:06:32 +02:00
|
|
|
youtubeVideoCodec: "h264",
|
|
|
|
videoQuality: "720",
|
|
|
|
filenameStyle: "classic",
|
|
|
|
downloadMode: "auto",
|
|
|
|
tiktokFullAudio: false,
|
2024-05-15 17:39:44 +02:00
|
|
|
disableMetadata: false,
|
2024-08-03 19:06:32 +02:00
|
|
|
youtubeDubBrowserLang: false,
|
|
|
|
youtubeDubLang: false,
|
2024-05-15 17:39:44 +02:00
|
|
|
twitterGif: false,
|
|
|
|
tiktokH265: false
|
|
|
|
}
|
2024-08-03 09:51:09 +02:00
|
|
|
|
2024-05-15 17:39:44 +02:00
|
|
|
const requestKeys = Object.keys(request);
|
|
|
|
const templateKeys = Object.keys(template);
|
|
|
|
|
|
|
|
if (requestKeys.length > templateKeys.length + 1 || !request.url) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const i in requestKeys) {
|
|
|
|
const key = requestKeys[i];
|
|
|
|
const item = request[key];
|
|
|
|
|
|
|
|
if (String(key) !== "url" && templateKeys.includes(key)) {
|
2024-08-03 19:06:32 +02:00
|
|
|
if (apiRequest.boolean.includes(key)) {
|
2024-05-15 17:39:44 +02:00
|
|
|
template[key] = !!item;
|
2024-08-03 19:06:32 +02:00
|
|
|
} else if (apiRequest.option[key] && apiRequest.option[key].includes(item)) {
|
2024-05-15 17:39:44 +02:00
|
|
|
template[key] = String(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-03 19:06:32 +02:00
|
|
|
if (template.youtubeDubBrowserLang)
|
|
|
|
template.youtubeDubLang = verifyLanguageCode(request.youtubeDubLang);
|
2024-05-15 17:39:44 +02:00
|
|
|
|
|
|
|
return template
|
|
|
|
} catch {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getIP(req) {
|
|
|
|
const strippedIP = req.ip.replace(/^::ffff:/, '');
|
|
|
|
const ip = ipaddr.parse(strippedIP);
|
|
|
|
if (ip.kind() === 'ipv4') {
|
|
|
|
return strippedIP;
|
|
|
|
}
|
|
|
|
|
|
|
|
const prefix = 56;
|
|
|
|
const v6Bytes = ip.toByteArray();
|
|
|
|
v6Bytes.fill(0, prefix / 8);
|
|
|
|
|
|
|
|
return ipaddr.fromByteArray(v6Bytes).toString();
|
|
|
|
}
|