@ctrl/transmission 2.5.0 → 4.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.
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/{transmission.d.ts → src/transmission.d.ts} +18 -24
- package/dist/{transmission.js → src/transmission.js} +84 -52
- package/dist/{types.d.ts → src/types.d.ts} +8 -3
- package/dist/src/types.js +1 -0
- package/dist/test/transmission.spec.d.ts +1 -0
- package/dist/test/transmission.spec.js +136 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +7 -0
- package/package.json +24 -30
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -14
- package/dist/types.js +0 -2
@@ -1,45 +1,38 @@
|
|
1
|
-
/// <reference types="node" />
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
2
2
|
import { Response } from 'got';
|
3
3
|
import { AddTorrentOptions as NormalizedAddTorrentOptions, AllClientData, NormalizedTorrent, TorrentClient, TorrentSettings } from '@ctrl/shared-torrent';
|
4
|
-
import { AddTorrentOptions, AddTorrentResponse, DefaultResponse, FreeSpaceResponse, GetTorrentRepsonse, RenamePathOptions, SessionArguments, SessionResponse, SetTorrentOptions
|
5
|
-
/**
|
6
|
-
* TODO: remove when hash is available on normalized torrent
|
7
|
-
* @deprecated
|
8
|
-
*/
|
9
|
-
export declare type TransmissionNormalizedTorrent = NormalizedTorrent & {
|
10
|
-
hash: string;
|
11
|
-
};
|
4
|
+
import { AddTorrentOptions, AddTorrentResponse, DefaultResponse, FreeSpaceResponse, GetTorrentRepsonse, NormalizedTorrentIds, RenamePathOptions, SessionArguments, SessionResponse, SetTorrentOptions } from './types.js';
|
12
5
|
export declare class Transmission implements TorrentClient {
|
13
6
|
config: TorrentSettings;
|
14
7
|
sessionId?: string;
|
15
8
|
constructor(options?: Partial<TorrentSettings>);
|
16
9
|
getSession(): Promise<SessionResponse>;
|
17
10
|
setSession(args: Partial<SessionArguments>): Promise<SessionResponse>;
|
18
|
-
queueTop(
|
19
|
-
queueBottom(
|
20
|
-
queueUp(
|
21
|
-
queueDown(
|
11
|
+
queueTop(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
12
|
+
queueBottom(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
13
|
+
queueUp(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
14
|
+
queueDown(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
22
15
|
freeSpace(path?: string): Promise<FreeSpaceResponse>;
|
23
|
-
pauseTorrent(
|
24
|
-
resumeTorrent(
|
25
|
-
verifyTorrent(
|
16
|
+
pauseTorrent(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
17
|
+
resumeTorrent(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
18
|
+
verifyTorrent(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
26
19
|
/**
|
27
20
|
* ask tracker for more peers
|
28
21
|
*/
|
29
|
-
reannounceTorrent(
|
30
|
-
moveTorrent(
|
22
|
+
reannounceTorrent(id: NormalizedTorrentIds): Promise<DefaultResponse>;
|
23
|
+
moveTorrent(id: NormalizedTorrentIds, location: string): Promise<DefaultResponse>;
|
31
24
|
/**
|
32
25
|
* Torrent Mutators
|
33
26
|
*/
|
34
|
-
setTorrent(
|
27
|
+
setTorrent(id: NormalizedTorrentIds, options?: Partial<SetTorrentOptions>): Promise<DefaultResponse>;
|
35
28
|
/**
|
36
29
|
* Renaming a Torrent's Path
|
37
30
|
*/
|
38
|
-
renamePath(
|
31
|
+
renamePath(id: NormalizedTorrentIds, options?: Partial<RenamePathOptions>): Promise<DefaultResponse>;
|
39
32
|
/**
|
40
33
|
* Removing a Torrent
|
41
34
|
*/
|
42
|
-
removeTorrent(
|
35
|
+
removeTorrent(id: NormalizedTorrentIds, removeData?: boolean): Promise<AddTorrentResponse>;
|
43
36
|
/**
|
44
37
|
* An alias for {@link Transmission.addMagnet}
|
45
38
|
*/
|
@@ -56,10 +49,11 @@ export declare class Transmission implements TorrentClient {
|
|
56
49
|
* @param torrent a string of file path or contents of the file as base64 string
|
57
50
|
*/
|
58
51
|
addTorrent(torrent: string | Buffer, options?: Partial<AddTorrentOptions>): Promise<AddTorrentResponse>;
|
59
|
-
normalizedAddTorrent(torrent: string | Buffer, options?: Partial<NormalizedAddTorrentOptions>): Promise<
|
60
|
-
getTorrent(id:
|
52
|
+
normalizedAddTorrent(torrent: string | Buffer, options?: Partial<NormalizedAddTorrentOptions>): Promise<NormalizedTorrent>;
|
53
|
+
getTorrent(id: NormalizedTorrentIds): Promise<NormalizedTorrent>;
|
61
54
|
getAllData(): Promise<AllClientData>;
|
62
|
-
listTorrents(
|
55
|
+
listTorrents(id?: NormalizedTorrentIds, additionalFields?: string[]): Promise<GetTorrentRepsonse>;
|
63
56
|
request<T>(method: string, args?: any): Promise<Response<T>>;
|
57
|
+
private _handleNormalizedIds;
|
64
58
|
private _normalizeTorrentData;
|
65
59
|
}
|
@@ -1,13 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
};
|
5
|
-
|
6
|
-
exports.Transmission = void 0;
|
7
|
-
const fs_1 = require("fs");
|
8
|
-
const got_1 = __importDefault(require("got"));
|
9
|
-
const shared_torrent_1 = require("@ctrl/shared-torrent");
|
10
|
-
const url_join_1 = require("@ctrl/url-join");
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
2
|
+
import got from 'got';
|
3
|
+
import { magnetDecode } from '@ctrl/magnet-link';
|
4
|
+
import { TorrentState, } from '@ctrl/shared-torrent';
|
5
|
+
import { urlJoin } from '@ctrl/url-join';
|
11
6
|
const defaults = {
|
12
7
|
baseUrl: 'http://localhost:9091/',
|
13
8
|
path: '/transmission/rpc',
|
@@ -15,8 +10,20 @@ const defaults = {
|
|
15
10
|
password: '',
|
16
11
|
timeout: 5000,
|
17
12
|
};
|
18
|
-
class Transmission {
|
13
|
+
export class Transmission {
|
19
14
|
constructor(options = {}) {
|
15
|
+
Object.defineProperty(this, "config", {
|
16
|
+
enumerable: true,
|
17
|
+
configurable: true,
|
18
|
+
writable: true,
|
19
|
+
value: void 0
|
20
|
+
});
|
21
|
+
Object.defineProperty(this, "sessionId", {
|
22
|
+
enumerable: true,
|
23
|
+
configurable: true,
|
24
|
+
writable: true,
|
25
|
+
value: void 0
|
26
|
+
});
|
20
27
|
this.config = { ...defaults, ...options };
|
21
28
|
}
|
22
29
|
async getSession() {
|
@@ -27,19 +34,23 @@ class Transmission {
|
|
27
34
|
const res = await this.request('session-set', args);
|
28
35
|
return res.body;
|
29
36
|
}
|
30
|
-
async queueTop(
|
37
|
+
async queueTop(id) {
|
38
|
+
const ids = this._handleNormalizedIds(id);
|
31
39
|
const res = await this.request('queue-move-top', { ids });
|
32
40
|
return res.body;
|
33
41
|
}
|
34
|
-
async queueBottom(
|
42
|
+
async queueBottom(id) {
|
43
|
+
const ids = this._handleNormalizedIds(id);
|
35
44
|
const res = await this.request('queue-move-bottom', { ids });
|
36
45
|
return res.body;
|
37
46
|
}
|
38
|
-
async queueUp(
|
47
|
+
async queueUp(id) {
|
48
|
+
const ids = this._handleNormalizedIds(id);
|
39
49
|
const res = await this.request('queue-move-up', { ids });
|
40
50
|
return res.body;
|
41
51
|
}
|
42
|
-
async queueDown(
|
52
|
+
async queueDown(id) {
|
53
|
+
const ids = this._handleNormalizedIds(id);
|
43
54
|
const res = await this.request('queue-move-down', { ids });
|
44
55
|
return res.body;
|
45
56
|
}
|
@@ -47,26 +58,31 @@ class Transmission {
|
|
47
58
|
const res = await this.request('free-space', { path });
|
48
59
|
return res.body;
|
49
60
|
}
|
50
|
-
async pauseTorrent(
|
61
|
+
async pauseTorrent(id) {
|
62
|
+
const ids = this._handleNormalizedIds(id);
|
51
63
|
const res = await this.request('torrent-stop', { ids });
|
52
64
|
return res.body;
|
53
65
|
}
|
54
|
-
async resumeTorrent(
|
66
|
+
async resumeTorrent(id) {
|
67
|
+
const ids = this._handleNormalizedIds(id);
|
55
68
|
const res = await this.request('torrent-start', { ids });
|
56
69
|
return res.body;
|
57
70
|
}
|
58
|
-
async verifyTorrent(
|
71
|
+
async verifyTorrent(id) {
|
72
|
+
const ids = this._handleNormalizedIds(id);
|
59
73
|
const res = await this.request('torrent-verify', { ids });
|
60
74
|
return res.body;
|
61
75
|
}
|
62
76
|
/**
|
63
77
|
* ask tracker for more peers
|
64
78
|
*/
|
65
|
-
async reannounceTorrent(
|
79
|
+
async reannounceTorrent(id) {
|
80
|
+
const ids = this._handleNormalizedIds(id);
|
66
81
|
const res = await this.request('torrent-reannounce', { ids });
|
67
82
|
return res.body;
|
68
83
|
}
|
69
|
-
async moveTorrent(
|
84
|
+
async moveTorrent(id, location) {
|
85
|
+
const ids = this._handleNormalizedIds(id);
|
70
86
|
const res = await this.request('torrent-set-location', {
|
71
87
|
ids,
|
72
88
|
move: true,
|
@@ -77,7 +93,8 @@ class Transmission {
|
|
77
93
|
/**
|
78
94
|
* Torrent Mutators
|
79
95
|
*/
|
80
|
-
async setTorrent(
|
96
|
+
async setTorrent(id, options = {}) {
|
97
|
+
const ids = this._handleNormalizedIds(id);
|
81
98
|
options.ids = ids;
|
82
99
|
const res = await this.request('torrent-set', options);
|
83
100
|
return res.body;
|
@@ -85,7 +102,8 @@ class Transmission {
|
|
85
102
|
/**
|
86
103
|
* Renaming a Torrent's Path
|
87
104
|
*/
|
88
|
-
async renamePath(
|
105
|
+
async renamePath(id, options = {}) {
|
106
|
+
const ids = this._handleNormalizedIds(id);
|
89
107
|
options.ids = ids;
|
90
108
|
const res = await this.request('torrent-rename-path', options);
|
91
109
|
return res.body;
|
@@ -93,7 +111,8 @@ class Transmission {
|
|
93
111
|
/**
|
94
112
|
* Removing a Torrent
|
95
113
|
*/
|
96
|
-
async removeTorrent(
|
114
|
+
async removeTorrent(id, removeData = true) {
|
115
|
+
const ids = this._handleNormalizedIds(id);
|
97
116
|
const res = await this.request('torrent-remove', {
|
98
117
|
ids,
|
99
118
|
'delete-local-data': removeData,
|
@@ -133,8 +152,8 @@ class Transmission {
|
|
133
152
|
...options,
|
134
153
|
};
|
135
154
|
if (typeof torrent === 'string') {
|
136
|
-
args.metainfo =
|
137
|
-
? Buffer.from(
|
155
|
+
args.metainfo = existsSync(torrent)
|
156
|
+
? Buffer.from(readFileSync(torrent)).toString('base64')
|
138
157
|
: Buffer.from(torrent, 'base64').toString('base64');
|
139
158
|
}
|
140
159
|
else {
|
@@ -148,15 +167,25 @@ class Transmission {
|
|
148
167
|
if (options.startPaused) {
|
149
168
|
torrentOptions.paused = true;
|
150
169
|
}
|
151
|
-
|
152
|
-
|
170
|
+
let torrentHash;
|
171
|
+
if (typeof torrent === 'string' && torrent.startsWith('magnet:')) {
|
172
|
+
torrentHash = magnetDecode(torrent).infoHash;
|
173
|
+
if (!torrentHash) {
|
174
|
+
throw new Error('Magnet did not contain hash');
|
175
|
+
}
|
176
|
+
await this.addMagnet(torrent, torrentOptions);
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
if (!Buffer.isBuffer(torrent)) {
|
180
|
+
torrent = Buffer.from(torrent);
|
181
|
+
}
|
182
|
+
const res = await this.addTorrent(torrent, torrentOptions);
|
183
|
+
torrentHash = res.arguments['torrent-added'].hashString;
|
153
184
|
}
|
154
|
-
const res = await this.addTorrent(torrent, torrentOptions);
|
155
|
-
const torrentId = res.arguments['torrent-added'].id;
|
156
185
|
if (options.label) {
|
157
|
-
await this.setTorrent(
|
186
|
+
await this.setTorrent(torrentHash, { labels: [options.label] });
|
158
187
|
}
|
159
|
-
return this.getTorrent(
|
188
|
+
return this.getTorrent(torrentHash);
|
160
189
|
}
|
161
190
|
async getTorrent(id) {
|
162
191
|
const result = await this.listTorrents(id);
|
@@ -186,7 +215,7 @@ class Transmission {
|
|
186
215
|
};
|
187
216
|
return results;
|
188
217
|
}
|
189
|
-
async listTorrents(
|
218
|
+
async listTorrents(id, additionalFields = []) {
|
190
219
|
const fields = [
|
191
220
|
'id',
|
192
221
|
'addedDate',
|
@@ -249,14 +278,14 @@ class Transmission {
|
|
249
278
|
...additionalFields,
|
250
279
|
];
|
251
280
|
const args = { fields };
|
252
|
-
if (
|
281
|
+
if (id) {
|
282
|
+
const ids = this._handleNormalizedIds(id);
|
253
283
|
args.ids = ids;
|
254
284
|
}
|
255
285
|
const res = await this.request('torrent-get', args);
|
256
286
|
return res.body;
|
257
287
|
}
|
258
288
|
async request(method, args = {}) {
|
259
|
-
var _a, _b, _c;
|
260
289
|
if (!this.sessionId && method !== 'session-get') {
|
261
290
|
await this.getSession();
|
262
291
|
}
|
@@ -264,27 +293,27 @@ class Transmission {
|
|
264
293
|
'X-Transmission-Session-Id': this.sessionId,
|
265
294
|
};
|
266
295
|
if (this.config.username || this.config.password) {
|
267
|
-
const str = `${
|
296
|
+
const str = `${this.config.username ?? ''}:${this.config.password ?? ''}`;
|
268
297
|
headers.Authorization = 'Basic ' + Buffer.from(str).toString('base64');
|
269
298
|
}
|
270
|
-
const url =
|
299
|
+
const url = urlJoin(this.config.baseUrl, this.config.path);
|
271
300
|
try {
|
272
|
-
const res = await
|
301
|
+
const res = await got.post(url, {
|
273
302
|
json: {
|
274
303
|
method,
|
275
304
|
arguments: args,
|
276
305
|
},
|
277
306
|
headers,
|
278
|
-
retry: 0,
|
307
|
+
retry: { limit: 0 },
|
279
308
|
// allow proxy agent
|
280
|
-
|
281
|
-
timeout: this.config.timeout,
|
309
|
+
timeout: { request: this.config.timeout },
|
282
310
|
responseType: 'json',
|
311
|
+
...(this.config.agent ? { agent: this.config.agent } : {}),
|
283
312
|
});
|
284
313
|
return res;
|
285
314
|
}
|
286
315
|
catch (error) {
|
287
|
-
if (
|
316
|
+
if (error?.response?.statusCode === 409) {
|
288
317
|
this.sessionId = error.response.headers['x-transmission-session-id'];
|
289
318
|
// eslint-disable-next-line no-return-await
|
290
319
|
return await this.request(method, args);
|
@@ -293,31 +322,35 @@ class Transmission {
|
|
293
322
|
throw error;
|
294
323
|
}
|
295
324
|
}
|
325
|
+
_handleNormalizedIds(ids) {
|
326
|
+
if (typeof ids === 'string' && ids !== 'recently-active') {
|
327
|
+
return [ids];
|
328
|
+
}
|
329
|
+
return ids;
|
330
|
+
}
|
296
331
|
_normalizeTorrentData(torrent) {
|
297
|
-
var _a;
|
298
332
|
const dateAdded = new Date(torrent.addedDate * 1000).toISOString();
|
299
333
|
const dateCompleted = new Date(torrent.doneDate * 1000).toISOString();
|
300
334
|
// normalize state to enum
|
301
335
|
// https://github.com/transmission/transmission/blob/c11f2870fd18ff781ca06ce84b6d43541f3293dd/web/javascript/torrent.js#L18
|
302
|
-
let state =
|
336
|
+
let state = TorrentState.unknown;
|
303
337
|
if (torrent.status === 6) {
|
304
|
-
state =
|
338
|
+
state = TorrentState.seeding;
|
305
339
|
}
|
306
340
|
else if (torrent.status === 4) {
|
307
|
-
state =
|
341
|
+
state = TorrentState.downloading;
|
308
342
|
}
|
309
343
|
else if (torrent.status === 0) {
|
310
|
-
state =
|
344
|
+
state = TorrentState.paused;
|
311
345
|
}
|
312
346
|
else if (torrent.status === 2) {
|
313
|
-
state =
|
347
|
+
state = TorrentState.checking;
|
314
348
|
}
|
315
349
|
else if (torrent.status === 3 || torrent.status === 5) {
|
316
|
-
state =
|
350
|
+
state = TorrentState.queued;
|
317
351
|
}
|
318
352
|
return {
|
319
|
-
id: torrent.
|
320
|
-
hash: torrent.hashString,
|
353
|
+
id: torrent.hashString,
|
321
354
|
name: torrent.name,
|
322
355
|
state,
|
323
356
|
isCompleted: torrent.leftUntilDone < 1,
|
@@ -326,7 +359,7 @@ class Transmission {
|
|
326
359
|
ratio: torrent.uploadRatio,
|
327
360
|
dateAdded,
|
328
361
|
dateCompleted,
|
329
|
-
label:
|
362
|
+
label: torrent.labels?.length ? torrent.labels[0] : undefined,
|
330
363
|
savePath: torrent.downloadDir,
|
331
364
|
uploadSpeed: torrent.rateUpload,
|
332
365
|
downloadSpeed: torrent.rateDownload,
|
@@ -343,4 +376,3 @@ class Transmission {
|
|
343
376
|
};
|
344
377
|
}
|
345
378
|
}
|
346
|
-
exports.Transmission = Transmission;
|
@@ -39,12 +39,17 @@ export interface FreeSpaceResponse extends DefaultResponse {
|
|
39
39
|
/**
|
40
40
|
* "ids", which specifies which torrents to use.
|
41
41
|
* All torrents are used if the "ids" argument is omitted.
|
42
|
+
*
|
42
43
|
* "ids" should be one of the following:
|
43
|
-
*
|
44
|
-
*
|
45
|
-
*
|
44
|
+
* 1. an integer referring to a torrent id
|
45
|
+
* 2. a list of torrent id numbers, sha1 hash strings, or both
|
46
|
+
* 3. a string, "recently-active", for recently-active torrents
|
46
47
|
*/
|
47
48
|
export declare type TorrentIds = number | 'recently-active' | Array<number | string>;
|
49
|
+
/**
|
50
|
+
* Allows the user to pass a single hash, this will be converted to an array
|
51
|
+
*/
|
52
|
+
export declare type NormalizedTorrentIds = TorrentIds | string;
|
48
53
|
export interface GetTorrentRepsonse extends DefaultResponse {
|
49
54
|
arguments: {
|
50
55
|
removed: Torrent[];
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,136 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
import path from 'path';
|
3
|
+
import pWaitFor from 'p-wait-for';
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
5
|
+
import { TorrentState } from '@ctrl/shared-torrent';
|
6
|
+
import { Transmission } from '../src/index.js';
|
7
|
+
const baseUrl = 'http://localhost:9091/';
|
8
|
+
const torrentName = 'ubuntu-18.04.1-desktop-amd64.iso';
|
9
|
+
const torrentFile = path.join(__dirname, '/ubuntu-18.04.1-desktop-amd64.iso.torrent');
|
10
|
+
async function setupTorrent(transmission) {
|
11
|
+
const res = await transmission.addTorrent(torrentFile);
|
12
|
+
await pWaitFor(async () => {
|
13
|
+
const r = await transmission.listTorrents(undefined, ['id']);
|
14
|
+
return r.arguments.torrents.length === 1;
|
15
|
+
}, { timeout: 10000, interval: 200 });
|
16
|
+
return res.arguments['torrent-added'].hashString;
|
17
|
+
}
|
18
|
+
describe('Transmission', () => {
|
19
|
+
afterEach(async () => {
|
20
|
+
const transmission = new Transmission({ baseUrl });
|
21
|
+
const res = await transmission.listTorrents();
|
22
|
+
// clean up all torrents
|
23
|
+
for (const torrent of res.arguments.torrents) {
|
24
|
+
// eslint-disable-next-line no-await-in-loop
|
25
|
+
await transmission.removeTorrent(torrent.id, false);
|
26
|
+
}
|
27
|
+
});
|
28
|
+
it('should be instantiable', () => {
|
29
|
+
const transmission = new Transmission({ baseUrl });
|
30
|
+
expect(transmission).toBeTruthy();
|
31
|
+
});
|
32
|
+
it('should add torrent from file path string', async () => {
|
33
|
+
const transmission = new Transmission({ baseUrl });
|
34
|
+
const res = await transmission.addTorrent(torrentFile);
|
35
|
+
expect(res.result).toBe('success');
|
36
|
+
});
|
37
|
+
it('should add magnet link', async () => {
|
38
|
+
const magnet = 'magnet:?xt=urn:btih:B0B81206633C42874173D22E564D293DAEFC45E2&dn=Ubuntu+11+10+Alternate+Amd64+Iso&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.open-internet.nl%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.demonii.si%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.pirateparty.gr%3A6969%2Fannounce&tr=udp%3A%2F%2Fdenis.stalker.upeer.me%3A6969%2Fannounce&tr=udp%3A%2F%2Fp4p.arenabg.com%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce';
|
39
|
+
const client = new Transmission({ baseUrl });
|
40
|
+
const res = await client.addMagnet(magnet);
|
41
|
+
expect(res.result).toBe('success');
|
42
|
+
});
|
43
|
+
it('should add normalized magnet link', async () => {
|
44
|
+
const magnet = 'magnet:?xt=urn:btih:B0B81206633C42874173D22E564D293DAEFC45E2&dn=Ubuntu+11+10+Alternate+Amd64+Iso&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.open-internet.nl%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.demonii.si%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.pirateparty.gr%3A6969%2Fannounce&tr=udp%3A%2F%2Fdenis.stalker.upeer.me%3A6969%2Fannounce&tr=udp%3A%2F%2Fp4p.arenabg.com%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce';
|
45
|
+
const client = new Transmission({ baseUrl });
|
46
|
+
const res = await client.normalizedAddTorrent(magnet);
|
47
|
+
expect(res.id).toBeTruthy();
|
48
|
+
});
|
49
|
+
it('should add torrent from file buffer', async () => {
|
50
|
+
const transmission = new Transmission({ baseUrl });
|
51
|
+
const res = await transmission.addTorrent(fs.readFileSync(torrentFile));
|
52
|
+
expect(res.result).toBe('success');
|
53
|
+
});
|
54
|
+
it('should add torrent from file contents base64', async () => {
|
55
|
+
const transmission = new Transmission({ baseUrl });
|
56
|
+
const contents = Buffer.from(fs.readFileSync(torrentFile)).toString('base64');
|
57
|
+
const res = await transmission.addTorrent(contents);
|
58
|
+
expect(res.result).toBe('success');
|
59
|
+
});
|
60
|
+
it('should get torrents', async () => {
|
61
|
+
const transmission = new Transmission({ baseUrl });
|
62
|
+
await setupTorrent(transmission);
|
63
|
+
const res = await transmission.listTorrents(undefined, ['id']);
|
64
|
+
expect(res.arguments.torrents).toHaveLength(1);
|
65
|
+
});
|
66
|
+
it('should get normalized all torrent data', async () => {
|
67
|
+
const transmission = new Transmission({ baseUrl });
|
68
|
+
await setupTorrent(transmission);
|
69
|
+
const res = await transmission.getAllData();
|
70
|
+
expect(res.torrents).toHaveLength(1);
|
71
|
+
expect(res.torrents[0].name).toBe(torrentName);
|
72
|
+
});
|
73
|
+
it('should get normalized torrent data', async () => {
|
74
|
+
const transmission = new Transmission({ baseUrl });
|
75
|
+
const id = await setupTorrent(transmission);
|
76
|
+
const res = await transmission.getTorrent(id);
|
77
|
+
expect(res.name).toBe(torrentName);
|
78
|
+
});
|
79
|
+
it('should remove torrent', async () => {
|
80
|
+
const transmission = new Transmission({ baseUrl });
|
81
|
+
const key = await setupTorrent(transmission);
|
82
|
+
await transmission.removeTorrent(key, false);
|
83
|
+
});
|
84
|
+
it('should verify torrent', async () => {
|
85
|
+
const transmission = new Transmission({ baseUrl });
|
86
|
+
const key = await setupTorrent(transmission);
|
87
|
+
await transmission.verifyTorrent(key);
|
88
|
+
});
|
89
|
+
it('should move in queue', async () => {
|
90
|
+
const transmission = new Transmission({ baseUrl });
|
91
|
+
const key = await setupTorrent(transmission);
|
92
|
+
await transmission.queueUp(key);
|
93
|
+
await transmission.queueDown(key);
|
94
|
+
await transmission.queueTop(key);
|
95
|
+
await transmission.queueBottom(key);
|
96
|
+
});
|
97
|
+
it('should report free space', async () => {
|
98
|
+
const transmission = new Transmission({ baseUrl });
|
99
|
+
const p = '/downloads';
|
100
|
+
const res = await transmission.freeSpace(p);
|
101
|
+
expect(res.result).toBe('success');
|
102
|
+
expect(res.arguments.path).toBe(p);
|
103
|
+
expect(typeof res.arguments['size-bytes']).toBe('number');
|
104
|
+
});
|
105
|
+
it('should add from url', async () => {
|
106
|
+
const transmission = new Transmission({ baseUrl });
|
107
|
+
const res = await transmission.addUrl('https://releases.ubuntu.com/20.10/ubuntu-20.10-desktop-amd64.iso.torrent');
|
108
|
+
expect(res.result).toBe('success');
|
109
|
+
});
|
110
|
+
it('should add torrent with normalized response', async () => {
|
111
|
+
const client = new Transmission({ baseUrl });
|
112
|
+
const torrent = await client.normalizedAddTorrent(fs.readFileSync(torrentFile), {
|
113
|
+
label: 'test',
|
114
|
+
});
|
115
|
+
expect(torrent.connectedPeers).toBe(0);
|
116
|
+
expect(torrent.connectedSeeds).toBe(0);
|
117
|
+
expect(torrent.downloadSpeed).toBe(0);
|
118
|
+
expect(torrent.eta).toBe(-1);
|
119
|
+
expect(torrent.isCompleted).toBe(false);
|
120
|
+
expect(torrent.label).toBe('test');
|
121
|
+
expect(torrent.name).toBe(torrentName);
|
122
|
+
expect(torrent.progress).toBeGreaterThanOrEqual(0);
|
123
|
+
expect(torrent.queuePosition).toBe(0);
|
124
|
+
// expect(torrent.ratio).toBe(0);
|
125
|
+
expect(torrent.savePath).toBe('/downloads');
|
126
|
+
expect(torrent.state).toBe(TorrentState.checking);
|
127
|
+
expect(torrent.stateMessage).toBe('');
|
128
|
+
expect(torrent.totalDownloaded).toBe(0);
|
129
|
+
expect(torrent.totalPeers).toBe(0);
|
130
|
+
expect(torrent.totalSeeds).toBe(0);
|
131
|
+
expect(torrent.totalSelected).toBe(1953349632);
|
132
|
+
// expect(torrent.totalSize).toBe(undefined);
|
133
|
+
expect(torrent.totalUploaded).toBe(0);
|
134
|
+
expect(torrent.uploadSpeed).toBe(0);
|
135
|
+
});
|
136
|
+
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ctrl/transmission",
|
3
|
-
"version": "
|
3
|
+
"version": "4.1.0",
|
4
4
|
"description": "TypeScript api wrapper for transmission using got",
|
5
5
|
"author": "Scott Cooper <scttcper@gmail.com>",
|
6
6
|
"license": "MIT",
|
@@ -9,8 +9,9 @@
|
|
9
9
|
"transmission",
|
10
10
|
"typescript"
|
11
11
|
],
|
12
|
-
"
|
13
|
-
"
|
12
|
+
"type": "module",
|
13
|
+
"main": "./dist/src/index.js",
|
14
|
+
"typings": "./dist/src/index.d.ts",
|
14
15
|
"files": [
|
15
16
|
"dist"
|
16
17
|
],
|
@@ -19,48 +20,41 @@
|
|
19
20
|
"lint": "eslint --ext .ts .",
|
20
21
|
"lint:fix": "eslint --fix --ext .ts .",
|
21
22
|
"prepare": "npm run build",
|
22
|
-
"build": "tsc
|
23
|
+
"build": "tsc",
|
23
24
|
"build:docs": "typedoc",
|
24
|
-
"test": "
|
25
|
-
"test:watch": "
|
26
|
-
"test:ci": "
|
25
|
+
"test": "vitest run",
|
26
|
+
"test:watch": "vitest",
|
27
|
+
"test:ci": "vitest run --coverage"
|
27
28
|
},
|
28
29
|
"dependencies": {
|
29
|
-
"@ctrl/
|
30
|
-
"
|
31
|
-
"@ctrl/url-join": "^
|
30
|
+
"@ctrl/magnet-link": "^3.1.0",
|
31
|
+
"@ctrl/shared-torrent": "^4.1.1",
|
32
|
+
"@ctrl/url-join": "^2.0.0",
|
33
|
+
"got": "^12.1.0"
|
32
34
|
},
|
33
35
|
"devDependencies": {
|
34
|
-
"@
|
35
|
-
"@
|
36
|
-
"@
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"typedoc": "0.22.11",
|
43
|
-
"typescript": "4.5.5"
|
36
|
+
"@ctrl/eslint-config": "3.4.4",
|
37
|
+
"@sindresorhus/tsconfig": "3.0.1",
|
38
|
+
"@types/node": "17.0.38",
|
39
|
+
"c8": "7.11.3",
|
40
|
+
"p-wait-for": "4.1.0",
|
41
|
+
"typedoc": "0.22.17",
|
42
|
+
"typescript": "4.7.2",
|
43
|
+
"vitest": "0.13.1"
|
44
44
|
},
|
45
45
|
"jest": {
|
46
46
|
"testEnvironment": "node",
|
47
47
|
"coverageProvider": "v8"
|
48
48
|
},
|
49
|
-
"babel": {
|
50
|
-
"presets": [
|
51
|
-
"@babel/preset-typescript"
|
52
|
-
],
|
53
|
-
"plugins": [
|
54
|
-
"@babel/plugin-transform-modules-commonjs"
|
55
|
-
]
|
56
|
-
},
|
57
49
|
"publishConfig": {
|
58
50
|
"access": "public"
|
59
51
|
},
|
60
52
|
"release": {
|
61
|
-
"
|
53
|
+
"branches": [
|
54
|
+
"master"
|
55
|
+
]
|
62
56
|
},
|
63
57
|
"engines": {
|
64
|
-
"node": ">=
|
58
|
+
"node": ">=14.16"
|
65
59
|
}
|
66
60
|
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
-
if (k2 === undefined) k2 = k;
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5
|
-
}) : (function(o, m, k, k2) {
|
6
|
-
if (k2 === undefined) k2 = k;
|
7
|
-
o[k2] = m[k];
|
8
|
-
}));
|
9
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
10
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
11
|
-
};
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
13
|
-
__exportStar(require("./transmission"), exports);
|
14
|
-
__exportStar(require("./types"), exports);
|
package/dist/types.js
DELETED