From 0b5d022f864c1fa75cacad8e1b0a1758aa1ae3ba Mon Sep 17 00:00:00 2001 From: Trim21 Date: Tue, 12 May 2026 21:13:45 +0800 Subject: [PATCH] Created Lua-Configuration.md (markdown) --- Lua‐Configuration.md.md | 499 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 Lua‐Configuration.md.md diff --git a/Lua‐Configuration.md.md b/Lua‐Configuration.md.md new file mode 100644 index 0000000..b52f8f7 --- /dev/null +++ b/Lua‐Configuration.md.md @@ -0,0 +1,499 @@ +# Lua Configuration Guide for rTorrent + +This guide covers writing configuration files for rTorrent using Lua instead of the traditional `.rc` format. + +## Table of Contents + +- [Quick Start](#quick-start) +- [Core Concepts](#core-concepts) + - [rtorrent.autocall](#1-rtorrentautocall) + - [rtorrent.call()](#2-rtorrentcall) + - [The `rc` Shorthand](#3-the-rc-shorthand) + - [Assignment Syntax Sugar](#4-assignment-syntax-sugar) + - [Targeting Downloads](#5-targeting-downloads) + - [Registering Lua Functions](#6-registering-lua-functions-as-rtorrent-commands) +- [Complete Example Config](#complete-example-config) +- [API Reference](#api-reference) + +--- + +## Quick Start + +1. Create your Lua config file (e.g. `~/.config/rtorrent/rtorrent.rc.lua`). +2. In your `rtorrent.rc`, add a single line to load it: + +``` +lua.execute = (cat, (system.env, HOME), "/.config/rtorrent/rtorrent.rc.lua") +``` + +3. At the top of your `.rc.lua` file: + +```lua +local rtorrent = require('rtorrent') +rc = rtorrent.autocall +``` + +4. Use `rc` to call any rTorrent command: + +```lua +rc.network.port_range = '50000-50000' +rc.session.path = '/home/user/.rtorrent/.session/' +rc.directory.default = '/home/user/rtorrent/download/' +``` + +--- + +## Core Concepts + +### 1. `rtorrent.autocall` + +`rtorrent.autocall` is a proxy object that uses a metatable chain to convert dotted property access into `rtorrent.call()` invocations. Each `.` access appends to an internal name stack. When the result is called as a function (with `()`), it executes the accumulated command name. + +**How it works internally:** + +```lua +-- This expression: +rc.system.hostname() + +-- Is equivalent to: +rtorrent.call("system.hostname", "") +``` + +The metatable chain: +- `__index`: Each `.key` access pushes `key` onto an internal `__namestack` table and returns a new proxy. +- `__call`: When invoked with `()`, joins the namestack with `.`, gets the `__target` (defaults to `""`), and calls `rtorrent.call(name, target, ...)`. +- `__newindex`: Assignment like `rc.foo = value` is sugar for `rc.foo.set(value)`. + +You can store sub-chains as aliases: + +```lua +local sys = rc.system +sys.hostname() -- same as rc.system.hostname() +sys.pid() -- same as rc.system.pid() + +local net = rc.network +net.port_range = '6881-6999' +``` + +### 2. `rtorrent.call()` + +The low-level function that all autocall usage resolves to. Its signature is: + +```lua +rtorrent.call(method, target, ...args) +``` + +- **method** (`string`): The rTorrent command name, e.g. `"d.name"`, `"system.hostname"`. +- **target** (`string`): The target for the command. + - For `d.*` commands: the download info hash (hex string). + - For `p.*`/`f.*`/`t.*` commands: used internally by multicall. + - For global commands: pass `""` (empty string). +- **...args**: Additional arguments depending on the command. + +**Example:** + +```lua +-- Get the system hostname (global command, target = "") +local name = rtorrent.call("system.hostname", "") + +-- Get a download's name (requires info hash) +local dl_name = rtorrent.call("d.name", "ABCDEF1234567890...") + +-- Set a value +rtorrent.call("throttle.global_up.max_rate.set", "", 102400) +``` + +### 3. The `rc` Shorthand + +By convention, `rc` is assigned as an alias for `rtorrent.autocall`: + +```lua +rc = rtorrent.autocall +``` + +This lets you write: + +```lua +rc.session.path('/tmp/session') +-- instead of +rtorrent.autocall.session.path('/tmp/session') +``` + +### 4. Assignment Syntax Sugar + +When you assign to an autocall property, it automatically calls `.set()`: + +```lua +rc.network.port_range = '50000-50000' +-- Is exactly equivalent to: +rc.network.port_range.set('50000-50000') +-- Which is: +rtorrent.call("network.port_range.set", "", '50000-50000') +``` + +This works for all variable types: + +```lua +rc.throttle.max_peers.normal = 60 -- integer +rc.protocol.pex = false -- boolean +rc.directory.default = '/data/' -- string +rc.pieces.memory.max = '1800M' -- string with unit +``` + +### 5. Targeting Downloads + +For commands that require a download target (all `d.*` commands), use `rtorrent.Target(hash)`: + +```lua +local dl = rtorrent.Target("ABCDEF1234567890ABCDEF1234567890ABCDEF12") + +-- Query download properties: +dl.d.name() -- get download name +dl.d.up.rate() -- get upload rate +dl.d.is_active() -- check if active + +-- Set download properties: +dl.d.priority.set(3) -- set priority to high +dl.d.directory.set('/new/path/') + +-- Iterate files: +local files = dl.f.multicall('', 'f.path=', 'f.size_bytes=') + +-- Iterate peers: +local peers = dl.p.multicall('p.address=', 'p.up_rate=') + +-- Iterate trackers: +local trackers = dl.t.multicall('t.url=', 't.is_enabled=') +``` + +**Convenience aliases** are also available: + +```lua +rtorrent.d(hash) -- equivalent to rtorrent.Target(hash, "d") +rtorrent.f(hash) -- equivalent to rtorrent.Target(hash, "f") +rtorrent.p(hash) -- equivalent to rtorrent.Target(hash, "p") +rtorrent.t(hash) -- equivalent to rtorrent.Target(hash, "t") +rtorrent.load(hash) -- equivalent to rtorrent.Target(hash, "load") +``` + +So you can write: + +```lua +local d = rtorrent.d("ABCDEF1234567890...") +d.name() -- rtorrent.call("d.name", "ABCDEF1234567890...") +d.up.rate() -- rtorrent.call("d.up.rate", "ABCDEF1234567890...") +``` + +**Using Target with multicall:** + +```lua +local dl = rtorrent.Target("INFO_HASH_HERE") + +-- f.multicall(filter, *cmds) — filter is a regex, "" means all files +local files = dl.f.multicall('', 'f.path=', 'f.size_bytes=', 'f.completed_chunks=') +for _, row in ipairs(files) do + local path, size, completed = row[1], row[2], row[3] + print(path .. ': ' .. size .. ' bytes, ' .. completed .. ' chunks done') +end + +-- p.multicall(*cmds) +local peers = dl.p.multicall('p.address=', 'p.client_version=', 'p.up_rate=') +for _, row in ipairs(peers) do + local addr, client, rate = row[1], row[2], row[3] + print(addr .. ' (' .. client .. ') up: ' .. rate .. ' B/s') +end + +-- t.multicall(*cmds) +local trackers = dl.t.multicall('t.url=', 't.is_enabled=') +for _, row in ipairs(trackers) do + local url, enabled = row[1], row[2] + print(url .. ' enabled=' .. tostring(enabled)) +end +``` + +### 6. Registering Lua Functions as rTorrent Commands + +`rtorrent.insert_lua_method(name, func_name, method_args)` registers a global Lua function as an rTorrent command that can be used in schedules, event handlers, etc. + +**Parameters:** + +| Parameter | Type | Description | +|-----------|------|-------------| +| `name` | `string` | rTorrent command slot name, e.g. `"d.watch_handler"` | +| `func_name` | `string` | Name of a global Lua function | +| `method_args` | `number \| string[] \| number[]` | How to pass rTorrent arguments to the Lua function | + +**method_args formats:** + +1. **Non-negative integer** — pass the first N event arguments: + + ```lua + -- Register handler that receives 1 argument (the torrent file path) + rtorrent.insert_lua_method('d.watch_handler', 'my_watch_handler', 1) + + -- The rTorrent command becomes: + -- d.watch_handler=$argument.0 + + -- Your Lua function receives (target, arg0): + function my_watch_handler(target, filepath) + print("New torrent: " .. filepath) + end + ``` + +2. **Table of strings** — explicit rTorrent argument expressions: + + ```lua + rtorrent.insert_lua_method('d.on_complete', 'handle_complete', { + '$d.hash=', '$d.name=' + }) + + -- Your Lua function receives (target, hash, name): + function handle_complete(target, hash, name) + print("Completed: " .. name .. " (" .. hash .. ")") + end + ``` + +3. **Table of integers** — argument indexes (converted to `$argument.N`): + + ```lua + rtorrent.insert_lua_method('d.reorder', 'reorder_args', { 1, 0 }) + + -- The rTorrent command becomes: + -- d.reorder=$argument.1,$argument.0 + -- i.e. arguments are passed in reversed order to your Lua function + ``` + +**Note:** The first argument to your Lua function is always `target` (may be an empty string for global commands). + +--- + +## Complete Example Config + +```lua +--[[ + A minimal rTorrent Lua configuration. + + Usage: + 1. Copy this file to ~/.config/rtorrent/rtorrent.rc.lua + 2. In rtorrent.rc, add: + lua.execute = (cat, (system.env, HOME), "/.config/rtorrent/rtorrent.rc.lua") +]]-- + +--[[ Bootstrap ]]-- + +local rtorrent = require('rtorrent') +rc = rtorrent.autocall + +--[[ Helper functions ]]-- + +local function makeArgs(cmdline) + return 'sh', '-c', "'"..table.concat(cmdline, "' '").."'" +end + +--[[ Instance layout (base paths) ]]-- + +local home = os.getenv('HOME') + +local cfg = {} +cfg.basedir = home..'/rtorrent/' +cfg.download = cfg.basedir..'download/' +cfg.logs = cfg.basedir..'log/' +cfg.logfile = cfg.logs..'rtorrent-'..rc.system.time()..'.log' +cfg.session = cfg.basedir..'.session/' +cfg.watch = cfg.basedir..'watch/' + +-- Register paths as private constant rTorrent methods +rc.method.insert('cfg.download', 'private|const|string', cfg.download) +rc.method.insert('cfg.logs', 'private|const|string', cfg.logs) +rc.method.insert('cfg.logfile', 'private|const|string', cfg.logfile) +rc.method.insert('cfg.session', 'private|const|string', cfg.session) +rc.method.insert('cfg.watch', 'private|const|string', cfg.watch) + +-- Create instance directories +rc.execute.throw( + makeArgs({'mkdir', '-p', + cfg.download, + cfg.logs, + cfg.session, + cfg.watch..'/load', + cfg.watch..'/start'})) + +--[[ Network settings ]]-- + +rc.network.port_range = '50000-50000' +rc.network.port_random = false + +-- Tracker-less torrent and UDP tracker support +rc.dht.mode = 'disable' +rc.protocol.pex = false +rc.trackers.use_udp = false + +--[[ Peer settings ]]-- + +rc.throttle.max_uploads = 100 +rc.throttle.max_uploads.global = 250 + +rc.throttle.min_peers.normal = 20 +rc.throttle.max_peers.normal = 60 +rc.throttle.min_peers.seed = 30 +rc.throttle.max_peers.seed = 80 +rc.trackers.numwant = 80 + +rc.protocol.encryption.set('allow_incoming', 'try_outgoing', 'enable_retry') + +--[[ Resource limits ]]-- + +rc.network.max_open_files = 600 +rc.network.max_open_sockets = 300 +rc.pieces.memory.max = '1800M' +rc.network.xmlrpc.size_limit = '4M' + +--[[ Operational settings ]]-- + +rc.session.path = cfg.session +rc.directory.default = cfg.download +rc.log.execute(cfg.logs.."execute.log") +rc.execute.nothrow( + "sh", "-c", table.concat( + {"echo >", rc.session.path(), "rtorrent.pid", " ", rc.system.pid() +})) +rc.encoding.add('utf8') +rc.system.umask = 0027 +rc.system.cwd = rc.directory.default() +rc.network.http.dns_cache_timeout = 25 +rc.schedule2('monitor_diskspace', '15', '60', 'close_low_diskspace=1000M') + +--[[ Custom methods ]]-- + +rc.method.insert('system.startup_time', 'value|const', rc.system.time()) +rc.method.insert('d.data_path', 'simple', + [[if=(d.is_multi_file), + (cat, (d.directory), /), + (cat, (d.directory), /, (d.name))]]) +rc.method.insert('d.session_file', 'simple', 'cat=(session.path), (d.hash), .torrent') + +--[[ Watch directories ]]-- + +rc.schedule2('watch_start', '10', '10', + 'load.start_verbose=(cat, (cfg.watch), "start/*.torrent")') +rc.schedule2('watch_load', '11', '10', + 'load.verbose=(cat, (cfg.watch), "load/*.torrent")') + +--[[ Logging ]]-- + +rc.print('Logging to '..cfg.logfile) +rc.log.open_file('log', cfg.logfile) +rc.log.add_output('info', 'log') + +--[[ SCGI (XMLRPC/JSONRPC) endpoint ]]-- + +-- Unix socket: +-- rc.network.scgi.open_local(cfg.session..'rtorrent.sock') +-- rc.execute.nothrow('chmod', '770', cfg.session..'rtorrent.sock') + +-- TCP port: +-- rc.network.scgi.open_port('127.0.0.1:5000') + +--[[ Register a Lua function as an rTorrent command ]]-- + +local function on_torrent_loaded(target, filepath) + -- target is the download hash, filepath is $argument.0 + print("Loaded torrent from: " .. filepath) +end + +rtorrent.insert_lua_method('event.torrent_loaded', 'on_torrent_loaded', 1) + +-- Use it in a schedule or view event: +-- rc.view.event_added('started', 'event.torrent_loaded=$d.tied_to_file=') +``` + +--- + +## API Reference + +### `rtorrent.autocall` + +Proxy object. Property access builds a dotted command name. Calling the result executes it. + +```lua +rc = rtorrent.autocall + +rc.system.hostname() -- call("system.hostname", "") +rc.throttle.max_peers.normal = 60 -- call("throttle.max_peers.normal.set", "", 60) +rc.d.name.set(hash, "New Name") -- explicit target via .set() with extra arg +``` + +### `rtorrent.call(method, target, ...args)` + +Low-level call to any rTorrent command. + +```lua +rtorrent.call("system.hostname", "") -- -> "myhost" +rtorrent.call("d.name", "ABCD...EF") -- -> "my_torrent.torrent" +rtorrent.call("d.priority.set", "ABCD...EF", 3) -- -> nil +rtorrent.call("throttle.global_up.max_rate.set", "", 0) -- -> nil (unlimited) +``` + +### `rtorrent.Target(hash, prefix)` + +Create a target-bound proxy for a specific download. + +```lua +local dl = rtorrent.Target("ABCDEF1234567890...") +dl.d.name() -- call("d.name", "ABCDEF1234567890...") +dl.d.up.rate() -- call("d.up.rate", "ABCDEF1234567890...") +dl.d.priority.set(3) -- call("d.priority.set", "ABCDEF1234567890...", 3) + +-- With custom prefix: +local custom = rtorrent.Target("ABCDEF1234567890...", "d") +custom.name() -- call("d.name", "ABCDEF1234567890...") +``` + +### `rtorrent.d(hash)`, `rtorrent.f(hash)`, `rtorrent.p(hash)`, `rtorrent.t(hash)` + +Convenience constructors for prefix-targeted proxies. + +```lua +local d = rtorrent.d("ABCDEF1234567890...") +d.name() -- call("d.name", "ABCDEF1234567890...") +d.is_active() -- call("d.is_active", "ABCDEF1234567890...") +``` + +### `rtorrent.insert_lua_method(name, func_name, method_args)` + +Register a global Lua function as an rTorrent command. + +```lua +function my_handler(target, arg0, arg1) + -- do something +end + +-- Pass 2 event arguments: +rtorrent.insert_lua_method('my.command', 'my_handler', 2) + +-- Pass specific arguments: +rtorrent.insert_lua_method('my.other', 'my_handler', { '$d.hash=', '$d.name=' }) +``` + +### `method.insert` via autocall + +Insert rTorrent methods (non-Lua) directly: + +```lua +-- Simple command method: +rc.method.insert('my.greeting', 'simple', 'cat=Hello, World!') + +-- Value with initial value: +rc.method.insert('my.counter', 'value', 0) + +-- String variable: +rc.method.insert('my.label', 'string', 'default') + +-- Constant string (read-only): +rc.method.insert('my.info', 'private|const|string', 'v1.0') + +-- Multi-method (key-value pairs): +rc.method.insert('my.map', 'multi') +rc.method.set_key('my.map', 'key1', 'value1') +rc.method.set_key('my.map', 'key2', 'value2') +```