We deal with rtorrent in earnest

  • Tutorial
There are enough articles on the installation and basic configuration of rtorrent on the hub, as well as debates about whether it is worth contacting the hardcore rtorrent at all or whether it is better to do something more user friendly. Personally, many years ago I reviewed all the rocking chairs and as a result rtorrent turned out to be the most stable and efficient. The interface is not the most convenient, but understandable and usable enough so that this does not become a serious problem. Alternative interfaces like rutorrent somehow didn’t get accustomed to me - installing php just for the sake of rutorrent is reluctant, and the rest of the options look very weak (and none other than rutorrent are even present in the Gentoo port). One of the main advantages of rtorrent is its very flexible configuration and automation capabilities . Sorry, syntax


~/.rtorrent.rc rather non-standard, there is no normal documentation, so usually the setup comes down to searching and copying (trying to change something in them besides the constants / paths to the directories usually fails) ready-made  recipes or generally limited to editing the constants in the basic configuration.

The other day, I decided that it couldn’t continue like this - we have known each other for many years, he pumped out so much good for me, but I still won’t get to know him better! I will not say that I thoroughly figured it out, but at least I was able to realize all my ideas on rtorrent automation, and did it realizing what and why I was doing, without shamanism with other people's recipes.


Rtorrent-0.9.2 was configured (with an additional color patch, as in the screenshot above), and if you have an older version, I highly recommend updating. After the upgrade, you should run  migrate_rtorrent_rc.sh  to convert the obsolete syntax inside ~/.rtorrent.rcto a more modern one and see  the migration instructions .

Other documentation

Studying the existing documentation evokes only one feeling: deep sadness. I'm lying. It causes a desire to swear rudely. A deep sadness is something that comes much later, when you realize that you haven’t imagined it, and everything is really so bad. Nevertheless, at least something is still better than nothing at all (here! I managed to find something positive), so let's see what we have:

  • official documentation   offers  an example of using schedule and  an example of setting up directory monitoring
  • the wiki has a rudimentary description of the  syntax for the config and the above recipes
  • on third-party sites there  are   lists of   commands that can be used in the config - the benefit from them is doubtful, because they are probably very outdated, plus they were created for the needs of calling RPC and not for use in the config, but sometimes useful examples come across between the lines
  • the actual list of commands will have to be pulled from the source, for example like this (in the directory where the source is unpacked): ack CMD2(well, or the old fashioned way grep -r CMD2)


For debugging and experimentation, instead of changing the config file and restarting rtorrent, you can use the built-in command line in it. It is called by pressing Ctrl-X, after which you can enter any command, for example:

command> print="Hello ",World!

press Enter and get on the screen and in the log:

(22:13:39) Hello World!

Old log entries that are not available on the screen can be seen by clicking l, to exit the log viewing screen by pressing the spacebar.

When using commands that should be applied to a specific object (torrent, file, tracker, peers) on the command line, the current object selected in the rtorrent interface will be used (i.e., you need to position the cursor on the desired line before executing the command).

The main disadvantage of this command line is the lack of history; you cannot press the “up” button to see the previous command, modify it, and execute it again.

Config file syntax

The first thing you need to understand about the syntax ~/.rtorrent.rcis not the config file . Yes, it looks like it, but it's just a dangerous illusion. In fact, this is a program : a sequence of commands (this is an official term, but I will continue to call them functions , it’s more familiar and essentially true), and it actively uses the transfer of some commands (along with their own parameters) to other parameters (hello,  callback- and in configs - only you were not enough for complete happiness!), with several levels of nesting and different screening options. You can (and have to) create your own functions- This greatly helps to reduce the number of levels of nesting and the resulting difficulties with shielding.

Thus, when you see a line in the config like:

throttle.global_down.max_rate.set_kb = 10000

then this is actually a function call with one parameter, more traditionally written as:


As I understand it, dots in function names do not make much sense, just a regular separator for readability, like underlining.

Spaces, oddly enough, are not acceptable everywhere where any delimiters are used. At a minimum, they can be used:

  • around =after the name of the first command in the line (it doesn’t matter if it is a line in the config file or a line in quotation marks ""in the middle of the line in the config file)
  • after the ,separating parameters (before the comma, in my opinion, it does not work everywhere)
  • around the ;separating command in a line describing the body of the new function

Function call

# без параметров, аналог func()
# с двумя параметрами, аналог func("param1","param2")

The value of the parameter is treated as a string. Quoting the value of the parameter in quotation marks is necessary if it contains a space or a separator character like  ,or ;(it is always difficult to use quotation marks for clarity, because very often this parameter is already inside a string containing several commands, so you have to use quotation marks and visibility will only worsen as a result).

func="first param, with special chars","second;param"

To get the result of calling a function (to pass it as a parameter to another function), you must put before the name of the function $:

# выведет: system.hostname=
# выведет: system.hostname=
# выведет: powerman.name
# выведет: powerman.name

In the first and second examples, the function printreceives one parameter: a string  "system.hostname=". In the third and fourth print, the parameter receives the result of the function system.hostnamewithout parameters: a string  "powerman.name".

The function printdisplays all its parameters, and if one of them is a call to a function that also has parameters, it is necessary to separate which parameters belong to printand which to the other function (in fact, this is not a very urgent problem, since most functions rtorrent parameters either do not accept at all or accept one):

# выведет: oneСб сен 27 23:50:51 EEST 2014-utwo
# выведет: oneСб сен 27 20:51:05 UTC 2014two

The function execute.captureexecutes any system command (via sh) and returns what this command will output to STDOUT. Therefore, it can accept any number of parameters (command name and parameters for it). As you see in the first version, she took only one parameter (and called date) and the rest (-u, two) went to -u print. And in the second we are clearly indicated what parameters are execute.capture, so she called date -uand printy got only the last parameter (two).

If you try to run this example, it will not work for you as described. The fact is that the system command datecompletes the output with a newline, so the rest of the parametersprint-a are already displayed on another line and rtorrent it shows not very clearly, even in the log. Therefore, for the example to work, you need to replace the command with a datevariant that does not output a line break, for example, put in ~/bin/date:

echo -n `/bin/date "$@"`


Some rtorrent functions expect in their parameters functions passed by the user (which they will call themselves later). There are two alternative syntaxes for how you can pass one function as a parameter to another: either a regular line containing the function call and its parameters in the normal rtorrent syntax is transferred, or a list of lines inside is passed (( )), where the function name is the first line and the rest of the lines are its parameters.

# func() вызывается с двумя параметрами:
# - пользовательским callback-ом func1()
# - строкой "param2"
# func() вызывается с двумя параметрами:
# - пользовательским callback-ом func1("param11","param12")
# - строкой "param2"
# то же самое, что и в предыдущем примере

Accordingly, if func1you need to pass a callback in the parameters to the transmitted callback, either you need to use different escaping styles (quotes / brackets), or use quotation marks to enclose quotes inside quotes, or use nested brackets.

# передадим в func1() первым параметром вместо строки param11
# callback func2("param21","param22") разными способами:

By the way, rtorrent has another way to syntactically pass a list - inside { }. Where and why it should be used, I have not figured it out yet.

Custom functions

New functions can be created using the function method.insert. The first parameter passed to the name for a new feature, the second of its type (usually  simple, but if you want to create does not function as a variable you should use types value, bool, string, so you can make the function / variable privateand constnot only - unfortunately, to deal with these possibilities need for source code), the third line with the body of the function. Unlike lines with callbacks described above, in this line you can pass several commands through ;but you can not use a separator (( )).

# создаём новую функцию:
method.insert = newfunc, simple, "func=param1,param2;func1=;func2="
# и вызываем её:

If you need to call the function created with different parameters (up to 4) - they can be accessed within the function through argument.0=... argument.3.

method.insert = hello, simple, "print = \"Hello \", $argument.0=, !"
# выведет: Hello Powerman!


With variables, the work goes essentially also through functions (get / set), and usually there are several alternative recording options.

# установить some.var в значение "value"
some.var.set = value
some.set_var = value
some.var = value
# получить значение some.var

As I understand it, the first of the options shown above is considered modern and should work for any variables, and all the others were used in older versions of rtorrent and are now supported for some variables for backward compatibility.

In order not to bother with the method.insertsake of creating a pair of variables, there are ready-made functions that allow you to work with variables attached to a specific torrent (which allows you to save information for each torrent separately):

# установить переменную var в значение value для текущего торрента
d.custom.set = var, value
# получить значение переменной var для текущего торрента

# установить 5 предопределённых переменных для текущего торрента
d.custom1.set = value1
d.custom2.set = value2
d.custom3.set = value3
d.custom4.set = value4
d.custom5.set = value5
# получить 5 предопределённых переменных для текущего торрента

I wrote above that dots in function names do not carry much meaning and, in fact, do not differ from underscores. As you can see, this is not entirely true: variables starting with d.are associated with a specific torrent, and each torrent has its own values ​​for these variables. Similarly, with functions starting with  d.- they also operate on the current torrent. Nevertheless, I think that this is a feature of the implementation of built-in functions, and for custom functions and variables the presence or absence of d.a name will not make them magically linked to the current torrent (however, I did not check this).

Oddities and bugs

If the callback for is schedulepassed inside (( ))and not " ", then the parameters for this callback also set inside the nested ones are (( )) somehow processed as receiving the result of the function, and not passing it as a callback:

schedule id,1,1, ((print, ((directory.default))))
# работает как
schedule id,1,1, "print = $directory.default="

Some functions are buggy if they are called when rtorrent is started (for example,  load.startor ui.current_view.set) - their execution should be delayed through schedule, and start time should be greater than 0. This is probably due to the fact that when rtorrent starts, by the time the commands from the config file are executed, all data structures are correctly initialized.

A brief overview of useful features

print = список строк

Debugging on the command line; output to the log.

$cat = список строк

It returns concatenated strings (analogue print, only returns a string instead of output to the log).


Get the 1/2/3/4th parameter of the current function.

method.insert = имя_функции, simple, "тело; функции"

Create a custom function.

In my opinion only method.insert, and method.set_keycan pass a sequence of multiple commands over ;- all other functions where you can send commands to take them either one by one or as a list (eg. load.normalAnd load.start).

method.set_key = event.download.тип, имя_события, "список; команд"

Create an event handler. Valid values for тип: insertedinserted_new, inserted_session, erased, opened, closedresumed, paused, finished, hash_done, hash_failedhash_final_failed, hash_removed, hash_queued. Why you need it is имя_событияnot clear (it can probably be checked through  method.has_keyand method.list_keysit’s not clear where this can come in handy), but it’s easy to guess that it should be unique.

schedule = имя_расписания, начало, интервал, список команд
schedule_remove = имя_расписания

Periodically executed список команд, the main tool for rtorrent automation. The values ​​for началоand интервалcan be set either in seconds or in the format чч:мм:сс(if интервалset to 0, it will be executed once). For commands that should be executed when rtorrent starts, the value for is началоusually set to 5 - it is likely that rtorrent has time to fully start and that the bugs mentioned above do not occur.

load.normal = путь_к_torrent_файлу(ам), список команд
load.start  = путь_к_torrent_файлу(ам), список команд

It is usually called from scheduleto automatically upload files added to a given directory. load.startautomatically starts downloading the file after adding.

execute             = системная_команда, список её параметров
execute.capture     = системная_команда, список её параметров
execute.nothrow     = системная_команда, список её параметров
execute.nothrow.bg  = системная_команда, список её параметров

Executes an external program. execute.capturereturns the output of this command, execute.nothrowignores the error of the running command,  execute.nothrow.bgruns the program in the background.

if      = выражение,    команда_если_истина, команда_если_ложь
branch  = команда_тест, команда_если_истина, команда_если_ложь

Allows you to select the command to be executed depending on the condition. The difference between ifand branchin how the condition is specified: as a value ( $func=) or as a command ( func=). In addition, команда_если_ложьyou can set the following condition instead  and add two more command parameters, etc.

not     = команда
and     = список команд
or      = список команд
equal   = список команд
less    = список значений
greater = список значений

I think the general meaning is clear. In addition to using in ifand branchfunctions,  lessthey greaterare also used to configure different sorts.

I did not check the format of the parameters for these and subsequent commands, so there may be errors (write to the PM, I will fix it).

import  = конфиг-файл

Connection of an additional config file.


The rtorrent file name is the .torrent file that you downloaded and uploaded to rtorrent (usually - automatically loading all .torrent files from a given directory through scheduleand load.start) as a Tied file . Curiously, rtorrent does not use this file to download, but a copy of it, which it saves in its directory session.path. Accordingly, stop_untiedit  close_untiedalso remove_untieddetermines during execution what to do with the torrent file in the directory session.pathif the original torrent file associated with it (tied) was deleted. What the team is doing, start_tiedI still do not understand.


Different properties of the current torrent. In fact, they are several times more, I just mentioned the most useful at first glance. For example,  d.directorythis is the directory where the torrent will be downloaded, the  d.tied_to_filename of the original (not from session.path) .torrent file, the  d.namename of the file or directory downloaded by this torrent, etc.


Current state - this torrent has been downloaded or not yet.

d.create_link   = тип, префикс, постфикс
d.delete_link   = тип, префикс, постфикс

Various operations on the current torrent. The value типcan be  base_path, base_filenameand tieddefines the directory for the symlink (from d.base_path, d.base_filenameor d.tied_to_file), префиксand  постфиксare added to the name and can be empty.


I did not understand this, but as I understand it, through this function, massive operations are performed on torrent groups.

Example from my config

When a .torrent file appears in one of the directories /mnt/torrent//mnt/torrent/serials/and /mnt/torrent/music/it should be:

  • automatically uploaded to rtorrent
  • configured to download to the same directory as the .torrent file
  • torrents from the directory /mnt/torrent/music/should start immediately

schedule = watch_directory_1, 5, 5, ((load.normal, /mnt/torrent/*.torrent, "d.custom.set = watchdir, / mnt / torrent", "d.directory.set = $ d.custom = watchdir "))
schedule = watch_directory_2, 5, 5, ((load.normal, /mnt/torrent/serials/*.torrent, "d.custom.set = watchdir, / mnt / torrent / serials", "d.directory.set = $ d.custom = watchdir "))
schedule = watch_directory_3, 5, 5, ((load.start, /mnt/torrent/music/*.torrent, "d.custom.set = watchdir, / mnt / torrent / music", "d.directory.set = $ d.custom = watchdir "))

  • автоматически переименован чтобы соответствовать имени файла или  каталога который он будет выкачивать (т.е. файл  [rutracker.org].t4788972.torrent автоматически переименуется в  Читающий Мысли (The Listener) (Season V, 2014, WEB-DL 720p) [FOX].torrent)
  • перед расширением .torrent к имени файла должен быть добавлен суффикс  показывающий его текущий статус: --- если он ещё не скачался или +++  если он уже скачался

method.insert = d.renamed_suffix,   simple, "if = $d.complete=, +++, ---"
# - WORKAROUND: extra / at beginning needed because $d.tied_to_file= begins with //
method.insert = d.renamed_file,     simple, "cat = /, $d.custom=watchdir, /, $d.name=, ., $d.renamed_suffix=, .torrent"
method.insert = d.rename_file,      simple, "execute = mv, --, $d.tied_to_file=, $d.renamed_file=; d.tied_to_file.set = $d.renamed_file="
method.insert = d.safe_rename_file, simple, "branch = ((equal, d.tied_to_file =, d.renamed_file =)),, d.rename_file ="
method.set_key = event.download.inserted_new, rename_loaded, d.safe_rename_file =
method.set_key = event.download.resumed, rename_resumed, d.safe_rename_file =
method.set_key = event.download.finished, rename_finished, d.safe_rename_file =

If the .torrent file is deleted, then a copy of it from session.pathalso needs to be deleted and this torrent removed from rtorrent.

# Watch a directory for torrents, and remove those that have been deleted.
schedule = watch_untied, 5, 5, ((remove_untied))

Show a pop-up notification on the monitor when a torrent with its name is downloaded (you need a small helper script).

# Notify when download finished
method.set_key = event.download.finished, notify_me, "execute = ~ / bin / rtorrent_finished, $ d.name ="

After starting rtorrent, open a list of still under-downloaded torrents (similar to 6manually clicking ).

# Set default view
schedule = default_view, 1, 0, ui.current_view.set = incomplete

Sort the list of those that were not downloaded by the date of addition (this code is taken from the recipe in the wiki and is given as an example of use here less=).

method.set_key = event.download.inserted_new, loaded_time, "d.custom.set = tm_loaded, $ system.time =; d.save_full_session ="
view.sort_new = incomplete, less = d.custom = tm_loaded
view.sort_current = incomplete, less = d.custom = tm_loaded


I hope this description will help automate the operation of your rtorrent and solve the problem of the lack of recipes suitable for you personally. Unfortunately, the quality of this article is noticeably lower than my usual articles, I still haven’t clarified many questions, there are probably errors in the descriptions of some functions, but the amount of time that was not a pity to kill for this task was very limited. It is probably worth using the information from this article to supplement the official wiki, but I already have the strength and time for that, hopefully someone else will do it.

Text converted using habrahabr backend for AsciiDoc .

Also popular now: