-
-
Notifications
You must be signed in to change notification settings - Fork 471
Lua‐Configuration
This guide covers writing configuration files for rTorrent using Lua instead of the traditional .rc format.
- Create your Lua config file (e.g.
~/.config/rtorrent/rtorrent.rc.lua). - In your
rtorrent.rc, add a single line to load it:
lua.execute = (cat, (system.env, HOME), "/.config/rtorrent/rtorrent.rc.lua")
- At the top of your
.rc.luafile:
local rtorrent = require('rtorrent')
rc = rtorrent.autocall- Use
rcto call any rTorrent command:
rc.network.port_range = '50000-50000'
rc.session.path = '/home/user/.rtorrent/.session/'
rc.directory.default = '/home/user/rtorrent/download/'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:
-- This expression:
rc.system.hostname()
-- Is equivalent to:
rtorrent.call("system.hostname", "")The metatable chain:
-
__index: Each.keyaccess pusheskeyonto an internal__namestacktable and returns a new proxy. -
__call: When invoked with(), joins the namestack with., gets the__target(defaults to""), and callsrtorrent.call(name, target, ...). -
__newindex: Assignment likerc.foo = valueis sugar forrc.foo.set(value).
You can store sub-chains as aliases:
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'The low-level function that all autocall usage resolves to. Its signature is:
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).
- For
- ...args: Additional arguments depending on the command.
Example:
-- 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)By convention, rc is assigned as an alias for rtorrent.autocall:
rc = rtorrent.autocallThis lets you write:
rc.session.path('/tmp/session')
-- instead of
rtorrent.autocall.session.path('/tmp/session')When you assign to an autocall property, it automatically calls .set():
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:
rc.throttle.max_peers.normal = 60 -- integer
rc.protocol.pex = false -- boolean
rc.directory.default = '/data/' -- string
rc.pieces.memory.max = '1800M' -- string with unitFor commands that require a download target (all d.* commands), use rtorrent.Target(hash):
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:
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:
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:
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))
endrtorrent.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:
-
Non-negative integer — pass the first N event arguments:
-- 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
-
Table of strings — explicit rTorrent argument expressions:
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
-
Table of integers — argument indexes (converted to
$argument.N):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).
--[[
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=')Proxy object. Property access builds a dotted command name. Calling the result executes it.
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 argLow-level call to any rTorrent command.
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)Create a target-bound proxy for a specific download.
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...")Convenience constructors for prefix-targeted proxies.
local d = rtorrent.d("ABCDEF1234567890...")
d.name() -- call("d.name", "ABCDEF1234567890...")
d.is_active() -- call("d.is_active", "ABCDEF1234567890...")Register a global Lua function as an rTorrent command.
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=' })Insert rTorrent methods (non-Lua) directly:
-- 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')