@ctrl/qbittorrent 2.9.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export * from './types.js';
2
+ export * from './qbittorrent.js';
@@ -0,0 +1,2 @@
1
+ export * from './types.js';
2
+ export * from './qbittorrent.js';
@@ -1,8 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { Options as GotOptions, Response } from 'got';
3
- import { AddTorrentOptions as NormalizedAddTorrentOptions, TorrentClient, TorrentSettings } from '@ctrl/shared-torrent';
4
- import { AddMagnetOptions, AddTorrentOptions, AllClientDataQbittorrent, BuildInfo, Categories, NormalizedTorrentQbittorrent, Preferences, Torrent, TorrentFile, TorrentFilePriority, TorrentFilters, TorrentPieceState, TorrentProperties, TorrentState, TorrentTrackers, TorrentTrackerStatus, WebSeed } from './types';
5
- export { TorrentState, TorrentTrackerStatus, TorrentFilePriority, TorrentPieceState };
3
+ import { AddTorrentOptions as NormalizedAddTorrentOptions, AllClientData, NormalizedTorrent, TorrentClient, TorrentSettings } from '@ctrl/shared-torrent';
4
+ import { AddMagnetOptions, AddTorrentOptions, BuildInfo, Preferences, Torrent, TorrentCategories, TorrentFile, TorrentFilePriority, TorrentFilters, TorrentPieceState, TorrentProperties, TorrentTrackers, WebSeed } from './types.js';
6
5
  export declare class QBittorrent implements TorrentClient {
7
6
  config: TorrentSettings;
8
7
  /**
@@ -28,7 +27,7 @@ export declare class QBittorrent implements TorrentClient {
28
27
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-build-info}
29
28
  */
30
29
  getBuildInfo(): Promise<BuildInfo>;
31
- getTorrent(hash: string): Promise<NormalizedTorrentQbittorrent>;
30
+ getTorrent(hash: string): Promise<NormalizedTorrent>;
32
31
  /**
33
32
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-application-preferences}
34
33
  */
@@ -44,8 +43,16 @@ export declare class QBittorrent implements TorrentClient {
44
43
  * @param category Get torrents with the given category (empty string means "without category"; no "category" parameter means "any category")
45
44
  * @returns list of torrents
46
45
  */
47
- listTorrents(hashes?: string | string[], filter?: TorrentFilters, category?: string, sort?: string, offset?: number, reverse?: boolean): Promise<Torrent[]>;
48
- getAllData(): Promise<AllClientDataQbittorrent>;
46
+ listTorrents({ hashes, filter, category, sort, offset, reverse, tag, }?: {
47
+ hashes?: string | string[];
48
+ filter?: TorrentFilters;
49
+ sort?: string;
50
+ tag?: string;
51
+ category?: string;
52
+ offset?: number;
53
+ reverse?: boolean;
54
+ }): Promise<Torrent[]>;
55
+ getAllData(): Promise<AllClientData>;
49
56
  /**
50
57
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-torrent-generic-properties}
51
58
  */
@@ -95,7 +102,7 @@ export declare class QBittorrent implements TorrentClient {
95
102
  /**
96
103
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-all-categories}
97
104
  */
98
- getCategories(): Promise<Categories>;
105
+ getCategories(): Promise<TorrentCategories>;
99
106
  /**
100
107
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#add-new-category}
101
108
  */
@@ -146,7 +153,7 @@ export declare class QBittorrent implements TorrentClient {
146
153
  */
147
154
  reannounceTorrent(hashes: string | string[] | 'all'): Promise<boolean>;
148
155
  addTorrent(torrent: string | Buffer, options?: Partial<AddTorrentOptions>): Promise<boolean>;
149
- normalizedAddTorrent(torrent: string | Buffer, options?: Partial<NormalizedAddTorrentOptions>): Promise<NormalizedTorrentQbittorrent>;
156
+ normalizedAddTorrent(torrent: string | Buffer, options?: Partial<NormalizedAddTorrentOptions>): Promise<NormalizedTorrent>;
150
157
  /**
151
158
  * @param hash Hash for desired torrent
152
159
  * @param id id of the file to be renamed
@@ -191,7 +198,7 @@ export declare class QBittorrent implements TorrentClient {
191
198
  */
192
199
  login(): Promise<boolean>;
193
200
  logout(): boolean;
194
- request<T extends object | string>(path: string, method: GotOptions['method'], params?: any, body?: GotOptions['body'], headers?: any, json?: boolean): Promise<Response<T>>;
201
+ request<T extends object | string>(path: string, method: GotOptions['method'], params?: any, body?: GotOptions['body'], form?: GotOptions['form'], headers?: any, json?: boolean): Promise<Response<T>>;
195
202
  /**
196
203
  * Normalizes hashes
197
204
  * @returns hashes as string seperated by `|`
@@ -1,23 +1,15 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.QBittorrent = exports.TorrentPieceState = exports.TorrentFilePriority = exports.TorrentTrackerStatus = exports.TorrentState = void 0;
7
1
  /* eslint-disable @typescript-eslint/ban-types */
8
- const fs_1 = require("fs");
9
- const url_1 = require("url");
10
- const form_data_1 = __importDefault(require("form-data"));
11
- const got_1 = __importDefault(require("got"));
12
- const tough_cookie_1 = require("tough-cookie");
13
- const shared_torrent_1 = require("@ctrl/shared-torrent");
14
- const torrent_file_1 = require("@ctrl/torrent-file");
15
- const url_join_1 = require("@ctrl/url-join");
16
- const types_1 = require("./types");
17
- Object.defineProperty(exports, "TorrentFilePriority", { enumerable: true, get: function () { return types_1.TorrentFilePriority; } });
18
- Object.defineProperty(exports, "TorrentPieceState", { enumerable: true, get: function () { return types_1.TorrentPieceState; } });
19
- Object.defineProperty(exports, "TorrentState", { enumerable: true, get: function () { return types_1.TorrentState; } });
20
- Object.defineProperty(exports, "TorrentTrackerStatus", { enumerable: true, get: function () { return types_1.TorrentTrackerStatus; } });
2
+ import { existsSync } from 'fs';
3
+ import { URLSearchParams } from 'url';
4
+ import { File, FormData } from 'formdata-node';
5
+ import { fileFromPath } from 'formdata-node/file-from-path';
6
+ import got from 'got';
7
+ import { Cookie } from 'tough-cookie';
8
+ import { magnetDecode } from '@ctrl/magnet-link';
9
+ import { TorrentState as NormalizedTorrentState, } from '@ctrl/shared-torrent';
10
+ import { hash } from '@ctrl/torrent-file';
11
+ import { urlJoin } from '@ctrl/url-join';
12
+ import { TorrentState, } from './types.js';
21
13
  const defaults = {
22
14
  baseUrl: 'http://localhost:9091/',
23
15
  path: '/api/v2',
@@ -25,8 +17,32 @@ const defaults = {
25
17
  password: '',
26
18
  timeout: 5000,
27
19
  };
28
- class QBittorrent {
20
+ export class QBittorrent {
29
21
  constructor(options = {}) {
22
+ Object.defineProperty(this, "config", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ /**
29
+ * auth cookie
30
+ */
31
+ Object.defineProperty(this, "_sid", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: void 0
36
+ });
37
+ /**
38
+ * cookie expiration
39
+ */
40
+ Object.defineProperty(this, "_exp", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: void 0
45
+ });
30
46
  this.config = { ...defaults, ...options };
31
47
  }
32
48
  /**
@@ -40,11 +56,11 @@ class QBittorrent {
40
56
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-application-version}
41
57
  */
42
58
  async getAppVersion() {
43
- const res = await this.request('/app/version', 'GET', undefined, undefined, undefined, false);
59
+ const res = await this.request('/app/version', 'GET', undefined, undefined, undefined, undefined, false);
44
60
  return res.body;
45
61
  }
46
62
  async getApiVersion() {
47
- const res = await this.request('/app/webapiVersion', 'GET', undefined, undefined, undefined, false);
63
+ const res = await this.request('/app/webapiVersion', 'GET', undefined, undefined, undefined, undefined, false);
48
64
  return res.body;
49
65
  }
50
66
  /**
@@ -55,7 +71,7 @@ class QBittorrent {
55
71
  return res.body;
56
72
  }
57
73
  async getTorrent(hash) {
58
- const torrentsResponse = await this.listTorrents(hash);
74
+ const torrentsResponse = await this.listTorrents({ hashes: hash });
59
75
  const torrentData = torrentsResponse[0];
60
76
  if (!torrentData) {
61
77
  throw new Error('Torrent not found');
@@ -73,9 +89,9 @@ class QBittorrent {
73
89
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#set-application-preferences}
74
90
  */
75
91
  async setPreferences(preferences) {
76
- const form = new form_data_1.default();
77
- form.append('json', JSON.stringify(preferences));
78
- await this.request('/app/setPreferences', 'POST', undefined, form);
92
+ await this.request('/app/setPreferences', 'POST', undefined, undefined, {
93
+ json: JSON.stringify(preferences),
94
+ });
79
95
  return true;
80
96
  }
81
97
  /**
@@ -85,8 +101,7 @@ class QBittorrent {
85
101
  * @param category Get torrents with the given category (empty string means "without category"; no "category" parameter means "any category")
86
102
  * @returns list of torrents
87
103
  */
88
- // eslint-disable-next-line max-params
89
- async listTorrents(hashes, filter, category, sort, offset, reverse) {
104
+ async listTorrents({ hashes, filter, category, sort, offset, reverse, tag, } = {}) {
90
105
  const params = {};
91
106
  if (hashes) {
92
107
  params.hashes = this._normalizeHashes(hashes);
@@ -97,6 +112,9 @@ class QBittorrent {
97
112
  if (category) {
98
113
  params.category = category;
99
114
  }
115
+ if (tag) {
116
+ params.tag = tag;
117
+ }
100
118
  if (offset !== undefined) {
101
119
  params.offset = `${offset}`;
102
120
  }
@@ -188,20 +206,20 @@ class QBittorrent {
188
206
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#set-torrent-location}
189
207
  */
190
208
  async setTorrentLocation(hashes, location) {
191
- const form = new form_data_1.default();
192
- form.append('location', location);
193
- form.append('hashes', this._normalizeHashes(hashes));
194
- await this.request('/torrents/setLocation', 'POST', undefined, form);
209
+ await this.request('/torrents/setLocation', 'POST', undefined, undefined, {
210
+ location,
211
+ hashes: this._normalizeHashes(hashes),
212
+ });
195
213
  return true;
196
214
  }
197
215
  /**
198
216
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#set-torrent-name}
199
217
  */
200
218
  async setTorrentName(hash, name) {
201
- const form = new form_data_1.default();
202
- form.append('hash', hash);
203
- form.append('name', name);
204
- await this.request('/torrents/rename', 'POST', undefined, form);
219
+ await this.request('/torrents/rename', 'POST', undefined, undefined, {
220
+ hash,
221
+ name,
222
+ });
205
223
  return true;
206
224
  }
207
225
  /**
@@ -216,9 +234,9 @@ class QBittorrent {
216
234
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#create-tags}
217
235
  */
218
236
  async createTags(tags) {
219
- const form = new form_data_1.default();
220
- form.append('tags', tags);
221
- await this.request('/torrents/createTags', 'POST', undefined, form, undefined, false);
237
+ await this.request('/torrents/createTags', 'POST', undefined, undefined, {
238
+ tags,
239
+ }, undefined, false);
222
240
  return true;
223
241
  }
224
242
  /**
@@ -226,9 +244,7 @@ class QBittorrent {
226
244
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#delete-tags}
227
245
  */
228
246
  async deleteTags(tags) {
229
- const form = new form_data_1.default();
230
- form.append('tags', tags);
231
- await this.request('/torrents/deleteTags', 'POST', undefined, form, undefined, false);
247
+ await this.request('/torrents/deleteTags', 'POST', undefined, undefined, { tags }, undefined, false);
232
248
  return true;
233
249
  }
234
250
  /**
@@ -242,39 +258,39 @@ class QBittorrent {
242
258
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#add-new-category}
243
259
  */
244
260
  async createCategory(category, savePath = '') {
245
- const form = new form_data_1.default();
246
- form.append('category', category);
247
- form.append('savePath', savePath);
248
- await this.request('/torrents/createCategory', 'POST', undefined, form);
261
+ await this.request('/torrents/createCategory', 'POST', undefined, undefined, {
262
+ category,
263
+ savePath,
264
+ }, undefined, false);
249
265
  return true;
250
266
  }
251
267
  /**
252
268
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#edit-category}
253
269
  */
254
270
  async editCategory(category, savePath = '') {
255
- const form = new form_data_1.default();
256
- form.append('category', category);
257
- form.append('savePath', savePath);
258
- await this.request('/torrents/editCategory', 'POST', undefined, form, undefined, false);
271
+ await this.request('/torrents/editCategory', 'POST', undefined, undefined, {
272
+ category,
273
+ savePath,
274
+ }, undefined, false);
259
275
  return true;
260
276
  }
261
277
  /**
262
278
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#remove-categories}
263
279
  */
264
280
  async removeCategory(categories) {
265
- const form = new form_data_1.default();
266
- form.append('categories', categories);
267
- await this.request('/torrents/removeCategories', 'POST', undefined, form, undefined, false);
281
+ await this.request('/torrents/removeCategories', 'POST', undefined, undefined, {
282
+ categories,
283
+ }, undefined, false);
268
284
  return true;
269
285
  }
270
286
  /**
271
287
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#add-torrent-tags}
272
288
  */
273
289
  async addTorrentTags(hashes, tags) {
274
- const form = new form_data_1.default();
275
- form.append('hashes', this._normalizeHashes(hashes));
276
- form.append('tags', tags);
277
- await this.request('/torrents/addTags', 'POST', undefined, form, undefined, false);
290
+ await this.request('/torrents/addTags', 'POST', undefined, undefined, {
291
+ hashes: this._normalizeHashes(hashes),
292
+ tags,
293
+ }, undefined, false);
278
294
  return true;
279
295
  }
280
296
  /**
@@ -282,12 +298,11 @@ class QBittorrent {
282
298
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#remove-torrent-tags}
283
299
  */
284
300
  async removeTorrentTags(hashes, tags) {
285
- const form = new form_data_1.default();
286
- form.append('hashes', this._normalizeHashes(hashes));
301
+ const form = { hashes: this._normalizeHashes(hashes) };
287
302
  if (tags) {
288
- form.append('tags', tags);
303
+ form.tags = tags;
289
304
  }
290
- await this.request('/torrents/removeTags', 'POST', undefined, form, undefined, false);
305
+ await this.request('/torrents/removeTags', 'POST', undefined, undefined, form, undefined, false);
291
306
  return true;
292
307
  }
293
308
  /**
@@ -300,10 +315,10 @@ class QBittorrent {
300
315
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#set-torrent-category}
301
316
  */
302
317
  async setTorrentCategory(hashes, category = '') {
303
- const form = new form_data_1.default();
304
- form.append('hashes', this._normalizeHashes(hashes));
305
- form.append('category', category);
306
- await this.request('/torrents/setCategory', 'POST', undefined, form);
318
+ await this.request('/torrents/setCategory', 'POST', undefined, undefined, {
319
+ hashes: this._normalizeHashes(hashes),
320
+ category,
321
+ });
307
322
  return true;
308
323
  }
309
324
  /**
@@ -358,26 +373,25 @@ class QBittorrent {
358
373
  return true;
359
374
  }
360
375
  async addTorrent(torrent, options = {}) {
361
- var _a;
362
- const form = new form_data_1.default();
363
- const fileOptions = {
364
- contentType: 'application/x-bittorrent',
365
- filename: (_a = options.filename) !== null && _a !== void 0 ? _a : 'torrent',
366
- };
376
+ var _a, _b;
377
+ const form = new FormData();
367
378
  // remove options.filename, not used in form
368
379
  if (options.filename) {
369
380
  delete options.filename;
370
381
  }
382
+ const type = { type: 'application/x-bittorrent' };
371
383
  if (typeof torrent === 'string') {
372
- if ((0, fs_1.existsSync)(torrent)) {
373
- form.append('file', Buffer.from((0, fs_1.readFileSync)(torrent)), fileOptions);
384
+ if (existsSync(torrent)) {
385
+ const file = await fileFromPath(torrent, (_a = options.filename) !== null && _a !== void 0 ? _a : 'torrent', type);
386
+ form.set('file', file);
374
387
  }
375
388
  else {
376
- form.append('file', Buffer.from(torrent, 'base64'), fileOptions);
389
+ form.set('file', new File([Buffer.from(torrent, 'base64')], 'file.torrent', type));
377
390
  }
378
391
  }
379
392
  else {
380
- form.append('file', torrent, fileOptions);
393
+ const file = new File([torrent], (_b = options.filename) !== null && _b !== void 0 ? _b : 'torrent', type);
394
+ form.set('file', file);
381
395
  }
382
396
  if (options) {
383
397
  // disable savepath when autoTMM is defined
@@ -391,7 +405,7 @@ class QBittorrent {
391
405
  form.append(key, value);
392
406
  }
393
407
  }
394
- const res = await this.request('/torrents/add', 'POST', undefined, form, form.getHeaders(), false);
408
+ const res = await this.request('/torrents/add', 'POST', undefined, form, undefined, undefined, false);
395
409
  if (res.body === 'Fails.') {
396
410
  throw new Error('Failed to add torrent');
397
411
  }
@@ -405,11 +419,21 @@ class QBittorrent {
405
419
  if (options.label) {
406
420
  torrentOptions.category = options.label;
407
421
  }
408
- if (!Buffer.isBuffer(torrent)) {
409
- torrent = Buffer.from(torrent);
422
+ let torrentHash;
423
+ if (typeof torrent === 'string' && torrent.startsWith('magnet:')) {
424
+ torrentHash = magnetDecode(torrent).infoHash;
425
+ if (!torrentHash) {
426
+ throw new Error('Magnet did not contain hash');
427
+ }
428
+ await this.addMagnet(torrent, torrentOptions);
429
+ }
430
+ else {
431
+ if (!Buffer.isBuffer(torrent)) {
432
+ torrent = Buffer.from(torrent);
433
+ }
434
+ torrentHash = await hash(torrent);
435
+ await this.addTorrent(torrent, torrentOptions);
410
436
  }
411
- const torrentHash = await (0, torrent_file_1.hash)(torrent);
412
- await this.addTorrent(torrent, torrentOptions);
413
437
  return this.getTorrent(torrentHash);
414
438
  }
415
439
  /**
@@ -418,11 +442,11 @@ class QBittorrent {
418
442
  * @param name new name to be assigned to the file
419
443
  */
420
444
  async renameFile(hash, id, name) {
421
- const form = new form_data_1.default();
445
+ const form = new FormData();
422
446
  form.append('hash', hash);
423
447
  form.append('id', id);
424
448
  form.append('name', name);
425
- await this.request('/torrents/renameFile', 'POST', undefined, form, form.getHeaders(), false);
449
+ await this.request('/torrents/renameFile', 'POST', undefined, form, undefined, false);
426
450
  return true;
427
451
  }
428
452
  /**
@@ -430,7 +454,7 @@ class QBittorrent {
430
454
  * @param options
431
455
  */
432
456
  async addMagnet(urls, options = {}) {
433
- const form = new form_data_1.default();
457
+ const form = new FormData();
434
458
  form.append('urls', urls);
435
459
  if (options) {
436
460
  // disable savepath when autoTMM is defined
@@ -444,7 +468,7 @@ class QBittorrent {
444
468
  form.append(key, value);
445
469
  }
446
470
  }
447
- const res = await this.request('/torrents/add', 'POST', undefined, form, form.getHeaders(), false);
471
+ const res = await this.request('/torrents/add', 'POST', undefined, form, undefined, undefined, false);
448
472
  if (res.body === 'Fails.') {
449
473
  throw new Error('Failed to add torrent');
450
474
  }
@@ -518,23 +542,25 @@ class QBittorrent {
518
542
  * {@link https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#login}
519
543
  */
520
544
  async login() {
521
- const url = (0, url_join_1.urlJoin)(this.config.baseUrl, this.config.path, '/auth/login');
522
- const form = new form_data_1.default();
523
- form.append('username', this.config.username);
524
- form.append('password', this.config.password);
525
- const res = await got_1.default.post(url, {
526
- isStream: false,
527
- body: form,
545
+ var _a, _b;
546
+ const url = urlJoin(this.config.baseUrl, this.config.path, '/auth/login');
547
+ const res = await got({
548
+ url,
549
+ method: 'POST',
550
+ form: {
551
+ username: (_a = this.config.username) !== null && _a !== void 0 ? _a : '',
552
+ password: (_b = this.config.password) !== null && _b !== void 0 ? _b : '',
553
+ },
528
554
  followRedirect: false,
529
- retry: 0,
555
+ retry: { limit: 0 },
556
+ timeout: { request: this.config.timeout },
530
557
  // allow proxy agent
531
- agent: this.config.agent,
532
- timeout: this.config.timeout,
558
+ ...(this.config.agent ? { agent: this.config.agent } : {}),
533
559
  });
534
560
  if (!res.headers['set-cookie'] || !res.headers['set-cookie'].length) {
535
561
  throw new Error('Cookie not found. Auth Failed.');
536
562
  }
537
- const cookie = tough_cookie_1.Cookie.parse(res.headers['set-cookie'][0]);
563
+ const cookie = Cookie.parse(res.headers['set-cookie'][0]);
538
564
  if (!cookie || cookie.key !== 'SID') {
539
565
  throw new Error('Invalid cookie');
540
566
  }
@@ -548,7 +574,7 @@ class QBittorrent {
548
574
  return true;
549
575
  }
550
576
  // eslint-disable-next-line max-params
551
- async request(path, method, params = {}, body, headers = {}, json = true) {
577
+ async request(path, method, params = {}, body, form, headers = {}, json = true) {
552
578
  var _a;
553
579
  if (!this._sid || !this._exp || this._exp.getTime() < new Date().getTime()) {
554
580
  const authed = await this.login();
@@ -556,8 +582,8 @@ class QBittorrent {
556
582
  throw new Error('Auth Failed');
557
583
  }
558
584
  }
559
- const url = (0, url_join_1.urlJoin)(this.config.baseUrl, this.config.path, path);
560
- const res = await (0, got_1.default)(url, {
585
+ const url = urlJoin(this.config.baseUrl, this.config.path, path);
586
+ const res = await got(url, {
561
587
  isStream: false,
562
588
  resolveBodyOnly: false,
563
589
  method,
@@ -565,13 +591,14 @@ class QBittorrent {
565
591
  Cookie: `SID=${(_a = this._sid) !== null && _a !== void 0 ? _a : ''}`,
566
592
  ...headers,
567
593
  },
568
- retry: 0,
594
+ retry: { limit: 0 },
569
595
  body,
570
- searchParams: new url_1.URLSearchParams(params),
596
+ form,
597
+ searchParams: new URLSearchParams(params),
571
598
  // allow proxy agent
572
- agent: this.config.agent,
573
- timeout: this.config.timeout,
599
+ timeout: { request: this.config.timeout },
574
600
  responseType: json ? 'json' : 'text',
601
+ ...(this.config.agent ? { agent: this.config.agent } : {}),
575
602
  });
576
603
  return res;
577
604
  }
@@ -586,40 +613,40 @@ class QBittorrent {
586
613
  return hashes;
587
614
  }
588
615
  _normalizeTorrentData(torrent) {
589
- let state = shared_torrent_1.TorrentState.unknown;
616
+ let state = NormalizedTorrentState.unknown;
590
617
  switch (torrent.state) {
591
- case types_1.TorrentState.ForcedDL:
592
- case types_1.TorrentState.MetaDL:
593
- state = shared_torrent_1.TorrentState.downloading;
618
+ case TorrentState.ForcedDL:
619
+ case TorrentState.MetaDL:
620
+ state = NormalizedTorrentState.downloading;
594
621
  break;
595
- case types_1.TorrentState.Allocating:
622
+ case TorrentState.Allocating:
596
623
  // state = 'stalledDL';
597
- state = shared_torrent_1.TorrentState.queued;
624
+ state = NormalizedTorrentState.queued;
598
625
  break;
599
- case types_1.TorrentState.ForcedUP:
600
- state = shared_torrent_1.TorrentState.seeding;
626
+ case TorrentState.ForcedUP:
627
+ state = NormalizedTorrentState.seeding;
601
628
  break;
602
- case types_1.TorrentState.PausedDL:
603
- state = shared_torrent_1.TorrentState.paused;
629
+ case TorrentState.PausedDL:
630
+ state = NormalizedTorrentState.paused;
604
631
  break;
605
- case types_1.TorrentState.PausedUP:
632
+ case TorrentState.PausedUP:
606
633
  // state = 'completed';
607
- state = shared_torrent_1.TorrentState.paused;
634
+ state = NormalizedTorrentState.paused;
608
635
  break;
609
- case types_1.TorrentState.QueuedDL:
610
- case types_1.TorrentState.QueuedUP:
611
- state = shared_torrent_1.TorrentState.queued;
636
+ case TorrentState.QueuedDL:
637
+ case TorrentState.QueuedUP:
638
+ state = NormalizedTorrentState.queued;
612
639
  break;
613
- case types_1.TorrentState.CheckingDL:
614
- case types_1.TorrentState.CheckingUP:
615
- case types_1.TorrentState.QueuedForChecking:
616
- case types_1.TorrentState.CheckingResumeData:
617
- case types_1.TorrentState.Moving:
618
- state = shared_torrent_1.TorrentState.checking;
640
+ case TorrentState.CheckingDL:
641
+ case TorrentState.CheckingUP:
642
+ case TorrentState.QueuedForChecking:
643
+ case TorrentState.CheckingResumeData:
644
+ case TorrentState.Moving:
645
+ state = NormalizedTorrentState.checking;
619
646
  break;
620
- case types_1.TorrentState.Unknown:
621
- case types_1.TorrentState.MissingFiles:
622
- state = shared_torrent_1.TorrentState.error;
647
+ case TorrentState.Unknown:
648
+ case TorrentState.MissingFiles:
649
+ state = NormalizedTorrentState.error;
623
650
  break;
624
651
  default:
625
652
  break;
@@ -653,4 +680,3 @@ class QBittorrent {
653
680
  return result;
654
681
  }
655
682
  }
656
- exports.QBittorrent = QBittorrent;
@@ -1,32 +1,22 @@
1
- import { AllClientData, NormalizedTorrent } from '@ctrl/shared-torrent';
2
- /**
3
- * refine the normalized torrent options for qbittorrent
4
- */
5
- export interface NormalizedTorrentQbittorrent extends NormalizedTorrent {
6
- id: string;
7
- }
8
- export interface AllClientDataQbittorrent extends AllClientData {
9
- torrents: NormalizedTorrentQbittorrent[];
10
- }
11
1
  export interface BuildInfo {
12
2
  /**
13
- * QT version
3
+ * QT version
14
4
  */
15
5
  qt: string;
16
6
  /**
17
- * libtorrent version
7
+ * libtorrent version
18
8
  */
19
9
  libtorrent: string;
20
10
  /**
21
- * Boost version
11
+ * Boost version
22
12
  */
23
13
  boost: string;
24
14
  /**
25
- * OpenSSL version
15
+ * OpenSSL version
26
16
  */
27
17
  openssl: string;
28
18
  /**
29
- * Application bitness (e.g. 64-bit)
19
+ * Application bitness (e.g. 64-bit)
30
20
  */
31
21
  bitness: string;
32
22
  }
@@ -176,8 +166,8 @@ export interface Torrent {
176
166
  */
177
167
  category: string;
178
168
  }
179
- export declare type Categories = Record<string, Category>;
180
- export interface Category {
169
+ export declare type TorrentCategories = Record<string, Category>;
170
+ interface Category {
181
171
  name: string;
182
172
  savePath: string;
183
173
  }
@@ -559,6 +549,14 @@ export interface AddTorrentOptions {
559
549
  * Set torrent download speed limit. Unit in bytes/second
560
550
  */
561
551
  dlLimit: number;
552
+ /**
553
+ * Set torrent share ratio limit
554
+ */
555
+ ratioLimit: number;
556
+ /**
557
+ * Set torrent seeding time limit. Unit in seconds
558
+ */
559
+ seedingTimeLimit: number;
562
560
  /**
563
561
  * Whether Automatic Torrent Management should be used, disables use of savepath
564
562
  */
@@ -670,9 +668,13 @@ export interface Preferences {
670
668
  */
671
669
  temp_path: string;
672
670
  /**
673
- * Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified
671
+ * Directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified
672
+ * Possible values of scan_dirs:
673
+ * 0 Download to the monitored folder
674
+ * 1 Download to the default save path
675
+ * "/path/to/download/to" Download to this path
674
676
  */
675
- scan_dirs: Record<string, unknown>;
677
+ scan_dirs: Record<string, 0 | 1 | string>;
676
678
  /**
677
679
  * Path to directory to copy .torrent files to. Slashes are used as path separators
678
680
  */
@@ -808,11 +810,14 @@ export interface Preferences {
808
810
  /**
809
811
  * True if the advanced libtorrent option piece_extent_affinity is enabled
810
812
  */
811
- piece_extent_affinity: boolean;
813
+ enable_piece_extent_affinity: boolean;
812
814
  /**
813
- * True if uTP protocol should be enabled; this option is only available in qBittorent built against libtorrent version 0.16.X and higher
815
+ * Bittorrent Protocol to use
816
+ * 0 TCP and μTP
817
+ * 1 TCP
818
+ * 2 μTP
814
819
  */
815
- enable_utp: boolean;
820
+ bittorrent_protocol: 0 | 1 | 2;
816
821
  /**
817
822
  * True if [du]l_limit should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher
818
823
  */
@@ -855,20 +860,22 @@ export interface Preferences {
855
860
  schedule_to_min: number;
856
861
  /**
857
862
  * Scheduler days. See possible values here below
858
- */
859
- scheduler_days: number;
863
+ * 0 Every day
864
+ * 1 Every weekday
865
+ * 2 Every weekend
866
+ * 3 Every Monday
867
+ * 4 Every Tuesday
868
+ * 5 Every Wednesday
869
+ * 6 Every Thursday
870
+ * 7 Every Friday
871
+ * 8 Every Saturday
872
+ * 9 Every Sunday
873
+ */
874
+ scheduler_days: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
860
875
  /**
861
876
  * True if DHT is enabled
862
877
  */
863
878
  dht: boolean;
864
- /**
865
- * True if DHT port should match TCP port
866
- */
867
- dhtSameAsBT: boolean;
868
- /**
869
- * DHT port if dhtSameAsBT is false
870
- */
871
- dht_port: number;
872
879
  /**
873
880
  * True if PeX is enabled
874
881
  */
@@ -879,10 +886,13 @@ export interface Preferences {
879
886
  lsd: boolean;
880
887
  /**
881
888
  * See list of possible values here below
889
+ * 0 Prefer encryption
890
+ * 1 Force encryption on
891
+ * 2 Force encryption off
882
892
  */
883
- encryption: number;
893
+ encryption: 0 | 1 | 2;
884
894
  /**
885
- * If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher
895
+ * If true anonymous mode will be enabled; read more [here](https://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher
886
896
  */
887
897
  anonymous_mode: boolean;
888
898
  /**
@@ -901,10 +911,6 @@ export interface Preferences {
901
911
  * True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher
902
912
  */
903
913
  proxy_peer_connections: boolean;
904
- /**
905
- * True if the connections not supported by the proxy are disabled
906
- */
907
- force_proxy: boolean;
908
914
  /**
909
915
  * True proxy requires authentication; doesn't apply to SOCKS4 proxies
910
916
  */
@@ -917,6 +923,10 @@ export interface Preferences {
917
923
  * Password for proxy authentication
918
924
  */
919
925
  proxy_password: string;
926
+ /**
927
+ * True if proxy is only used for torrents
928
+ */
929
+ proxy_torrents_only: boolean;
920
930
  /**
921
931
  * True if external IP filter should be enabled
922
932
  */
@@ -973,6 +983,14 @@ export interface Preferences {
973
983
  * WebUI access ban duration in seconds
974
984
  */
975
985
  web_ui_ban_duration: number;
986
+ /**
987
+ * Seconds until WebUI is automatically signed off
988
+ */
989
+ web_ui_session_timeout: number;
990
+ /**
991
+ * True if WebUI host header validation is enabled
992
+ */
993
+ web_ui_host_header_validation_enabled: boolean;
976
994
  /**
977
995
  * True if authentication challenge for loopback address (127.0.0.1) should be disabled
978
996
  */
@@ -1005,6 +1023,14 @@ export interface Preferences {
1005
1023
  * SSL certificate contents (this is a not a path)
1006
1024
  */
1007
1025
  ssl_cert: string;
1026
+ /**
1027
+ * For API ≥ v2.0.1: Path to SSL keyfile
1028
+ */
1029
+ web_ui_https_key_path: string;
1030
+ /**
1031
+ * For API ≥ v2.0.1: Path to SSL certificate
1032
+ */
1033
+ web_ui_https_cert_path: string;
1008
1034
  /**
1009
1035
  * True if server DNS should be updated dynamically
1010
1036
  */
@@ -1041,5 +1067,157 @@ export interface Preferences {
1041
1067
  * Enable auto-downloading of torrents from the RSS feeds
1042
1068
  */
1043
1069
  rss_auto_downloading_enabled: boolean;
1070
+ /**
1071
+ * For API ≥ v2.5.1: Enable downloading of repack/proper Episodes
1072
+ */
1073
+ rss_download_repack_proper_episodes: boolean;
1074
+ /**
1075
+ * For API ≥ v2.5.1: List of RSS Smart Episode Filters
1076
+ */
1077
+ rss_smart_episode_filters: string;
1078
+ /**
1079
+ * Enable automatic adding of trackers to new torrents
1080
+ */
1081
+ add_trackers_enabled: boolean;
1082
+ /**
1083
+ * List of trackers to add to new torrent
1084
+ */
1085
+ add_trackers: string;
1086
+ /**
1087
+ * For API ≥ v2.5.1: Enable custom http headers
1088
+ */
1089
+ web_ui_use_custom_http_headers_enabled: boolean;
1090
+ /**
1091
+ * For API ≥ v2.5.1: List of custom http headers
1092
+ */
1093
+ web_ui_custom_http_headers: string;
1094
+ /**
1095
+ * True enables max seeding time
1096
+ */
1097
+ max_seeding_time_enabled: boolean;
1098
+ /**
1099
+ * Number of minutes to seed a torrent
1100
+ */
1101
+ max_seeding_time: number;
1102
+ /**
1103
+ * TODO
1104
+ */
1105
+ announce_ip: string;
1106
+ /**
1107
+ * True always announce to all tiers
1108
+ */
1109
+ announce_to_all_tiers: boolean;
1110
+ /**
1111
+ * True always announce to all trackers in a tier
1112
+ */
1113
+ announce_to_all_trackers: boolean;
1114
+ /**
1115
+ * Number of asynchronous I/O threads
1116
+ */
1117
+ async_io_threads: number;
1118
+ /**
1119
+ * List of banned IPs
1120
+ */
1121
+ banned_IPs: string;
1122
+ /**
1123
+ * Outstanding memory when checking torrents in MiB
1124
+ */
1125
+ checking_memory_use: number;
1126
+ /**
1127
+ * IP Address to bind to. Empty String means All addresses
1128
+ */
1129
+ current_interface_address: string;
1130
+ /**
1131
+ * Network Interface used
1132
+ */
1133
+ current_network_interface: string;
1134
+ /**
1135
+ * Disk cache used in MiB
1136
+ */
1137
+ disk_cache: number;
1138
+ /**
1139
+ * Disk cache expiry interval in seconds
1140
+ */
1141
+ disk_cache_ttl: number;
1142
+ /**
1143
+ * Port used for embedded tracker
1144
+ */
1145
+ embedded_tracker_port: number;
1146
+ /**
1147
+ * True enables coalesce reads & writes
1148
+ */
1149
+ enable_coalesce_read_write: boolean;
1150
+ /**
1151
+ * True enables embedded tracker
1152
+ */
1153
+ enable_embedded_tracker: boolean;
1154
+ /**
1155
+ * True allows multiple connections from the same IP address
1156
+ */
1157
+ enable_multi_connections_from_same_ip: boolean;
1158
+ /**
1159
+ * True enables os cache
1160
+ */
1161
+ enable_os_cache: boolean;
1162
+ /**
1163
+ * True enables sending of upload piece suggestions
1164
+ */
1165
+ enable_upload_suggestions: boolean;
1166
+ /**
1167
+ * File pool size
1168
+ */
1169
+ file_pool_size: number;
1170
+ /**
1171
+ * Maximal outgoing port (0: Disabled)
1172
+ */
1173
+ outgoing_ports_max: number;
1174
+ /**
1175
+ * Minimal outgoing port (0: Disabled)
1176
+ */
1177
+ outgoing_ports_min: number;
1178
+ /**
1179
+ * True rechecks torrents on completion
1180
+ */
1181
+ recheck_completed_torrents: boolean;
1182
+ /**
1183
+ * True resolves peer countries
1184
+ */
1185
+ resolve_peer_countries: boolean;
1186
+ /**
1187
+ * Save resume data interval in min
1188
+ */
1189
+ save_resume_data_interval: number;
1190
+ /**
1191
+ * Send buffer low watermark in KiB
1192
+ */
1193
+ send_buffer_low_watermark: number;
1194
+ /**
1195
+ * Send buffer watermark in KiB
1196
+ */
1197
+ send_buffer_watermark: number;
1198
+ /**
1199
+ * Send buffer watermark factor in percent
1200
+ */
1201
+ send_buffer_watermark_factor: number;
1202
+ /**
1203
+ * Socket backlog size
1204
+ */
1205
+ socket_backlog_size: number;
1206
+ /**
1207
+ * Upload choking algorithm used (see list of possible values below)
1208
+ */
1209
+ upload_choking_algorithm: number;
1210
+ /**
1211
+ * Upload slots behavior used (see list of possible values below)
1212
+ */
1213
+ upload_slots_behavior: number;
1214
+ /**
1215
+ * UPnP lease duration (0: Permanent lease)
1216
+ */
1217
+ upnp_lease_duration: number;
1218
+ /**
1219
+ * μTP-TCP mixed mode algorithm (see list of possible values below)
1220
+ */
1221
+ utp_tcp_mixed_mode: number;
1044
1222
  }
1045
1223
  export {};
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TorrentPieceState = exports.TorrentFilePriority = exports.TorrentTrackerStatus = exports.TorrentState = void 0;
4
- var TorrentState;
1
+ export var TorrentState;
5
2
  (function (TorrentState) {
6
3
  /**
7
4
  * Some error occurred, applies to paused torrents
@@ -80,8 +77,8 @@ var TorrentState;
80
77
  * Torrent data files is missing
81
78
  */
82
79
  TorrentState["MissingFiles"] = "missingFiles";
83
- })(TorrentState = exports.TorrentState || (exports.TorrentState = {}));
84
- var TorrentTrackerStatus;
80
+ })(TorrentState || (TorrentState = {}));
81
+ export var TorrentTrackerStatus;
85
82
  (function (TorrentTrackerStatus) {
86
83
  /**
87
84
  * Tracker is disabled (used for DHT, PeX, and LSD)
@@ -103,8 +100,8 @@ var TorrentTrackerStatus;
103
100
  * Tracker has not been contacted yet
104
101
  */
105
102
  TorrentTrackerStatus[TorrentTrackerStatus["Waiting"] = 4] = "Waiting";
106
- })(TorrentTrackerStatus = exports.TorrentTrackerStatus || (exports.TorrentTrackerStatus = {}));
107
- var TorrentFilePriority;
103
+ })(TorrentTrackerStatus || (TorrentTrackerStatus = {}));
104
+ export var TorrentFilePriority;
108
105
  (function (TorrentFilePriority) {
109
106
  /**
110
107
  * Do not download
@@ -122,8 +119,8 @@ var TorrentFilePriority;
122
119
  * Maximal priority
123
120
  */
124
121
  TorrentFilePriority[TorrentFilePriority["MaxPriority"] = 7] = "MaxPriority";
125
- })(TorrentFilePriority = exports.TorrentFilePriority || (exports.TorrentFilePriority = {}));
126
- var TorrentPieceState;
122
+ })(TorrentFilePriority || (TorrentFilePriority = {}));
123
+ export var TorrentPieceState;
127
124
  (function (TorrentPieceState) {
128
125
  /**
129
126
  * Not downloaded yet
@@ -137,4 +134,4 @@ var TorrentPieceState;
137
134
  * Already downloaded
138
135
  */
139
136
  TorrentPieceState[TorrentPieceState["Downloaded"] = 2] = "Downloaded";
140
- })(TorrentPieceState = exports.TorrentPieceState || (exports.TorrentPieceState = {}));
137
+ })(TorrentPieceState || (TorrentPieceState = {}));
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@ctrl/qbittorrent",
3
- "version": "2.9.0",
3
+ "version": "3.1.0",
4
4
  "description": "TypeScript api wrapper for qbittorrent using got",
5
5
  "author": "Scott Cooper <scttcper@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": "scttcper/qbittorrent",
8
- "main": "dist/index.js",
9
- "types": "dist/index.d.ts",
8
+ "homepage": "https://qbittorrent.vercel.app",
9
+ "type": "module",
10
+ "main": "./dist/src/index.js",
11
+ "typings": "./dist/src/index.d.ts",
10
12
  "files": [
11
- "dist"
13
+ "dist/src"
12
14
  ],
13
15
  "sideEffects": false,
14
16
  "publishConfig": {
@@ -22,49 +24,49 @@
22
24
  "lint": "eslint --ext .js,.ts, .",
23
25
  "lint:fix": "eslint --fix --ext .js,.ts, .",
24
26
  "prepare": "npm run build",
25
- "build": "tsc -p tsconfig.build.json",
27
+ "build": "tsc",
26
28
  "build:docs": "typedoc",
27
- "test": "jest --runInBand",
28
- "test:watch": "jest --watch --runInBand",
29
- "test:ci": "jest --ci --reporters=default --reporters=jest-junit --runInBand --coverage"
29
+ "test": "ava",
30
+ "test:watch": "ava --watch",
31
+ "test:ci": "c8 --reporter=text --reporter=lcov ava --tap | tap-junit -o junit -n test-result.xml"
30
32
  },
31
33
  "dependencies": {
32
- "@ctrl/shared-torrent": "^3.0.5",
33
- "@ctrl/torrent-file": "^1.3.3",
34
- "@ctrl/url-join": "^1.0.4",
35
- "form-data": "^4.0.0",
36
- "got": "^11.8.2",
34
+ "@ctrl/magnet-link": "^3.1.0",
35
+ "@ctrl/shared-torrent": "^4.1.0",
36
+ "@ctrl/torrent-file": "^2.0.1",
37
+ "@ctrl/url-join": "^2.0.0",
38
+ "formdata-node": "^4.3.2",
39
+ "got": "^12.0.3",
37
40
  "tough-cookie": "^4.0.0"
38
41
  },
39
42
  "devDependencies": {
40
- "@babel/plugin-transform-modules-commonjs": "7.16.0",
41
- "@babel/preset-typescript": "7.16.0",
42
- "@ctrl/eslint-config": "3.1.1",
43
- "@jest/globals": "27.3.1",
44
- "@types/form-data": "2.5.0",
45
- "@types/node": "16.11.6",
43
+ "@ctrl/eslint-config": "3.4.1",
44
+ "@sindresorhus/tsconfig": "2.0.0",
45
+ "@types/node": "17.0.23",
46
46
  "@types/tough-cookie": "4.0.1",
47
- "jest-junit": "13.0.0",
48
- "jest": "27.3.1",
49
- "p-wait-for": "3.2.0",
50
- "typedoc": "0.22.8",
51
- "typescript": "4.4.4"
47
+ "ava": "4.2.0",
48
+ "c8": "7.11.0",
49
+ "p-wait-for": "4.1.0",
50
+ "tap-junit": "5.0.2",
51
+ "ts-node": "10.7.0",
52
+ "typedoc": "0.22.15",
53
+ "typescript": "4.6.3"
52
54
  },
53
- "jest": {
54
- "testEnvironment": "node"
55
- },
56
- "babel": {
57
- "presets": [
58
- "@babel/preset-typescript"
55
+ "ava": {
56
+ "files": [
57
+ "test/**/*.spec.ts"
59
58
  ],
60
- "plugins": [
61
- "@babel/plugin-transform-modules-commonjs"
59
+ "extensions": {
60
+ "ts": "module"
61
+ },
62
+ "nodeArguments": [
63
+ "--loader=ts-node/esm"
62
64
  ]
63
65
  },
64
66
  "release": {
65
67
  "branch": "master"
66
68
  },
67
69
  "engines": {
68
- "node": ">=10.19.0"
70
+ "node": ">=14.16"
69
71
  }
70
72
  }