/* eslint-disable no-underscore-dangle */ import crypto from 'crypto'; import querystring from 'querystring'; import { sha256, uniqueRid, validateRid, createEncryptionToken, decrypt, encrypt } from "./tools" import getServiceWidget from "utils/config/service-helpers"; import { httpProxy } from "utils/proxy/http"; import createLogger from "utils/logger"; 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; } async function login(loginSecret, deviceSecret, params) { const rid = uniqueRid(); const path = `/my/connect?${querystring.stringify({ ...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({ ...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, , 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(device => device.name === deviceName); return [status, filteredDevice[0].id]; } catch (e) { logger.error("Error decoding jdownloader API data. Data: %s", data.toString()); return [status, null]; } } function createBody(rid, query, params) { const baseBody = { apiVer: 1, rid, url: query }; return params ? { ...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, , 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, null]; } } 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"); const { username } = widget const { password } = widget 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], { "bytesLoaded": true, "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, } ) let totalLoaded = 0; let totalBytes = 0; let totalSpeed = 0; packageStatus.forEach(file => { totalBytes += file.bytesTotal; totalLoaded += file.bytesLoaded; if (file.finished !== true && file.speed) { totalSpeed += file.speed; } }); const data = { downloadCount: packageStatus.length, bytesRemaining: totalBytes - totalLoaded, totalBytes, totalSpeed }; return res.send(data); }