Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 31 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ Install aria2.js
npm install aria2
```

then
[↑](#aria2js)

## Usage

```js
import Aria2 from "aria2";
Expand All @@ -52,9 +54,7 @@ const aria2 = new Aria2(options);

In the browser you can also use `node_modules/aria2/bundle.js` directly in `<script>` and `window.Aria2`.

[↑](#aria2js)

## Usage
Aria2 extends [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget).

Default options match aria2c defaults and are

Expand All @@ -81,10 +81,8 @@ The `"aria2."` prefix can be omitted from both methods and notifications.
`aria2.open()` opens the WebSocket connection. All subsequent requests will use the WebSocket transport instead of HTTP.

```javascript
aria2
.open()
.then(() => console.log("open"))
.catch((err) => console.log("error", err));
await aria2.open();
console.log("open");
```

[↑](#aria2js)
Expand All @@ -94,10 +92,8 @@ aria2
`aria2.close()` closes the WebSocket connection. All subsequent requests will use the HTTP transport instead of WebSocket.

```javascript
aria2
.close()
.then(() => console.log("closed"))
.catch((err) => console.log("error", err));
await aria2.close();
console.log("closed");
```

[↑](#aria2js)
Expand All @@ -111,7 +107,7 @@ Example using [`addUri`](https://aria2.github.io/manual/en/html/aria2c.html#aria
```javascript
const magnet =
"magnet:?xt=urn:btih:88594AAACBDE40EF3E2510C47374EC0AA396C08E&dn=bbb_sunflower_1080p_30fps_normal.mp4&tr=udp%3a%2f%2ftracker.openbittorrent.com%3a80%2fannounce&tr=udp%3a%2f%2ftracker.publicbt.com%3a80%2fannounce&ws=http%3a%2f%2fdistribution.bbb3d.renderfarming.net%2fvideo%2fmp4%2fbbb_sunflower_1080p_30fps_normal.mp4";
const [guid] = await aria2.call("addUri", [magnet], { dir: "/tmp" });
const guid = await aria2.call("addUri", [magnet], { dir: "/tmp" });
```

[↑](#aria2js)
Expand Down Expand Up @@ -163,9 +159,9 @@ const notifications = await aria2.listNotifications();

// notifications logger example
notifications.forEach((notification) => {
aria2.on(notification, (params) => {
console.log("aria2", notification, params);
});
aria2.addEventListener(notification, ({detail}) => {
console.log("aria2", notification, detail);
}});
});
```

Expand All @@ -191,30 +187,37 @@ const methods = await aria2.listMethods();

```javascript
// emitted when the WebSocket is open.
aria2.on("open", () => {
console.log("aria2 OPEN");
aria2.addEventListener("open", () => {
console.log("aria2", "OPEN");
});

// emitted when the WebSocket is closed.
aria2.on("close", () => {
console.log("aria2 CLOSE");
aria2.addEventListener("close", () => {
console.log("aria2", "CLOSE");
});

// emitted when error occur.
aria2.addEventListener("error", ({ error }) => {
console.error("aria2", "error", error);
});

// emitted for every message sent.
aria2.on("output", (m) => {
console.log("aria2 OUT", m);
// emitted for every data sent.
aria2.addEventListener("input", ({ data }) => {
console.log("aria2", "IN");
console.dir(data);
});

// emitted for every message received.
aria2.on("input", (m) => {
console.log("aria2 IN", m);
// emitted for every data received.
aria2.addEventListener("output", ({ data }) => {
console.log("aria2", "OUT");
console.dir(data);
});
```

Additionally every [aria2 notifications](https://aria2.github.io/manual/en/html/aria2c.html#notifications) received will be emitted as an event (with and without the `"aria2."` prefix). Only available when using WebSocket, see [open](#open).
Additionally, every [aria2 notifications](https://aria2.github.io/manual/en/html/aria2c.html#notifications) received will be emitted as an event (with and without the `"aria2."` prefix). Only available when using WebSocket, see [open](#open).

```javascript
aria2.on("onDownloadStart", ([guid]) => {
aria2.addEventListener("onDownloadStart", ({detail: [guid]}) => {
console.log("aria2 onDownloadStart", guid);
});
```
Expand Down
25 changes: 25 additions & 0 deletions example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Aria2 from "./src/Aria2.js"; // or "aria2" if installed with npm
import debug from "./src/debug.js";

(async () => {
const aria2 = new Aria2();
debug(aria2);

// comment to use HTTP
try {
await aria2.open();
} catch (err) {
console.log("Could not connect");
return;
}

aria2.addEventListener("onDownloadStart", ({ detail }) => {
console.log("Download start", detail);
});

const magnet =
"magnet:?xt=urn:btih:88594AAACBDE40EF3E2510C47374EC0AA396C08E&dn=bbb_sunflower_1080p_30fps_normal.mp4&tr=udp%3a%2f%2ftracker.openbittorrent.com%3a80%2fannounce&tr=udp%3a%2f%2ftracker.publicbt.com%3a80%2fannounce&ws=http%3a%2f%2fdistribution.bbb3d.renderfarming.net%2fvideo%2fmp4%2fbbb_sunflower_1080p_30fps_normal.mp4";
const guid = await aria2.call("addUri", [magnet], { dir: "/tmp" });

console.log("guid", guid);
})();
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"name": "aria2",
"version": "4.1.2",
"version": "5.0.0",
"description": "Library and for aria2, \"The next generation download utility.\"",
"homepage": "https://github.com/sonnyp/aria2.js",
"bugs": "https://github.com/sonnyp/aria2.js/issues",
"license": "ISC",
"type": "module",
"main": "./cjs/Aria2.js",
"module": "./src/Aria2.js",
"main": "./src/Aria2.js",
"author": "Sonny Piers <[email protected]>",
"keywords": [
"aria2",
Expand Down
10 changes: 1 addition & 9 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import nodePolyfills from "rollup-plugin-polyfill-node";

export default [
{
input: ["src/Aria2.js"],
output: {
dir: "cjs",
format: "cjs",
preserveModules: true,
exports: "auto",
},
},
{
input: "src/Aria2.js",
output: {
file: "bundle.js",
format: "iife",
name: "Aria2",
},
// Still needed?
plugins: [nodePolyfills()],
},
];
5 changes: 3 additions & 2 deletions src/Aria2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import JSONRPCClient from "./JSONRPCClient.js";
import JSONRPCClient, { JSONRPCNotificationEvent } from "./JSONRPCClient.js";

function prefix(str) {
if (!str.startsWith("system.") && !str.startsWith("aria2.")) {
Expand All @@ -24,7 +24,8 @@ class Aria2 extends JSONRPCClient {
_onnotification(notification) {
const { method, params } = notification;
const event = unprefix(method);
if (event !== method) this.emit(event, params);
if (event !== method)
this.dispatchEvent(new JSONRPCNotificationEvent(event, { params }));
return super._onnotification(notification);
}

Expand Down
90 changes: 50 additions & 40 deletions src/JSONRPCClient.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
import { EventEmitter } from "events";

import promiseEvent from "./promiseEvent.js";
import JSONRPCError from "./JSONRPCError.js";

class JSONRPCClient extends EventEmitter {
// https://github.com/nodejs/node/issues/58918
if (!globalThis.ErrorEvent) {
globalThis.ErrorEvent = class ErrorEvent extends Event {
constructor(type, options) {
super(type, options);
this.error = options?.error;
}
};
}

export class JSONRPCEvent extends Event {
constructor(type, options) {
super(type, options);
this.data = options?.data;
}
}

export class JSONRPCNotificationEvent extends Event {
constructor(type, options) {
super(type, options);
this.params = options?.params;
}
}

class JSONRPCClient extends EventTarget {
constructor(options) {
super();
this.deferreds = Object.create(null);
this.lastId = 0;

Object.assign(
this,
{ WebSocket: global.WebSocket, fetch: global.fetch.bind(this) },
this.constructor.defaultOptions,
options,
);
Object.assign(this, this.constructor.defaultOptions, options);
}

id() {
Expand All @@ -33,19 +50,12 @@ class JSONRPCClient extends EventEmitter {
);
}

websocket(message) {
return new Promise((resolve, reject) => {
const cb = (err) => {
if (err) reject(err);
else resolve();
};
this.socket.send(JSON.stringify(message), cb);
if (global.WebSocket && this.socket instanceof global.WebSocket) cb();
});
async websocket(message) {
this.socket.send(JSON.stringify(message));
}

async http(message) {
const response = await this.fetch(this.url("http"), {
const response = await fetch(this.url("http"), {
method: "POST",
body: JSON.stringify(message),
headers: {
Expand All @@ -54,14 +64,16 @@ class JSONRPCClient extends EventEmitter {
},
});

response
.json()
.then((msg) => this._onmessage(msg))
.catch((err) => {
this.emit("error", err);
});
let msg;
try {
msg = await response.json();
this._onmessage(msg);
} catch (error) {
this.dispatchEvent(new ErrorEvent("error", { error }));
throw error;
}

return response;
return msg;
}

_buildMessage(method, params) {
Expand All @@ -80,8 +92,6 @@ class JSONRPCClient extends EventEmitter {
}

async batch(calls) {
const promises = [];

const message = calls.map(([method, params]) => {
return this._buildMessage(method, params);
});
Expand All @@ -104,7 +114,7 @@ class JSONRPCClient extends EventEmitter {
}

async _send(message) {
this.emit("output", message);
this.dispatchEvent(new JSONRPCEvent("output", { data: message }));

const { socket } = this;
return socket && socket.readyState === 1
Expand All @@ -125,11 +135,11 @@ class JSONRPCClient extends EventEmitter {
}

_onnotification({ method, params }) {
this.emit(method, params);
this.dispatchEvent(new JSONRPCNotificationEvent(method, { params }));
}

_onmessage(message) {
this.emit("input", message);
this.dispatchEvent(new JSONRPCEvent("input", { data: message }));

if (Array.isArray(message)) {
for (const object of message) {
Expand All @@ -147,26 +157,26 @@ class JSONRPCClient extends EventEmitter {
}

async open() {
const socket = (this.socket = new this.WebSocket(this.url("ws")));
const socket = (this.socket = new WebSocket(this.url("ws")));

socket.onclose = (...args) => {
this.emit("close", ...args);
socket.onclose = (evt) => {
this.dispatchEvent(new Event("close"));
};
socket.onmessage = (event) => {
let message;
try {
message = JSON.parse(event.data);
} catch (err) {
this.emit("error", err);
} catch (error) {
this.dispatchEvent(new ErrorEvent("error", { error }));
return;
}
this._onmessage(message);
};
socket.onopen = (...args) => {
this.emit("open", ...args);
socket.onopen = (evt) => {
this.dispatchEvent(new Event("open"));
};
socket.onerror = (...args) => {
this.emit("error", ...args);
socket.onerror = (evt) => {
this.dispatchEvent(new ErrorEvent("error", { error: evt }));
};

return promiseEvent(this, "open");
Expand Down
Loading
Loading