@ctrl/rqbit 1.0.0 → 2.0.1
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.
- package/README.md +24 -10
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/normalizeTorrentData.js +1 -1
- package/dist/src/rqbit.d.ts +11 -11
- package/dist/src/rqbit.js +22 -10
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
npm install @ctrl/rqbit
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Requires Node.js 24 or newer.
|
|
12
|
+
|
|
11
13
|
### Use
|
|
12
14
|
|
|
13
15
|
```ts
|
|
@@ -66,27 +68,23 @@ console.log(data);
|
|
|
66
68
|
|
|
67
69
|
##### pauseTorrent and resumeTorrent
|
|
68
70
|
|
|
69
|
-
Pause or resume
|
|
71
|
+
Pause or resume one or more torrents
|
|
70
72
|
|
|
71
73
|
```ts
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const resumed = await client.resumeTorrent('torrent-hash');
|
|
75
|
-
console.log(resumed);
|
|
74
|
+
await client.pauseTorrent('torrent-hash');
|
|
75
|
+
await client.resumeTorrent(['torrent-hash', 'other-torrent-hash']);
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
##### removeTorrent
|
|
79
79
|
|
|
80
|
-
Remove a torrent. Does not remove data on disk by default.
|
|
80
|
+
Remove one or more torrents, throws if a torrent doesn't exist. Does not remove data on disk by default.
|
|
81
81
|
|
|
82
82
|
```ts
|
|
83
83
|
// does not remove data on disk
|
|
84
|
-
|
|
85
|
-
console.log(result);
|
|
84
|
+
await client.removeTorrent('torrent-hash', false);
|
|
86
85
|
|
|
87
86
|
// remove data on disk
|
|
88
|
-
|
|
89
|
-
console.log(res);
|
|
87
|
+
await client.removeTorrent(['torrent-hash', 'other-torrent-hash'], true);
|
|
90
88
|
```
|
|
91
89
|
|
|
92
90
|
##### addTorrent
|
|
@@ -113,6 +111,22 @@ const result = await client.normalizedAddTorrent('magnet:?xt=urn:btih:...', {
|
|
|
113
111
|
console.log(result);
|
|
114
112
|
```
|
|
115
113
|
|
|
114
|
+
##### Errors
|
|
115
|
+
|
|
116
|
+
Failed requests throw a `TorrentClientError` from [@ctrl/shared-torrent](https://github.com/scttcper/shared-torrent) with a `code` of `torrent_not_found`, `unauthorized`, `request_failed` or `client_error`, the http `status` when there is one and the original error as the `cause`. rqbit errors are a `RqbitApiError`, a `TorrentClientError` that also has rqbit's error `kind`.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { TorrentClientError } from '@ctrl/rqbit';
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
await client.getTorrent('torrent-hash');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error instanceof TorrentClientError && error.code === 'torrent_not_found') {
|
|
125
|
+
// not in the client
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
116
130
|
##### export and create from state
|
|
117
131
|
|
|
118
132
|
rqbit uses basic auth on every request so there is no session to save, this exists to match the other clients.
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -48,7 +48,7 @@ export function normalizeTorrentData(torrent, files) {
|
|
|
48
48
|
savePath: torrent.output_folder,
|
|
49
49
|
uploadSpeed: live ? Math.round(live.upload_speed.mbps * MIB) : 0,
|
|
50
50
|
downloadSpeed: live ? Math.round(live.download_speed.mbps * MIB) : 0,
|
|
51
|
-
eta: live?.time_remaining?.duration.secs ??
|
|
51
|
+
eta: stats.finished ? 0 : (live?.time_remaining?.duration.secs ?? -1),
|
|
52
52
|
// rqbit has no queue
|
|
53
53
|
queuePosition: 0,
|
|
54
54
|
// rqbit does not report seeds separately from peers
|
package/dist/src/rqbit.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AddTorrentOptions as NormalizedAddTorrentOptions, type AllClientData, type NormalizedTorrent, type TorrentClient, type TorrentClientConfig, TorrentClientError, type TorrentClientState } from '@ctrl/shared-torrent';
|
|
2
2
|
import { type FetchOptions, type MappedResponseType, type ResponseType } from 'ofetch';
|
|
3
3
|
import type { Jsonify } from 'type-fest';
|
|
4
4
|
import type { AddTorrentOptions, AddTorrentResponse, ApiRootResponse, CreateTorrentOptions, CreateTorrentResponse, DhtStats, EmptyResponse, ListTorrentsResponse, PeerStatsResponse, RateLimits, RqbitErrorResponse, SessionStats, TorrentDetails, TorrentIdOrHash, TorrentListItem, TorrentStats, TorrentWithStats } from './types.js';
|
|
@@ -21,12 +21,11 @@ export type ResolvedConfig = RqbitConfig & {
|
|
|
21
21
|
/**
|
|
22
22
|
* Error thrown when a request to rqbit fails, either an error response or a timeout/network error
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A {@link TorrentClientError} with rqbit's error details
|
|
26
|
+
*/
|
|
27
|
+
export declare class RqbitApiError extends TorrentClientError {
|
|
25
28
|
name: string;
|
|
26
|
-
/**
|
|
27
|
-
* HTTP status code, undefined when no response was received (timeout or network error)
|
|
28
|
-
*/
|
|
29
|
-
status?: number;
|
|
30
29
|
/**
|
|
31
30
|
* rqbit error kind, ex - `torrent_not_found`. Undefined when rqbit returned a plain text error.
|
|
32
31
|
*/
|
|
@@ -89,13 +88,14 @@ export declare class Rqbit implements TorrentClient {
|
|
|
89
88
|
* rqbit requires basic auth on this url when it is enabled.
|
|
90
89
|
*/
|
|
91
90
|
getPlaylistUrl(id?: TorrentIdOrHash): string;
|
|
92
|
-
pauseTorrent(id: TorrentIdOrHash): Promise<
|
|
93
|
-
resumeTorrent(id: TorrentIdOrHash): Promise<
|
|
91
|
+
pauseTorrent(id: TorrentIdOrHash | TorrentIdOrHash[]): Promise<void>;
|
|
92
|
+
resumeTorrent(id: TorrentIdOrHash | TorrentIdOrHash[]): Promise<void>;
|
|
94
93
|
/**
|
|
95
94
|
* Remove a torrent
|
|
96
95
|
* @param removeData (default: false) If true, remove the downloaded files.
|
|
96
|
+
* @throws {RqbitApiError} when a torrent doesn't exist
|
|
97
97
|
*/
|
|
98
|
-
removeTorrent(id: TorrentIdOrHash, removeData?: boolean): Promise<
|
|
98
|
+
removeTorrent(id: TorrentIdOrHash | TorrentIdOrHash[], removeData?: boolean): Promise<void>;
|
|
99
99
|
/**
|
|
100
100
|
* Change which files are downloaded
|
|
101
101
|
* @param fileIds indexes into the torrent's file list
|
|
@@ -111,11 +111,11 @@ export declare class Rqbit implements TorrentClient {
|
|
|
111
111
|
/**
|
|
112
112
|
* rqbit does not support queueing
|
|
113
113
|
*/
|
|
114
|
-
queueUp(_id: TorrentIdOrHash): Promise<never>;
|
|
114
|
+
queueUp(_id: TorrentIdOrHash | TorrentIdOrHash[]): Promise<never>;
|
|
115
115
|
/**
|
|
116
116
|
* rqbit does not support queueing
|
|
117
117
|
*/
|
|
118
|
-
queueDown(_id: TorrentIdOrHash): Promise<never>;
|
|
118
|
+
queueDown(_id: TorrentIdOrHash | TorrentIdOrHash[]): Promise<never>;
|
|
119
119
|
/**
|
|
120
120
|
* Add a magnet link or a url to a .torrent file.
|
|
121
121
|
* rqbit resolves magnet metadata from peers before it responds, so this can take a while, see {@link RqbitConfig.addTimeout}
|
package/dist/src/rqbit.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TorrentClientError, } from '@ctrl/shared-torrent';
|
|
1
2
|
import { FetchError, ofetch, } from 'ofetch';
|
|
2
3
|
import { joinURL } from 'ufo';
|
|
3
4
|
import { base64ToUint8Array, stringToBase64 } from 'uint8array-extras';
|
|
@@ -13,24 +14,28 @@ const defaults = {
|
|
|
13
14
|
/**
|
|
14
15
|
* Error thrown when a request to rqbit fails, either an error response or a timeout/network error
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* A {@link TorrentClientError} with rqbit's error details
|
|
19
|
+
*/
|
|
20
|
+
export class RqbitApiError extends TorrentClientError {
|
|
17
21
|
name = 'RqbitApiError';
|
|
18
|
-
/**
|
|
19
|
-
* HTTP status code, undefined when no response was received (timeout or network error)
|
|
20
|
-
*/
|
|
21
|
-
status;
|
|
22
22
|
/**
|
|
23
23
|
* rqbit error kind, ex - `torrent_not_found`. Undefined when rqbit returned a plain text error.
|
|
24
24
|
*/
|
|
25
25
|
kind;
|
|
26
26
|
response;
|
|
27
27
|
constructor(status, message, response, cause) {
|
|
28
|
-
super(message, { cause });
|
|
29
|
-
this.status = status;
|
|
28
|
+
super(message, rqbitErrorCode(status, response?.error_kind), { status, cause });
|
|
30
29
|
this.kind = response?.error_kind;
|
|
31
30
|
this.response = response;
|
|
32
31
|
}
|
|
33
32
|
}
|
|
33
|
+
function rqbitErrorCode(status, kind) {
|
|
34
|
+
if (status === 401 || status === 403) {
|
|
35
|
+
return 'unauthorized';
|
|
36
|
+
}
|
|
37
|
+
return kind === 'torrent_not_found' ? 'torrent_not_found' : 'request_failed';
|
|
38
|
+
}
|
|
34
39
|
/**
|
|
35
40
|
* rqbit has no published api reference. `GET /` on the server lists every endpoint, see {@link Rqbit.getApiInfo}.
|
|
36
41
|
* Routes {@link https://github.com/ikatson/rqbit/blob/v9.0.1/crates/librqbit/src/http_api/handlers/mod.rs}
|
|
@@ -126,18 +131,25 @@ export class Rqbit {
|
|
|
126
131
|
return this.url(id === undefined ? '/torrents/playlist' : `/torrents/${id}/playlist`);
|
|
127
132
|
}
|
|
128
133
|
async pauseTorrent(id) {
|
|
129
|
-
|
|
134
|
+
for (const i of Array.isArray(id) ? id : [id]) {
|
|
135
|
+
await this.request(`/torrents/${i}/pause`, { method: 'POST' });
|
|
136
|
+
}
|
|
130
137
|
}
|
|
131
138
|
async resumeTorrent(id) {
|
|
132
|
-
|
|
139
|
+
for (const i of Array.isArray(id) ? id : [id]) {
|
|
140
|
+
await this.request(`/torrents/${i}/start`, { method: 'POST' });
|
|
141
|
+
}
|
|
133
142
|
}
|
|
134
143
|
/**
|
|
135
144
|
* Remove a torrent
|
|
136
145
|
* @param removeData (default: false) If true, remove the downloaded files.
|
|
146
|
+
* @throws {RqbitApiError} when a torrent doesn't exist
|
|
137
147
|
*/
|
|
138
148
|
async removeTorrent(id, removeData = false) {
|
|
139
149
|
const action = removeData ? 'delete' : 'forget';
|
|
140
|
-
|
|
150
|
+
for (const i of Array.isArray(id) ? id : [id]) {
|
|
151
|
+
await this.request(`/torrents/${i}/${action}`, { method: 'POST' });
|
|
152
|
+
}
|
|
141
153
|
}
|
|
142
154
|
/**
|
|
143
155
|
* Change which files are downloaded
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctrl/rqbit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "TypeScript api wrapper for rqbit using ofetch",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rqbit",
|
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
"sideEffects": false,
|
|
22
22
|
"main": "./dist/src/index.js",
|
|
23
23
|
"types": "./dist/src/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/src/index.d.ts",
|
|
27
|
+
"default": "./dist/src/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
24
31
|
"publishConfig": {
|
|
25
32
|
"access": "public",
|
|
26
33
|
"provenance": true
|
|
@@ -35,7 +42,7 @@
|
|
|
35
42
|
"test:watch": "vitest"
|
|
36
43
|
},
|
|
37
44
|
"dependencies": {
|
|
38
|
-
"@ctrl/shared-torrent": "^6.
|
|
45
|
+
"@ctrl/shared-torrent": "^6.6.0",
|
|
39
46
|
"ofetch": "^1.5.1",
|
|
40
47
|
"type-fest": "^5.10.0",
|
|
41
48
|
"ufo": "^1.6.4",
|
|
@@ -59,7 +66,7 @@
|
|
|
59
66
|
]
|
|
60
67
|
},
|
|
61
68
|
"engines": {
|
|
62
|
-
"node": ">=
|
|
69
|
+
"node": ">=24"
|
|
63
70
|
},
|
|
64
71
|
"packageManager": "pnpm@12.9.1"
|
|
65
72
|
}
|