Events
Subscribe to download events from the Risuko engine.
onEvent
Register a callback to receive download events from the engine. Events are forwarded from a background task via ThreadsafeFunction; the callback runs on the JavaScript thread and is scheduled in non-blocking mode.
type EngineEventName =
| "risuko.onDownloadStart"
| "risuko.onDownloadPause"
| "risuko.onDownloadStop"
| "risuko.onDownloadComplete"
| "risuko.onDownloadError"
| "risuko.onBtDownloadComplete";
function onEvent(
callback: (eventName: EngineEventName, gid: string) => void
): Promise<void>;onEvent returns a Promise that rejects with Engine not running if the engine has not been started. Only one callback can be registered at a time: calling onEvent again replaces the previous callback, and there is no unsubscribe function. Each event carries only the event name and the gid — use tellStatus(gid) for anything else. If the callback falls behind the internal event buffer (256 events), older events are dropped.
Call onEvent after startEngine but before adding downloads to avoid missing events.
Event Types
risuko.onDownloadStart
Fired when a download is added or resumed (unpaused).
risuko.onDownloadPause
Fired when a download is paused.
risuko.onDownloadStop
Fired when a download is removed with remove(gid).
risuko.onDownloadComplete
Fired when a download finishes successfully.
risuko.onDownloadError
Fired when a download encounters an error. Use tellStatus(gid) to get the error details.
risuko.onBtDownloadComplete
Fired when a BitTorrent download finishes downloading (but may still be seeding). This is distinct from onDownloadComplete, which fires once seeding is also done — or immediately after this event if no seeding goal is configured.
Examples
Basic event logging
import { startEngine, addUri, onEvent } from "@risuko/risuko-js";
await startEngine();
await onEvent((event, gid) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${event} ${gid}`);
});
await addUri(["https://example.com/file.zip"]);Waiting for completion
import { startEngine, addUri, onEvent, stopEngine } from "@risuko/risuko-js";
await startEngine();
// One shared listener: onEvent only supports a single callback,
// so dispatch from it instead of registering per download
const pending = new Map();
await onEvent((event, gid) => {
const waiter = pending.get(gid);
if (!waiter) return;
switch (event) {
case "risuko.onDownloadComplete":
pending.delete(gid);
waiter.resolve(gid);
break;
case "risuko.onDownloadError":
pending.delete(gid);
waiter.reject(new Error(`Download failed: ${gid}`));
break;
}
});
async function downloadAndWait(urls, options) {
const gid = await addUri(urls, options);
return new Promise((resolve, reject) => {
pending.set(gid, { resolve, reject });
});
}
await downloadAndWait(["https://example.com/file.zip"], { dir: "/tmp" });
await stopEngine();Error handling
import { startEngine, addUri, onEvent, tellStatus } from "@risuko/risuko-js";
await startEngine();
await onEvent(async (event, gid) => {
if (event === "risuko.onDownloadError") {
const status = await tellStatus(gid);
console.error(`Download ${gid} failed:`);
console.error(` Code: ${status.errorCode}`);
console.error(` Message: ${status.errorMessage}`);
}
});Torrent seeding events
await onEvent((event, gid) => {
if (event === "risuko.onBtDownloadComplete") {
console.log(`Torrent ${gid} download complete, now seeding...`);
}
if (event === "risuko.onDownloadComplete") {
console.log(`Torrent ${gid} fully complete (seeding done)`);
}
});