@ctrl/transmission 3.0.0 → 4.0.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} +2 -2
- package/dist/{transmission.js → src/transmission.js} +33 -30
- package/dist/{types.d.ts → src/types.d.ts} +0 -0
- package/dist/src/types.js +1 -0
- package/dist/test/transmission.spec.d.ts +1 -0
- package/dist/test/transmission.spec.js +130 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +7 -0
- package/package.json +23 -30
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -14
- package/dist/types.js +0 -2
@@ -1,7 +1,7 @@
|
|
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, NormalizedTorrentIds, RenamePathOptions, SessionArguments, SessionResponse, SetTorrentOptions } from './types';
|
4
|
+
import { AddTorrentOptions, AddTorrentResponse, DefaultResponse, FreeSpaceResponse, GetTorrentRepsonse, NormalizedTorrentIds, RenamePathOptions, SessionArguments, SessionResponse, SetTorrentOptions } from './types.js';
|
5
5
|
export declare class Transmission implements TorrentClient {
|
6
6
|
config: TorrentSettings;
|
7
7
|
sessionId?: string;
|
@@ -1,13 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
};
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
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 { TorrentState, } from '@ctrl/shared-torrent';
|
4
|
+
import { urlJoin } from '@ctrl/url-join';
|
11
5
|
const defaults = {
|
12
6
|
baseUrl: 'http://localhost:9091/',
|
13
7
|
path: '/transmission/rpc',
|
@@ -15,8 +9,20 @@ const defaults = {
|
|
15
9
|
password: '',
|
16
10
|
timeout: 5000,
|
17
11
|
};
|
18
|
-
class Transmission {
|
12
|
+
export class Transmission {
|
19
13
|
constructor(options = {}) {
|
14
|
+
Object.defineProperty(this, "config", {
|
15
|
+
enumerable: true,
|
16
|
+
configurable: true,
|
17
|
+
writable: true,
|
18
|
+
value: void 0
|
19
|
+
});
|
20
|
+
Object.defineProperty(this, "sessionId", {
|
21
|
+
enumerable: true,
|
22
|
+
configurable: true,
|
23
|
+
writable: true,
|
24
|
+
value: void 0
|
25
|
+
});
|
20
26
|
this.config = { ...defaults, ...options };
|
21
27
|
}
|
22
28
|
async getSession() {
|
@@ -145,8 +151,8 @@ class Transmission {
|
|
145
151
|
...options,
|
146
152
|
};
|
147
153
|
if (typeof torrent === 'string') {
|
148
|
-
args.metainfo =
|
149
|
-
? Buffer.from(
|
154
|
+
args.metainfo = existsSync(torrent)
|
155
|
+
? Buffer.from(readFileSync(torrent)).toString('base64')
|
150
156
|
: Buffer.from(torrent, 'base64').toString('base64');
|
151
157
|
}
|
152
158
|
else {
|
@@ -269,7 +275,6 @@ class Transmission {
|
|
269
275
|
return res.body;
|
270
276
|
}
|
271
277
|
async request(method, args = {}) {
|
272
|
-
var _a, _b, _c;
|
273
278
|
if (!this.sessionId && method !== 'session-get') {
|
274
279
|
await this.getSession();
|
275
280
|
}
|
@@ -277,27 +282,27 @@ class Transmission {
|
|
277
282
|
'X-Transmission-Session-Id': this.sessionId,
|
278
283
|
};
|
279
284
|
if (this.config.username || this.config.password) {
|
280
|
-
const str = `${
|
285
|
+
const str = `${this.config.username ?? ''}:${this.config.password ?? ''}`;
|
281
286
|
headers.Authorization = 'Basic ' + Buffer.from(str).toString('base64');
|
282
287
|
}
|
283
|
-
const url =
|
288
|
+
const url = urlJoin(this.config.baseUrl, this.config.path);
|
284
289
|
try {
|
285
|
-
const res = await
|
290
|
+
const res = await got.post(url, {
|
286
291
|
json: {
|
287
292
|
method,
|
288
293
|
arguments: args,
|
289
294
|
},
|
290
295
|
headers,
|
291
|
-
retry: 0,
|
296
|
+
retry: { limit: 0 },
|
292
297
|
// allow proxy agent
|
293
|
-
|
294
|
-
timeout: this.config.timeout,
|
298
|
+
timeout: { request: this.config.timeout },
|
295
299
|
responseType: 'json',
|
300
|
+
...(this.config.agent ? { agent: this.config.agent } : {}),
|
296
301
|
});
|
297
302
|
return res;
|
298
303
|
}
|
299
304
|
catch (error) {
|
300
|
-
if (
|
305
|
+
if (error?.response?.statusCode === 409) {
|
301
306
|
this.sessionId = error.response.headers['x-transmission-session-id'];
|
302
307
|
// eslint-disable-next-line no-return-await
|
303
308
|
return await this.request(method, args);
|
@@ -313,26 +318,25 @@ class Transmission {
|
|
313
318
|
return ids;
|
314
319
|
}
|
315
320
|
_normalizeTorrentData(torrent) {
|
316
|
-
var _a;
|
317
321
|
const dateAdded = new Date(torrent.addedDate * 1000).toISOString();
|
318
322
|
const dateCompleted = new Date(torrent.doneDate * 1000).toISOString();
|
319
323
|
// normalize state to enum
|
320
324
|
// https://github.com/transmission/transmission/blob/c11f2870fd18ff781ca06ce84b6d43541f3293dd/web/javascript/torrent.js#L18
|
321
|
-
let state =
|
325
|
+
let state = TorrentState.unknown;
|
322
326
|
if (torrent.status === 6) {
|
323
|
-
state =
|
327
|
+
state = TorrentState.seeding;
|
324
328
|
}
|
325
329
|
else if (torrent.status === 4) {
|
326
|
-
state =
|
330
|
+
state = TorrentState.downloading;
|
327
331
|
}
|
328
332
|
else if (torrent.status === 0) {
|
329
|
-
state =
|
333
|
+
state = TorrentState.paused;
|
330
334
|
}
|
331
335
|
else if (torrent.status === 2) {
|
332
|
-
state =
|
336
|
+
state = TorrentState.checking;
|
333
337
|
}
|
334
338
|
else if (torrent.status === 3 || torrent.status === 5) {
|
335
|
-
state =
|
339
|
+
state = TorrentState.queued;
|
336
340
|
}
|
337
341
|
return {
|
338
342
|
id: torrent.hashString,
|
@@ -344,7 +348,7 @@ class Transmission {
|
|
344
348
|
ratio: torrent.uploadRatio,
|
345
349
|
dateAdded,
|
346
350
|
dateCompleted,
|
347
|
-
label:
|
351
|
+
label: torrent.labels?.length ? torrent.labels[0] : undefined,
|
348
352
|
savePath: torrent.downloadDir,
|
349
353
|
uploadSpeed: torrent.rateUpload,
|
350
354
|
downloadSpeed: torrent.rateDownload,
|
@@ -361,4 +365,3 @@ class Transmission {
|
|
361
365
|
};
|
362
366
|
}
|
363
367
|
}
|
364
|
-
exports.Transmission = Transmission;
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,130 @@
|
|
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 torrent from file buffer', async () => {
|
44
|
+
const transmission = new Transmission({ baseUrl });
|
45
|
+
const res = await transmission.addTorrent(fs.readFileSync(torrentFile));
|
46
|
+
expect(res.result).toBe('success');
|
47
|
+
});
|
48
|
+
it('should add torrent from file contents base64', async () => {
|
49
|
+
const transmission = new Transmission({ baseUrl });
|
50
|
+
const contents = Buffer.from(fs.readFileSync(torrentFile)).toString('base64');
|
51
|
+
const res = await transmission.addTorrent(contents);
|
52
|
+
expect(res.result).toBe('success');
|
53
|
+
});
|
54
|
+
it('should get torrents', async () => {
|
55
|
+
const transmission = new Transmission({ baseUrl });
|
56
|
+
await setupTorrent(transmission);
|
57
|
+
const res = await transmission.listTorrents(undefined, ['id']);
|
58
|
+
expect(res.arguments.torrents).toHaveLength(1);
|
59
|
+
});
|
60
|
+
it('should get normalized all torrent data', async () => {
|
61
|
+
const transmission = new Transmission({ baseUrl });
|
62
|
+
await setupTorrent(transmission);
|
63
|
+
const res = await transmission.getAllData();
|
64
|
+
expect(res.torrents).toHaveLength(1);
|
65
|
+
expect(res.torrents[0].name).toBe(torrentName);
|
66
|
+
});
|
67
|
+
it('should get normalized torrent data', async () => {
|
68
|
+
const transmission = new Transmission({ baseUrl });
|
69
|
+
const id = await setupTorrent(transmission);
|
70
|
+
const res = await transmission.getTorrent(id);
|
71
|
+
expect(res.name).toBe(torrentName);
|
72
|
+
});
|
73
|
+
it('should remove torrent', async () => {
|
74
|
+
const transmission = new Transmission({ baseUrl });
|
75
|
+
const key = await setupTorrent(transmission);
|
76
|
+
await transmission.removeTorrent(key, false);
|
77
|
+
});
|
78
|
+
it('should verify torrent', async () => {
|
79
|
+
const transmission = new Transmission({ baseUrl });
|
80
|
+
const key = await setupTorrent(transmission);
|
81
|
+
await transmission.verifyTorrent(key);
|
82
|
+
});
|
83
|
+
it('should move in queue', async () => {
|
84
|
+
const transmission = new Transmission({ baseUrl });
|
85
|
+
const key = await setupTorrent(transmission);
|
86
|
+
await transmission.queueUp(key);
|
87
|
+
await transmission.queueDown(key);
|
88
|
+
await transmission.queueTop(key);
|
89
|
+
await transmission.queueBottom(key);
|
90
|
+
});
|
91
|
+
it('should report free space', async () => {
|
92
|
+
const transmission = new Transmission({ baseUrl });
|
93
|
+
const p = '/downloads';
|
94
|
+
const res = await transmission.freeSpace(p);
|
95
|
+
expect(res.result).toBe('success');
|
96
|
+
expect(res.arguments.path).toBe(p);
|
97
|
+
expect(typeof res.arguments['size-bytes']).toBe('number');
|
98
|
+
});
|
99
|
+
it('should add from url', async () => {
|
100
|
+
const transmission = new Transmission({ baseUrl });
|
101
|
+
const res = await transmission.addUrl('https://releases.ubuntu.com/20.10/ubuntu-20.10-desktop-amd64.iso.torrent');
|
102
|
+
expect(res.result).toBe('success');
|
103
|
+
});
|
104
|
+
it('should add torrent with normalized response', async () => {
|
105
|
+
const client = new Transmission({ baseUrl });
|
106
|
+
const torrent = await client.normalizedAddTorrent(fs.readFileSync(torrentFile), {
|
107
|
+
label: 'test',
|
108
|
+
});
|
109
|
+
expect(torrent.connectedPeers).toBe(0);
|
110
|
+
expect(torrent.connectedSeeds).toBe(0);
|
111
|
+
expect(torrent.downloadSpeed).toBe(0);
|
112
|
+
expect(torrent.eta).toBe(-1);
|
113
|
+
expect(torrent.isCompleted).toBe(false);
|
114
|
+
expect(torrent.label).toBe('test');
|
115
|
+
expect(torrent.name).toBe(torrentName);
|
116
|
+
expect(torrent.progress).toBeGreaterThanOrEqual(0);
|
117
|
+
expect(torrent.queuePosition).toBe(0);
|
118
|
+
// expect(torrent.ratio).toBe(0);
|
119
|
+
expect(torrent.savePath).toBe('/downloads');
|
120
|
+
expect(torrent.state).toBe(TorrentState.checking);
|
121
|
+
expect(torrent.stateMessage).toBe('');
|
122
|
+
expect(torrent.totalDownloaded).toBe(0);
|
123
|
+
expect(torrent.totalPeers).toBe(0);
|
124
|
+
expect(torrent.totalSeeds).toBe(0);
|
125
|
+
expect(torrent.totalSelected).toBe(1953349632);
|
126
|
+
// expect(torrent.totalSize).toBe(undefined);
|
127
|
+
expect(torrent.totalUploaded).toBe(0);
|
128
|
+
expect(torrent.uploadSpeed).toBe(0);
|
129
|
+
});
|
130
|
+
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ctrl/transmission",
|
3
|
-
"version": "
|
3
|
+
"version": "4.0.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,40 @@
|
|
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/shared-torrent": "^
|
30
|
-
"
|
31
|
-
"
|
30
|
+
"@ctrl/shared-torrent": "^4.1.0",
|
31
|
+
"@ctrl/url-join": "^2.0.0",
|
32
|
+
"got": "^12.1.0"
|
32
33
|
},
|
33
34
|
"devDependencies": {
|
34
|
-
"@
|
35
|
-
"@
|
36
|
-
"@
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"typedoc": "0.22.11",
|
43
|
-
"typescript": "4.5.5"
|
35
|
+
"@ctrl/eslint-config": "3.4.4",
|
36
|
+
"@sindresorhus/tsconfig": "3.0.1",
|
37
|
+
"@types/node": "17.0.38",
|
38
|
+
"c8": "7.11.3",
|
39
|
+
"p-wait-for": "4.1.0",
|
40
|
+
"typedoc": "0.22.17",
|
41
|
+
"typescript": "4.7.2",
|
42
|
+
"vitest": "0.13.1"
|
44
43
|
},
|
45
44
|
"jest": {
|
46
45
|
"testEnvironment": "node",
|
47
46
|
"coverageProvider": "v8"
|
48
47
|
},
|
49
|
-
"babel": {
|
50
|
-
"presets": [
|
51
|
-
"@babel/preset-typescript"
|
52
|
-
],
|
53
|
-
"plugins": [
|
54
|
-
"@babel/plugin-transform-modules-commonjs"
|
55
|
-
]
|
56
|
-
},
|
57
48
|
"publishConfig": {
|
58
49
|
"access": "public"
|
59
50
|
},
|
60
51
|
"release": {
|
61
|
-
"
|
52
|
+
"branches": [
|
53
|
+
"master"
|
54
|
+
]
|
62
55
|
},
|
63
56
|
"engines": {
|
64
|
-
"node": ">=
|
57
|
+
"node": ">=14.16"
|
65
58
|
}
|
66
59
|
}
|
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