/* 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); }