@georgemiao/qbit.js
Version:
a qBittorrent library made with typescript
1,121 lines • 51.7 kB
JavaScript
import { stat } from "fs/promises";
import { fileFrom, FormData } from "node-fetch";
export var RawScanDirTarget;
(function (RawScanDirTarget) {
RawScanDirTarget[RawScanDirTarget["monitoredFolder"] = 0] = "monitoredFolder";
RawScanDirTarget[RawScanDirTarget["defaultSavePath"] = 1] = "defaultSavePath";
})(RawScanDirTarget || (RawScanDirTarget = {}));
export var RawMaxRatioAction;
(function (RawMaxRatioAction) {
RawMaxRatioAction[RawMaxRatioAction["pause"] = 0] = "pause";
RawMaxRatioAction[RawMaxRatioAction["remove"] = 1] = "remove";
})(RawMaxRatioAction || (RawMaxRatioAction = {}));
export var RawBittorrentProtocol;
(function (RawBittorrentProtocol) {
RawBittorrentProtocol[RawBittorrentProtocol["both"] = 0] = "both";
RawBittorrentProtocol[RawBittorrentProtocol["TCP"] = 1] = "TCP";
RawBittorrentProtocol[RawBittorrentProtocol["uTP"] = 2] = "uTP";
})(RawBittorrentProtocol || (RawBittorrentProtocol = {}));
export var RawSchedulerDays;
(function (RawSchedulerDays) {
RawSchedulerDays[RawSchedulerDays["everyDay"] = 0] = "everyDay";
RawSchedulerDays[RawSchedulerDays["everyWeekday"] = 1] = "everyWeekday";
RawSchedulerDays[RawSchedulerDays["everyWeekend"] = 2] = "everyWeekend";
RawSchedulerDays[RawSchedulerDays["everyMonday"] = 3] = "everyMonday";
RawSchedulerDays[RawSchedulerDays["everyTuesday"] = 4] = "everyTuesday";
RawSchedulerDays[RawSchedulerDays["everyWednesday"] = 5] = "everyWednesday";
RawSchedulerDays[RawSchedulerDays["everyThursday"] = 6] = "everyThursday";
RawSchedulerDays[RawSchedulerDays["everyFriday"] = 7] = "everyFriday";
RawSchedulerDays[RawSchedulerDays["everySaturday"] = 8] = "everySaturday";
RawSchedulerDays[RawSchedulerDays["everySunday"] = 9] = "everySunday";
})(RawSchedulerDays || (RawSchedulerDays = {}));
export var RawEncryptionOption;
(function (RawEncryptionOption) {
RawEncryptionOption[RawEncryptionOption["prefer"] = 0] = "prefer";
RawEncryptionOption[RawEncryptionOption["on"] = 1] = "on";
RawEncryptionOption[RawEncryptionOption["off"] = 2] = "off";
})(RawEncryptionOption || (RawEncryptionOption = {}));
export var RawProxyType;
(function (RawProxyType) {
RawProxyType[RawProxyType["disabled"] = -1] = "disabled";
RawProxyType[RawProxyType["httpWithouAuthentication"] = 1] = "httpWithouAuthentication";
RawProxyType[RawProxyType["socks5WithoutAuthentication"] = 2] = "socks5WithoutAuthentication";
RawProxyType[RawProxyType["httpWithAuthentication"] = 3] = "httpWithAuthentication";
RawProxyType[RawProxyType["socks5WithAuthentication"] = 4] = "socks5WithAuthentication";
RawProxyType[RawProxyType["socks4WithoutAuthentication"] = 5] = "socks4WithoutAuthentication";
})(RawProxyType || (RawProxyType = {}));
export var RawDynDnsService;
(function (RawDynDnsService) {
RawDynDnsService[RawDynDnsService["DyDNS"] = 0] = "DyDNS";
RawDynDnsService[RawDynDnsService["NOIP"] = 1] = "NOIP";
})(RawDynDnsService || (RawDynDnsService = {}));
export var RawUploadChokingAlgorithm;
(function (RawUploadChokingAlgorithm) {
RawUploadChokingAlgorithm[RawUploadChokingAlgorithm["roundRobin"] = 0] = "roundRobin";
RawUploadChokingAlgorithm[RawUploadChokingAlgorithm["fastestUpload"] = 1] = "fastestUpload";
RawUploadChokingAlgorithm[RawUploadChokingAlgorithm["antiLeech"] = 2] = "antiLeech";
})(RawUploadChokingAlgorithm || (RawUploadChokingAlgorithm = {}));
export var RawUploadSlotsBehavior;
(function (RawUploadSlotsBehavior) {
RawUploadSlotsBehavior[RawUploadSlotsBehavior["fixedSlots"] = 0] = "fixedSlots";
RawUploadSlotsBehavior[RawUploadSlotsBehavior["uploadRateBased"] = 1] = "uploadRateBased";
})(RawUploadSlotsBehavior || (RawUploadSlotsBehavior = {}));
export var RawUtpTcpMixedMode;
(function (RawUtpTcpMixedMode) {
RawUtpTcpMixedMode[RawUtpTcpMixedMode["preferTcp"] = 0] = "preferTcp";
RawUtpTcpMixedMode[RawUtpTcpMixedMode["peerProportional"] = 1] = "peerProportional";
})(RawUtpTcpMixedMode || (RawUtpTcpMixedMode = {}));
export var RawLogEntryType;
(function (RawLogEntryType) {
RawLogEntryType[RawLogEntryType["normal"] = 1] = "normal";
RawLogEntryType[RawLogEntryType["info"] = 2] = "info";
RawLogEntryType[RawLogEntryType["warning"] = 4] = "warning";
RawLogEntryType[RawLogEntryType["critical"] = 8] = "critical";
})(RawLogEntryType || (RawLogEntryType = {}));
export var RawTorrentState;
(function (RawTorrentState) {
RawTorrentState["error"] = "error";
RawTorrentState["missingFiles"] = "missingFiles";
RawTorrentState["uploading"] = "uploading";
RawTorrentState["pausedUP"] = "pausedUP";
RawTorrentState["queuedUP"] = "queuedUP";
RawTorrentState["stalledUP"] = "stalledUP";
RawTorrentState["checkingUP"] = "checkingUP";
RawTorrentState["forcedUP"] = "forcedUP";
RawTorrentState["allocating"] = "allocating";
RawTorrentState["downloading"] = "downloading";
RawTorrentState["metaDL"] = "metaDL";
RawTorrentState["pausedDL"] = "pausedDL";
RawTorrentState["queuedDL"] = "queuedDL";
RawTorrentState["stalledDL"] = "stalledDL";
RawTorrentState["checkingDL"] = "checkingDL";
RawTorrentState["forcedDL"] = "forcedDL";
RawTorrentState["checkingResumeData"] = "checkingResumeData";
RawTorrentState["moving"] = "moving";
RawTorrentState["unknown"] = "unknown";
})(RawTorrentState || (RawTorrentState = {}));
export var RawConnectionStatus;
(function (RawConnectionStatus) {
RawConnectionStatus["connected"] = "connected";
RawConnectionStatus["firewalled"] = "firewalled";
RawConnectionStatus["disconnected"] = "disconnected";
})(RawConnectionStatus || (RawConnectionStatus = {}));
export var RawConnectionType;
(function (RawConnectionType) {
RawConnectionType["uTP"] = "\u03BCTP";
RawConnectionType["BT"] = "BT";
})(RawConnectionType || (RawConnectionType = {}));
export var RawTorrentListFilter;
(function (RawTorrentListFilter) {
RawTorrentListFilter["all"] = "all";
RawTorrentListFilter["downloading"] = "downloading";
RawTorrentListFilter["seeding"] = "seeding";
RawTorrentListFilter["completed"] = "completed";
RawTorrentListFilter["paused"] = "paused";
RawTorrentListFilter["active"] = "active";
RawTorrentListFilter["inactive"] = "inactive";
RawTorrentListFilter["resumed"] = "resumed";
RawTorrentListFilter["stalled"] = "stalled";
RawTorrentListFilter["stalled_uploading"] = "stalled_uploading";
RawTorrentListFilter["stalled_downloading"] = "stalled_downloading";
RawTorrentListFilter["errored"] = "errored";
})(RawTorrentListFilter || (RawTorrentListFilter = {}));
export var RawTorrentSortKey;
(function (RawTorrentSortKey) {
RawTorrentSortKey["added_on"] = "added_on";
RawTorrentSortKey["amount_left"] = "amount_left";
RawTorrentSortKey["auto_tmm"] = "auto_tmm";
RawTorrentSortKey["availability"] = "availability";
RawTorrentSortKey["category"] = "category";
RawTorrentSortKey["completed"] = "completed";
RawTorrentSortKey["completion_on"] = "completion_on";
RawTorrentSortKey["content_path"] = "content_path";
RawTorrentSortKey["dl_limit"] = "dl_limit";
RawTorrentSortKey["dlspeed"] = "dlspeed";
RawTorrentSortKey["downloaded"] = "downloaded";
RawTorrentSortKey["downloaded_session"] = "downloaded_session";
RawTorrentSortKey["eta"] = "eta";
RawTorrentSortKey["f_l_piece_prio"] = "f_l_piece_prio";
RawTorrentSortKey["force_start"] = "force_start";
RawTorrentSortKey["hash"] = "hash";
RawTorrentSortKey["last_activity"] = "last_activity";
RawTorrentSortKey["magnet_uri"] = "magnet_uri";
RawTorrentSortKey["max_ratio"] = "max_ratio";
RawTorrentSortKey["max_seeding_time"] = "max_seeding_time";
RawTorrentSortKey["name"] = "name";
RawTorrentSortKey["num_complete"] = "num_complete";
RawTorrentSortKey["num_incomplete"] = "num_incomplete";
RawTorrentSortKey["num_leechs"] = "num_leechs";
RawTorrentSortKey["num_seeds"] = "num_seeds";
RawTorrentSortKey["priority"] = "priority";
RawTorrentSortKey["progress"] = "progress";
RawTorrentSortKey["ratio"] = "ratio";
RawTorrentSortKey["ratio_limit"] = "ratio_limit";
RawTorrentSortKey["save_path"] = "save_path";
RawTorrentSortKey["seeding_time"] = "seeding_time";
RawTorrentSortKey["seeding_time_limit"] = "seeding_time_limit";
RawTorrentSortKey["seen_complete"] = "seen_complete";
RawTorrentSortKey["seq_dl"] = "seq_dl";
RawTorrentSortKey["size"] = "size";
RawTorrentSortKey["state"] = "state";
RawTorrentSortKey["super_seeding"] = "super_seeding";
RawTorrentSortKey["tags"] = "tags";
RawTorrentSortKey["time_active"] = "time_active";
RawTorrentSortKey["total_size"] = "total_size";
RawTorrentSortKey["tracker"] = "tracker";
RawTorrentSortKey["up_limit"] = "up_limit";
RawTorrentSortKey["uploaded"] = "uploaded";
RawTorrentSortKey["uploaded_session"] = "uploaded_session";
RawTorrentSortKey["upspeed"] = "upspeed";
})(RawTorrentSortKey || (RawTorrentSortKey = {}));
export var RawTrackerStatus;
(function (RawTrackerStatus) {
RawTrackerStatus[RawTrackerStatus["disabled"] = 0] = "disabled";
RawTrackerStatus[RawTrackerStatus["notContacted"] = 1] = "notContacted";
RawTrackerStatus[RawTrackerStatus["working"] = 2] = "working";
RawTrackerStatus[RawTrackerStatus["updating"] = 3] = "updating";
RawTrackerStatus[RawTrackerStatus["notWorking"] = 4] = "notWorking";
})(RawTrackerStatus || (RawTrackerStatus = {}));
export var RawTorrentFilePriority;
(function (RawTorrentFilePriority) {
RawTorrentFilePriority[RawTorrentFilePriority["doNotDownload"] = 0] = "doNotDownload";
RawTorrentFilePriority[RawTorrentFilePriority["normal"] = 1] = "normal";
RawTorrentFilePriority[RawTorrentFilePriority["high"] = 6] = "high";
RawTorrentFilePriority[RawTorrentFilePriority["maximal"] = 7] = "maximal";
})(RawTorrentFilePriority || (RawTorrentFilePriority = {}));
export var RawPieceState;
(function (RawPieceState) {
RawPieceState[RawPieceState["notDownloaded"] = 0] = "notDownloaded";
RawPieceState[RawPieceState["nowDownloading"] = 1] = "nowDownloading";
RawPieceState[RawPieceState["alreadyDownloaded"] = 2] = "alreadyDownloaded";
})(RawPieceState || (RawPieceState = {}));
export var RawSearchStatusType;
(function (RawSearchStatusType) {
RawSearchStatusType["Stopped"] = "Stopped";
RawSearchStatusType["Running"] = "Running";
})(RawSearchStatusType || (RawSearchStatusType = {}));
function safeURL(str) {
let url;
try {
url = new URL(str);
}
catch (e) {
return null;
}
return url;
}
export class Api {
constructor(qbit) {
this.qbit = qbit;
}
async login(username, password) {
const res = await this.qbit.fetch("auth/login", {
method: "POST",
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 403)
throw new Error("IP Banned");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
const session = res.headers.get("set-cookie")?.split(";")?.[0];
if (session === null || session === undefined) {
throw new Error("Invalid credentials");
}
return session;
}
async logout() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("auth/logout");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getApplicationVersion() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/version");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.text();
}
async getApiVersion() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/webapiVersion");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.text();
}
async getBuildInfo() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/buildInfo");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async shutdown() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/shutdown");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getPreferences() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/preferences");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async setPreferences(preferences) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/preferences", {
method: "POST",
body: `json=${encodeURIComponent(JSON.stringify(preferences))}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getDefaultSavePath() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("app/defaultSavePath");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.text();
}
async getLog(opts = {}) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`log/main?${Object.entries(opts)
.map(([key, val]) => `${key}=${encodeURIComponent(val)}`)
.join("&")}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getPeerLog(last_known_id) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`log/peers${last_known_id ? `?last_known_id=${last_known_id}` : ""}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getMainData(rid) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`sync/maindata${rid ? `?rid=${rid}` : ""}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentPeers(hash, rid) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`sync/torrentPeers?hash=${encodeURIComponent(hash)}${rid ? `&rid=${rid}` : ""}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTransferInfo() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/info");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async isAlternativeSpeedLimitEnabled() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/speedLimitsMode");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return (await res.text()) === "1";
}
async toggleAlternativeSpeedLimit() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`transfer/toggleSpeedLimitsMode`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getGlobalDownloadLimit() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/downloadLimit");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return parseFloat(await res.text());
}
async setGlobalDownloadLimit(limit) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/setDownloadLimit", {
method: "POST",
body: `limit=${Math.round(limit)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getGlobalUploadLimit() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/uploadLimit");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return parseFloat(await res.text());
}
async setGlobalUploadLimit(limit) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/setUploadLimit", {
method: "POST",
body: `limit=${Math.round(limit)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async banPeers(peers) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("transfer/banPeers", {
method: "POST",
body: `peers=${encodeURIComponent(peers.join("|"))}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getTorrents(opts = {}) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/info?${Object.entries(opts)
.map(([key, val]) => `${key}=${encodeURIComponent(val)}`)
.join("&")}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrent(hash) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/properties?hash=${encodeURIComponent(hash)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentTrackers(hash) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/trackers?hash=${encodeURIComponent(hash)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentWebSeeds(hash) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/webseeds?hash=${encodeURIComponent(hash)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentFiles(hash, indexes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/files?hash=${encodeURIComponent(hash)}${indexes ? `&indexes=${encodeURIComponent(indexes.join("|"))}` : ""}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentPieceStates(hash) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/pieceStates?hash=${encodeURIComponent(hash)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getTorrentPieceHashes(hash) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/pieceHashes?hash=${encodeURIComponent(hash)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async pauseTorrents(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/pause?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async resumeTorrents(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/resume?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async deleteTorrents(hashes, deleteFiles = false) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/delete?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&deleteFiles=${deleteFiles}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async recheckTorrents(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/recheck?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async reannounceTorrents(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/reannounce?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addTorrent(torrent, opts) {
await this.qbit.checkLogin();
const data = new FormData();
const torrents = Array.isArray(torrent) ? torrent : [torrent];
const links = [];
for (const torrent of torrents) {
if (Buffer.isBuffer(torrent)) {
data.append("torrents", new File([torrent], "upload.torrent", {
type: "application/x-bittorrent",
}), "upload.torrent");
}
else {
const parsed = safeURL(torrent);
if (parsed && ["http:", "https:", "magnet:"].includes(parsed.protocol)) {
links.push(torrent);
}
else {
const res = await stat(torrent).catch(() => null);
if (res)
data.append("torrents", await fileFrom(torrent, "application/x-bittorrent"));
else
throw Error(`Invalid torrent string "${torrent}"`);
}
}
}
if (links.length > 0)
data.set("urls", links.join("\n"));
if (opts) {
for (const [key, val] of Object.entries(opts)) {
if (key === "tags")
data.set(key, val.join(","));
else
data.set(key, val.toString());
}
}
// fix for bullshit formdata bugs
data.set("fix", "fix");
const res = await this.qbit.fetch("torrents/add", {
method: "POST",
body: data,
});
if (res.status === 415)
throw new Error("Torrent file not valid");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addTorrentTrackers(hash, trackers) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/addTrackers?hash=${encodeURIComponent(hash)}&urls=${encodeURIComponent(Array.isArray(trackers) ? trackers.join("\n") : trackers)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async editTorrentTrackers(hash, origUrl, newUrl) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/editTrackers?hash=${encodeURIComponent(hash)}&origUrl=${encodeURIComponent(origUrl)}&newUrl=${encodeURIComponent(newUrl)}`);
if (res.status === 400)
throw new Error("New url is not valid");
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status === 409)
throw new Error("Old url is not used or new one is already used");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async removeTorrentTracker(hash, trackers) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/removeTrackers?hash=${encodeURIComponent(hash)}&urls=${encodeURIComponent(Array.isArray(trackers) ? trackers.join("\n") : trackers)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status === 409)
throw new Error("Trackers not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addTorrentPeers(hashes, peers) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/addPeers?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&peers=${encodeURIComponent(Array.isArray(peers) ? peers.join("|") : peers)}`);
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async increaseTorrentPriority(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/increasePrio?hashes=${encodeURIComponent(hashes)}`);
if (res.status === 409)
throw new Error("Torrent queueing is not enabled");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async decreaseTorrentPriority(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/decreasePrio?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status === 409)
throw new Error("Torrent queueing is not enabled");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTopPriority(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/topPrio?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status === 409)
throw new Error("Torrent queueing is not enabled");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setBottomPriority(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/bottomPrio?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status === 409)
throw new Error("Torrent queueing is not enabled");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentFilePriority(hash, files, priority) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/filePrio?hash=${encodeURIComponent(hash)}&id=${encodeURIComponent(files.join("|"))}&priority=${priority}`);
if (res.status === 400)
throw new Error("Invalid file index or priority");
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status === 409)
throw new Error("File id not found or torrent metadata not yet downloaded");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getTorrentDownloadLimit(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/downloadLimit?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async setTorrentDownloadLimit(hashes, limit) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/setDownloadLimit`, {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&limit=${limit}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentShareLimits(opts) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setShareLimits", {
method: "POST",
body: Object.entries(opts)
.map(([key, val]) => `${key}=${encodeURIComponent(Array.isArray(val) ? val.join("|") : val)}`)
.join("&"),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getTorrentUploadLimit(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/uploadLimit?hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async setTorrentUploadLimit(hashes, limit) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`torrents/setUploadLimit`, {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&limit=${limit}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentLocation(hashes, location) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setLocation", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&location=${encodeURIComponent(location)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 400)
throw new Error("Invalid location");
if (res.status === 403)
throw new Error("No write perms to direcotry");
if (res.status === 409)
throw new Error("Unable to create directory");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentName(hash, name) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/rename", {
method: "POST",
body: `hash=${encodeURIComponent(hash)}&name=${encodeURIComponent(name)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 404)
throw new Error("Torrent not found");
if (res.status === 409)
throw new Error("Torrent name is invalid");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentCategory(hashes, category) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setCategory", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&category=${encodeURIComponent(category)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Torrent category is invalid");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getCategories() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/categories");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async createCategory(name, savePath = "") {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/createCategory", {
method: "POST",
body: `category=${encodeURIComponent(name)}&savePath=${encodeURIComponent(savePath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 400 || res.status === 409)
throw new Error("Category name is invalid");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async editCategory(name, savePath) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/editCategory", {
method: "POST",
body: `category=${encodeURIComponent(name)}&savePath=${encodeURIComponent(savePath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 400)
throw new Error("Category name is invalid");
if (res.status === 409)
throw new Error("Category editing failed");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async removeCategories(names) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/removeCategories", {
method: "POST",
body: `categories=${encodeURIComponent(Array.isArray(names) ? names.join("\n") : names)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addTorrentTags(hashes, tags) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/addTags", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&tags=${encodeURIComponent(Array.isArray(tags) ? tags.join(",") : tags)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async removeTorrentTags(hashes, tags) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/removeTags", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&tags=${encodeURIComponent(Array.isArray(tags) ? tags.join(",") : tags)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getTags() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/tags");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async createTags(names) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/createTags", {
method: "POST",
body: `tags=${encodeURIComponent(Array.isArray(names) ? names.join(",") : names)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async deleteTags(names) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/deleteTags", {
method: "POST",
body: `tags=${encodeURIComponent(Array.isArray(names) ? names.join(",") : names)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentAutoManagement(hashes, enabled) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setAutoManagement", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&enabled=${enabled}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async toggleTorrentSequentialDownload(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/toggleSequentialDownload", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async toggleTorrentFirstLastPiecePriority(hashes) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/toggleFirstLastPiecePrio", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentForceStart(hashes, enabled) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setForceStart", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&value=${enabled}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setTorrentSuperSeeding(hashes, enabled) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/setSuperSeeding", {
method: "POST",
body: `hashes=${encodeURIComponent(Array.isArray(hashes) ? hashes.join("|") : hashes)}&value=${enabled}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async renameTorrentFile(hash, oldPath, newPath) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/renameFile", {
method: "POST",
body: `hash=${encodeURIComponent(hash)}&oldPath=${encodeURIComponent(oldPath)}&newPath=${encodeURIComponent(newPath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 400 || res.status === 409)
throw new Error("Invalid path");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async renameTorrentFolder(hash, oldPath, newPath) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("torrents/renameFolder", {
method: "POST",
body: `hash=${encodeURIComponent(hash)}&oldPath=${encodeURIComponent(oldPath)}&newPath=${encodeURIComponent(newPath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 400 || res.status === 409)
throw new Error("Invalid path");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addRssFolder(path) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/addFolder", {
method: "POST",
body: `path=${encodeURIComponent(path)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Failed to add folder");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async addRssFeed(url, path) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/addFeed", {
method: "POST",
body: `url=${encodeURIComponent(url)}${path ? `&path=${encodeURIComponent(path)}` : ""}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Failed to add feed");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async removeRssItem(path) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/removeItem", {
method: "POST",
body: `path=${encodeURIComponent(path)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Failed to remove item");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async moveRssItem(itemPath, destPath) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/moveItem", {
method: "POST",
body: `itemPath=${encodeURIComponent(itemPath)}&destPath=${encodeURIComponent(destPath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Failed to move item");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getRssItems(withdata = false) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`rss/items?withdata=${withdata}`);
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async markRssAsRead(itemPath, articleId) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/markAsRead", {
method: "POST",
body: `itemPath=${encodeURIComponent(itemPath)}${articleId ? `&articleId=${encodeURIComponent(articleId)}` : ""}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async refreshRssItem(itemPath) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/refreshItem", {
method: "POST",
body: `itemPath=${encodeURIComponent(itemPath)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async setRssRule(ruleName, ruleDef) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/setRule", {
method: "POST",
body: `ruleName=${encodeURIComponent(ruleName)}&ruleDef=${encodeURIComponent(JSON.stringify(ruleDef))}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async renameRssRule(ruleName, newRuleName) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/renameRule", {
method: "POST",
body: `ruleName=${encodeURIComponent(ruleName)}&newRuleName=${encodeURIComponent(newRuleName)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async removeRssRule(ruleName) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/removeRule", {
method: "POST",
body: `ruleName=${encodeURIComponent(ruleName)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getRssRules() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/rules");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getRssRuleArticles(ruleName) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("rss/matchingArticles", {
method: "POST",
body: `ruleName=${encodeURIComponent(ruleName)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async startSearch(query, plugins, category) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("search/start", {
method: "POST",
body: `pattern=${encodeURIComponent(query)}&plugins=${encodeURIComponent(Array.isArray(plugins) ? plugins.join("|") : plugins)}&category=${encodeURIComponent(category)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 409)
throw new Error("Reached max number of concurrent queries");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
const { id } = (await res.json());
return id;
}
async stopSearch(id) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("search/stop", {
method: "POST",
body: `id=${encodeURIComponent(id)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 404)
throw new Error("Search not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getSearchStatus(id) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`search/status${id ? `?id=${encodeURIComponent(id)}` : ""}`);
if (res.status === 404)
throw new Error("Search not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async getSearchResults(opts) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch(`search/results?${Object.entries(opts)
.map(([key, val]) => `${key}=${encodeURIComponent(val)}`)
.join("&")}`);
if (res.status === 404)
throw new Error("Search not found");
if (res.status === 409)
throw new Error("Offset out of range");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async deleteSearch(id) {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("search/delete", {
method: "POST",
body: `id=${encodeURIComponent(id)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (res.status === 404)
throw new Error("Search not found");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
}
async getSearchPlugins() {
await this.qbit.checkLogin();
const res = await this.qbit.fetch("search/plugins");
if (res.status !== 200)
throw new Error(`Unexpected status "${res.status}"`);
return res.json();
}
async installSearchPlugin(sources) {
await this.qbit.checkLogin();
const