194 lines
6.0 KiB
JavaScript
Raw Normal View History

2023-06-07 12:47:49 +01:00
/* eslint-disable no-underscore-dangle */
2023-06-13 18:11:53 +01:00
import crypto from 'crypto';
import querystring from 'querystring';
2023-06-07 12:47:49 +01:00
import getServiceWidget from "utils/config/service-helpers";
2023-06-13 18:11:53 +01:00
import { httpProxy } from "utils/proxy/http";
2023-06-07 12:47:49 +01:00
import createLogger from "utils/logger";
2023-06-13 18:11:53 +01:00
import { sha256, uniqueRid, validateRid, createEncryptionToken, decrypt, encrypt } from "./tools"
2023-06-07 12:47:49 +01:00
const proxyName = "jdownloaderProxyHandler";
const logger = createLogger(proxyName);
async function getWidget(req) {
const { group, service } = req.query;
if (!group || !service) {
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
return null;
}
const widget = await getServiceWidget(group, service);
if (!widget) {
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
return null;
}
return widget;
}
2023-06-13 18:11:53 +01:00
async function login(loginSecret, deviceSecret, params) {
const rid = uniqueRid();
const path = '/my/connect' +
'?' +
querystring.stringify(Object.assign({}, params, { rid }));
const signature = crypto
.createHmac('sha256', loginSecret)
.update(path)
.digest('hex');
const url = `${new URL(`https://api.jdownloader.org${path}&signature=${signature}`)}`
const [status, contentType, data] = await httpProxy(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
if (status !== 200) {
logger.error("HTTP %d communicating with jdownloader. Data: %s", status, data.toString());
return [status, data];
}
try {
const decryptedData = JSON.parse(decrypt(data.toString(), loginSecret))
const sessionToken = decryptedData.sessiontoken;
validateRid(decryptedData, rid);
const serverEncryptionToken = createEncryptionToken(loginSecret, sessionToken);
const deviceEncryptionToken = createEncryptionToken(deviceSecret, sessionToken);
return [status, decryptedData, contentType, serverEncryptionToken, deviceEncryptionToken, sessionToken];
} catch (e) {
logger.error("Error decoding jdownloader API data. Data: %s", data.toString());
return [status, null];
}
}
async function getDevice(serverEncryptionToken, deviceName, params) {
const rid = uniqueRid();
const path = '/my/listdevices' +
'?' +
querystring.stringify(Object.assign({}, params, { rid }));
const signature = crypto
.createHmac('sha256', serverEncryptionToken)
.update(path)
.digest('hex');
const url = `${new URL(`https://api.jdownloader.org${path}&signature=${signature}`)}`
const [status, contentType, data] = await httpProxy(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
if (status !== 200) {
logger.error("HTTP %d communicating with jdownloader. Data: %s", status, data.toString());
return [status, data];
}
try {
const decryptedData = JSON.parse(decrypt(data.toString(), serverEncryptionToken))
const filteredDevice = decryptedData.list.filter(function (device) {
return device.name == deviceName;
});
return [status, filteredDevice[0].id];
} catch (e) {
logger.error("Error decoding jdownloader API data. Data: %s", data.toString());
return [status, None];
}
}
function createBody(rid, query, params) {
const baseBody = {
apiVer: 1,
rid,
url: query
};
return params ? Object.assign({}, baseBody, { params: [JSON.stringify(params)] }) : baseBody;
}
async function queryPackages(deviceEncryptionToken, deviceId, sessionToken, params) {
const rid = uniqueRid();
const body = encrypt(JSON.stringify(createBody(rid, '/downloadsV2/queryPackages', params)), deviceEncryptionToken);
const url = `${new URL('https://api.jdownloader.org/t_' + encodeURI(sessionToken) + '_' + encodeURI(deviceId) + '/downloadsV2/queryPackages')}`
const [status, contentType, data] = await httpProxy(url, {
method: 'POST',
body,
});
if (status !== 200) {
logger.error("HTTP %d communicating with jdownloader. Data: %s", status, data.toString());
return [status, data];
}
try {
const decryptedData = JSON.parse(decrypt(data.toString(), deviceEncryptionToken))
return decryptedData.data;
} catch (e) {
logger.error("Error decoding JDRss jdownloader data. Data: %s", data.toString());
return [status, None];
}
}
2023-06-07 12:47:49 +01:00
export default async function jdownloaderProxyHandler(req, res) {
const widget = await getWidget(req);
if (!widget) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
logger.debug("Getting data from JDRss API");
2023-06-13 18:11:53 +01:00
const username = widget.username
const password = widget.password
const appKey = "homepage"
const loginSecret = sha256(username + password + 'server')
const deviceSecret = sha256(username + password + 'device')
const email = username;
const loginData = await login(loginSecret, deviceSecret, {
appKey,
email
})
const deviceData = await getDevice(loginData[3], widget.client, {
sessiontoken: loginData[5]
})
const packageStatus = await queryPackages(loginData[4], deviceData[1], loginData[5], {
2023-06-07 12:47:49 +01:00
"bytesLoaded": false,
"bytesTotal": true,
"comment": false,
"enabled": true,
"eta": false,
"priority": false,
"finished": true,
"running": true,
"speed": true,
"status": true,
"childCount": false,
"hosts": false,
"saveTo": false,
"maxResults": -1,
"startAt": 0,
2023-06-13 18:11:53 +01:00
}
)
2023-06-07 12:47:49 +01:00
let totalBytes = 0;
let totalSpeed = 0;
packageStatus.forEach(file => {
totalBytes += file.bytesTotal;
if (file.speed) {
totalSpeed += file.speed;
}
});
const data = {
downloadCount: packageStatus.length,
totalBytes,
totalSpeed
};
return res.send(data);
}