Risuko
Guides

Batch Downloads

Download multiple files efficiently with Risuko.

Per-URL Rename Directive

In the desktop app's Add Task dialog, append Rename: <filename> to any URL on its own line to override the output filename for just that link:

https://example.com/abc123/file.bin Rename: ubuntu.iso
https://example.com/xyz/raw?id=42  Rename: report.pdf
https://example.com/another.zip
  • The directive is matched case-insensitively at the end of a line and separated from the URL by whitespace.
  • Filenames may contain spaces; path separators (/, \) are rejected — the directive is stripped from the line and the URL is used without a rename.
  • A per-line Rename: always wins over the form-level Rename field (which still supports the index expansion rule, e.g. file-(01+1).zip).
  • Magnet links and .torrent files are not affected by this directive (their output names come from the torrent metadata).

For thunder:// links and curl ... paste-as-task lines, the directive is parsed off the line first; the remaining link or command is decoded afterwards.

Scheduled Starts

The desktop app can hold URI tasks until a future time. In the Add Task dialog, set Time before submitting the batch; Risuko stores the Unix timestamp as the risuko-start-at task option and creates each non-torrent item with status scheduled.

  • Scheduled items live in the Scheduled list until their startAt time.
  • Torrent items ignore the schedule field and start immediately.
  • Existing non-torrent tasks can be scheduled, rescheduled, or started now from the task action menu.
  • If the app misses the start time by more than 5 minutes, the task remains scheduled with scheduleMissed: true and the desktop app offers to start the missed tasks manually.

Queue Order And Concurrency

Drag task rows, or use the keyboard reorder handle, to move tasks in the All, Active, Waiting, and Scheduled lists when default sorting is active and no filter is applied. The order is persisted through the engine session.

For non-torrent tasks, max-concurrent-downloads is a strict queue cap. When a higher-priority waiting or due scheduled task should run, the engine preempts a lower-priority active worker back to waiting and restarts it later. BitTorrent tasks are managed by the torrent engine and are not preempted by this cap.

CLI Batch Downloads

Passing multiple URLs to a single risuko download does not batch: the URLs are treated as HTTP mirrors of one file. Run one download per file instead.

From a URL List File

# urls.txt — one URL per line
while IFS= read -r url; do
  risuko download "$url" -d ~/Downloads
done < urls.txt

Parallel with Wait

# Download multiple files concurrently
risuko download https://example.com/file1.zip &
risuko download https://example.com/file2.zip &
risuko download https://example.com/file3.zip &
wait
echo "All downloads complete"

Start an engine before running downloads in parallel (the desktop app, or risuko serve in another terminal). When no engine is running, download starts a temporary in-process engine and shuts it down as soon as its own download finishes — killing any sibling downloads that attached to it.

With JSON Scripting

# Get all GIDs, then wait for completion
for url in $(cat urls.txt); do
  risuko download "$url" --json 2>/dev/null &
done
wait

# Check final status
risuko status --json | jq '.[] | {gid, status, name: .files[0].path}'

Managing a Batch

risuko global-stat   # overall speeds and active/waiting/stopped counts
risuko pause-all
risuko resume-all
risuko purge         # clear completed/error/removed results

Node.js Batch Downloads

Simple Batch

import { startEngine, addUri } from "@risuko/risuko-js";

const urls = [
  "https://example.com/file1.zip",
  "https://example.com/file2.zip",
  "https://example.com/file3.zip",
];

await startEngine();

const gids = [];
for (const url of urls) {
  const gid = await addUri([url], { dir: "/downloads" });
  gids.push(gid);
}

console.log(`Started ${gids.length} downloads`);

With Concurrency Control

import { startEngine, changeGlobalOption, addUri, onEvent } from "@risuko/risuko-js";

await startEngine();

// Limit to 3 concurrent downloads
await changeGlobalOption({ "max-concurrent-downloads": "3" });

const urls = Array.from({ length: 20 }, (_, i) =>
  `https://example.com/file${i + 1}.zip`
);

let completed = 0;
onEvent((eventName, gid) => {
  if (eventName === "risuko.onDownloadComplete") {
    completed++;
    console.log(`Progress: ${completed}/${urls.length} (${gid})`);
  }
});

for (const url of urls) {
  await addUri([url], { dir: "/downloads" });
}

With Speed Limiting

import { startEngine, changeGlobalOption } from "@risuko/risuko-js";

await startEngine();

// Limit total bandwidth to 50 MB/s
await changeGlobalOption({
  "max-overall-download-limit": String(50 * 1024 * 1024),
});

Global Speed Limiting via CLI

Engine-wide keys set from the CLI must go inside the engine-overrides object — risuko config set writes to the user config, and the engine only reads keys like max-overall-download-limit from there via engine-overrides. The value is picked up the next time the engine starts.

# Set global speed limit (K/M suffixes supported)
risuko config set engine-overrides '{"max-overall-download-limit": "50M"}'

# Connection count is per download (default 16)
risuko download https://example.com/file.zip -t 8

On this page