mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-10 16:19:23 +00:00
Created Lua-Configuration.md (markdown)
@@ -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')
|
||||
```
|
||||
Reference in New Issue
Block a user