Risuko
Node.js API

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)`);
  }
});

On this page