@ctrl/qbittorrent 4.1.0 → 5.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.
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import { Options as GotOptions, Response } from 'got';
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';
2
+ import type { Options as GotOptions, Response } from 'got';
3
+ import type { AddTorrentOptions as NormalizedAddTorrentOptions, AllClientData, NormalizedTorrent, TorrentClient, TorrentSettings } from '@ctrl/shared-torrent';
4
+ import type { AddMagnetOptions, AddTorrentOptions, BuildInfo, Preferences, Torrent, TorrentCategories, TorrentFile, TorrentFilePriority, TorrentFilters, TorrentPieceState, TorrentProperties, TorrentTrackers, WebSeed } from './types.js';
5
5
  export declare class QBittorrent implements TorrentClient {
6
6
  config: TorrentSettings;
7
7
  /**
@@ -7,10 +7,10 @@ import { fileFromPath } from 'formdata-node/file-from-path';
7
7
  import got from 'got';
8
8
  import { Cookie } from 'tough-cookie';
9
9
  import { magnetDecode } from '@ctrl/magnet-link';
10
- import { TorrentState as NormalizedTorrentState, } from '@ctrl/shared-torrent';
10
+ import { TorrentState as NormalizedTorrentState } from '@ctrl/shared-torrent';
11
11
  import { hash } from '@ctrl/torrent-file';
12
12
  import { urlJoin } from '@ctrl/url-join';
13
- import { TorrentState, } from './types.js';
13
+ import { TorrentState } from './types.js';
14
14
  const defaults = {
15
15
  baseUrl: 'http://localhost:9091/',
16
16
  path: '/api/v2',
@@ -133,6 +133,7 @@ export class QBittorrent {
133
133
  const results = {
134
134
  torrents: [],
135
135
  labels: [],
136
+ raw: listTorrents,
136
137
  };
137
138
  const labels = {};
138
139
  for (const torrent of listTorrents) {
@@ -623,39 +624,59 @@ export class QBittorrent {
623
624
  }
624
625
  _normalizeTorrentData(torrent) {
625
626
  let state = NormalizedTorrentState.unknown;
627
+ let stateMessage = '';
628
+ let { eta } = torrent;
629
+ /**
630
+ * Good references https://github.com/qbittorrent/qBittorrent/blob/master/src/webui/www/private/scripts/dynamicTable.js#L933
631
+ * https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs#L242
632
+ */
626
633
  switch (torrent.state) {
627
- case TorrentState.ForcedDL:
628
- case TorrentState.MetaDL:
634
+ case TorrentState.Error:
635
+ state = NormalizedTorrentState.warning;
636
+ stateMessage = 'qBittorrent is reporting an error';
637
+ break;
638
+ case TorrentState.PausedDL:
639
+ state = NormalizedTorrentState.paused;
640
+ break;
641
+ case TorrentState.QueuedDL: // queuing is enabled and torrent is queued for download
642
+ case TorrentState.CheckingDL: // same as checkingUP, but torrent has NOT finished downloading
643
+ case TorrentState.CheckingUP: // torrent has finished downloading and is being checked. Set when `recheck torrent on completion` is enabled. In the event the check fails we shouldn't treat it as completed.
644
+ state = NormalizedTorrentState.queued;
645
+ break;
646
+ case TorrentState.MetaDL: // Metadl could be an error if DHT is not enabled
647
+ case TorrentState.ForcedDL: // torrent is being downloaded, and was forced started
648
+ case TorrentState.ForcedMetaDL: // torrent metadata is being forcibly downloaded
649
+ case TorrentState.Downloading: // torrent is being downloaded and data is being transferred
629
650
  state = NormalizedTorrentState.downloading;
630
651
  break;
631
652
  case TorrentState.Allocating:
632
653
  // state = 'stalledDL';
633
654
  state = NormalizedTorrentState.queued;
634
655
  break;
635
- case TorrentState.ForcedUP:
636
- state = NormalizedTorrentState.seeding;
637
- break;
638
- case TorrentState.PausedDL:
639
- state = NormalizedTorrentState.paused;
656
+ case TorrentState.StalledDL:
657
+ state = NormalizedTorrentState.warning;
658
+ stateMessage = 'The download is stalled with no connection';
640
659
  break;
641
- case TorrentState.PausedUP:
660
+ case TorrentState.PausedUP: // torrent is paused and has finished downloading:
661
+ case TorrentState.Uploading: // torrent is being seeded and data is being transferred
662
+ case TorrentState.StalledUP: // torrent is being seeded, but no connection were made
663
+ case TorrentState.QueuedUP: // queuing is enabled and torrent is queued for upload
664
+ case TorrentState.ForcedUP: // torrent has finished downloading and is being forcibly seeded
642
665
  // state = 'completed';
643
- state = NormalizedTorrentState.paused;
644
- break;
645
- case TorrentState.QueuedDL:
646
- case TorrentState.QueuedUP:
647
- state = NormalizedTorrentState.queued;
666
+ state = NormalizedTorrentState.seeding;
667
+ eta = 0; // qBittorrent sends eta=8640000 for completed torrents
648
668
  break;
649
- case TorrentState.CheckingDL:
650
- case TorrentState.CheckingUP:
669
+ case TorrentState.Moving: // torrent is being moved from a folder
651
670
  case TorrentState.QueuedForChecking:
652
671
  case TorrentState.CheckingResumeData:
653
- case TorrentState.Moving:
654
672
  state = NormalizedTorrentState.checking;
655
673
  break;
656
674
  case TorrentState.Unknown:
675
+ state = NormalizedTorrentState.error;
676
+ break;
657
677
  case TorrentState.MissingFiles:
658
678
  state = NormalizedTorrentState.error;
679
+ stateMessage = 'The download is missing files';
659
680
  break;
660
681
  default:
661
682
  break;
@@ -664,17 +685,18 @@ export class QBittorrent {
664
685
  const result = {
665
686
  id: torrent.hash,
666
687
  name: torrent.name,
667
- stateMessage: '',
688
+ stateMessage,
668
689
  state,
690
+ eta,
669
691
  dateAdded: new Date(torrent.added_on * 1000).toISOString(),
670
692
  isCompleted,
671
693
  progress: torrent.progress,
672
694
  label: torrent.category,
695
+ tags: torrent.tags.split(', '),
673
696
  dateCompleted: new Date(torrent.completion_on * 1000).toISOString(),
674
697
  savePath: torrent.save_path,
675
698
  uploadSpeed: torrent.upspeed,
676
699
  downloadSpeed: torrent.dlspeed,
677
- eta: torrent.eta,
678
700
  queuePosition: torrent.priority,
679
701
  connectedPeers: torrent.num_leechs,
680
702
  connectedSeeds: torrent.num_seeds,
@@ -685,6 +707,7 @@ export class QBittorrent {
685
707
  totalUploaded: torrent.uploaded,
686
708
  totalDownloaded: torrent.downloaded,
687
709
  ratio: torrent.ratio,
710
+ raw: torrent,
688
711
  };
689
712
  return result;
690
713
  }
@@ -96,6 +96,7 @@ export interface Torrent {
96
96
  * Torrent tracker
97
97
  */
98
98
  tracker: string;
99
+ trackers_count: number;
99
100
  /**
100
101
  * Torrent download limit
101
102
  */
@@ -148,6 +149,10 @@ export interface Torrent {
148
149
  * Upload seeding time limit
149
150
  */
150
151
  seeding_time_limit: number;
152
+ /**
153
+ * True if super seeding is enabled
154
+ */
155
+ super_seeding: boolean;
151
156
  /**
152
157
  * Indicates the time when the torrent was last seen complete/whole
153
158
  */
@@ -165,6 +170,10 @@ export interface Torrent {
165
170
  * Category name
166
171
  */
167
172
  category: string;
173
+ /**
174
+ * Comma-concatenated tag list of the torrent e.g. - "abc, 123"
175
+ */
176
+ tags: string;
168
177
  }
169
178
  export declare type TorrentCategories = Record<string, Category>;
170
179
  interface Category {
@@ -220,6 +229,10 @@ export declare enum TorrentState {
220
229
  * Torrent is forced to downloading to ignore queue limit
221
230
  */
222
231
  ForcedDL = "forcedDL",
232
+ /**
233
+ * Forced Downloading Metadata
234
+ */
235
+ ForcedMetaDL = "ForcedMetaDL",
223
236
  /**
224
237
  * Torrent is forced to uploading and ignore queue limit
225
238
  */
package/dist/src/types.js CHANGED
@@ -48,6 +48,10 @@ export var TorrentState;
48
48
  * Torrent is forced to downloading to ignore queue limit
49
49
  */
50
50
  TorrentState["ForcedDL"] = "forcedDL";
51
+ /**
52
+ * Forced Downloading Metadata
53
+ */
54
+ TorrentState["ForcedMetaDL"] = "ForcedMetaDL";
51
55
  /**
52
56
  * Torrent is forced to uploading and ignore queue limit
53
57
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctrl/qbittorrent",
3
- "version": "4.1.0",
3
+ "version": "5.1.0",
4
4
  "description": "TypeScript api wrapper for qbittorrent using got",
5
5
  "author": "Scott Cooper <scttcper@gmail.com>",
6
6
  "license": "MIT",
@@ -28,27 +28,28 @@
28
28
  "build:docs": "typedoc",
29
29
  "test": "vitest run",
30
30
  "test:watch": "vitest",
31
- "test:ci": "vitest run --coverage --reporter=junit --outputFile=./junit.xml"
31
+ "test:ci": "vitest run --coverage --reporter=default --reporter=junit --outputFile=./junit.xml"
32
32
  },
33
33
  "dependencies": {
34
34
  "@ctrl/magnet-link": "^3.1.1",
35
- "@ctrl/shared-torrent": "^4.1.1",
35
+ "@ctrl/shared-torrent": "^4.3.1",
36
36
  "@ctrl/torrent-file": "^2.0.2",
37
37
  "@ctrl/url-join": "^2.0.2",
38
- "formdata-node": "^4.3.3",
39
- "got": "^12.1.0",
40
- "tough-cookie": "^4.0.0"
38
+ "formdata-node": "^5.0.0",
39
+ "got": "^12.5.0",
40
+ "tough-cookie": "^4.1.2"
41
41
  },
42
42
  "devDependencies": {
43
- "@ctrl/eslint-config": "3.4.6",
43
+ "@ctrl/eslint-config": "3.5.0",
44
44
  "@sindresorhus/tsconfig": "3.0.1",
45
- "@types/node": "18.0.3",
45
+ "@types/node": "18.7.18",
46
46
  "@types/tough-cookie": "4.0.2",
47
- "c8": "7.11.3",
48
- "p-wait-for": "4.1.0",
49
- "typedoc": "0.23.7",
50
- "typescript": "4.7.4",
51
- "vitest": "0.18.0"
47
+ "@vitest/coverage-c8": "0.23.4",
48
+ "c8": "7.12.0",
49
+ "p-wait-for": "5.0.0",
50
+ "typedoc": "0.23.15",
51
+ "typescript": "4.8.3",
52
+ "vitest": "0.23.4"
52
53
  },
53
54
  "release": {
54
55
  "branches": [