Skip to content

Note

Using these features requires some knowledge in the area of Linux, Bash, and Python, but they enable you to customize your setup even further and handle very specific use-cases.

Advanced 'rtcontrol'

Executing OS commands

The --call and --spawn options can be used to call an OS level command and feed it with data from the selected items. The argument to both options is a template, i.e. you can have things like {{d.hash}} in them.

When using --call, the command is passed to the shell for parsing -- with obvious implications regarding the quoting of arguments, thus --call only makes sense if you need I/O redirection or similar shell features.

In contrast, the --spawn option splits its argument list according to shell rules before expanding the template placeholders, and then calls the resulting sequence of command name and arguments directly. Consider --spawn 'echo "name: {{d.name}}"' vs. --spawn 'echo name: {{d.name}}' -- the first form passes one argument to /bin/echo, the second form two arguments. Note that in both cases, spaces or shell meta characters contained in the item name are of no relevance, since the argument list is split according to the template, not its expanded value.

To list all the fields available in the first five items, try this command:

rtcontrol // -/5 --spawn "echo -e '\\n'{{i}}" | sed -re 's/, /,\n    /g'

Unlike --call, where you can use shell syntax to call several commands, --spawn can be passed several times for executing a sequence of commands. If any called command fails, the rtcontrol call is aborted with an error.

Copy Session Metafiles by Category

Here's a practical example for using --spawn, it copies all your loaded metafiles from the session directory into a folder structure categorized by the ruTorrent label. Unlabelled items go to the _NOLABEL folder.

target="/tmp/metafiles"
rm -rf "$target"
rtcontrol // \
  --spawn 'mkdir -p "'"$target/"'"'{{d.label or "_NOLABEL" | shell}}' \
  --spawn 'cp {{d.sessionfile}} "'"$target"'"/{{d.label or "_NOLABEL" | shell}}/{{d.name|shell}}-{{d.hash[:7]}}.torrent'

The copied metafiles themselves are renamed to the contained name of the item's data, plus a small part of the infohash to make these names unique.

Replace the d.label by d.‹fieldname› to categorize by other values, e.g. d.alias for 'by tracker'.

Executing XMLRPC commands {#rtcontrol-exec}

If you want to apply some custom XMLRPC commands against a set of download items, the --exec option of rtcontrol allows you to do that. For global commands not referring to specific items, see the next section about the rtxmlrpc tool. Read through the following examples to understand how --exec works, features are explained as they are used there. Also make sure you understand basic things like output-templates{.interpreted-text role="ref"} beforehand, it's assumed here that you do.

Repairing Stuck Items

Let's start with an easy example of using --exec, where no templating is needed:

rtcontrol --exec 'd.stop= ; d.close= ; f.multicall=,f.set_create_queued=0,f.set_resize_queued=0 ; d.check_hash=' \
          --from stopped // -/1

This command simulates pressing ^K^E^R in the curses UI (which cleans the state of stuck / damaged items), and as written above only affects the first stopped item.

Use different filter arguments after --exec to select other items. Afterwards, use --start to start these items again.

Manually Triggering Events

Since rTorrent events are merely multi-call commands, you can trigger them manually by calling them on selected items. This calls event.download.finished (again) on complete items loaded in the last 10 minutes:

rtcontrol --exec "event.download.finished=" 'loaded<10m' is_complete=y

Make sure that the registered handlers do not have adverse effects when called repeatedly, i.e. know what you're doing. The handlers for an event can be listed like so:

rtxmlrpc --output-format repr method.get '' event.download.finished

Relocating Download Data

The most simple variant of changing the download path is setting a new fixed location for all selected items, as follows:

rtcontrol --exec 'd.directory_base.set="/mnt/data/new/path"' directory=/mnt/data/old/path

This replaces the location of items stored at /mnt/data/old/path with a new path. But to be really useful, we'd want to shift any path under a given base directory to a new location -- the next command does this by using templating and calculating the new path based on the old one:

rtcontrol \
    --exec 'd.directory_base.set="{{item.directory|subst("^/mnt/data/","/var/data/")}}" ; >d.directory=' \
    path=/mnt/data/\*

This selects any item stored under /mnt/data and relocates it to the new base directory /var/data. Fields of an item can be used via a item.‹field-name› reference. Adding >d.directory= prints the new location to the console -- a semicolon with spaces on both sides delimits several commands, and the > prints the result of a XMLRPC command.

The move-data{.interpreted-text role="ref"} section has more on how to also move the data on disk, in addition to changing the location in rTorrent's session as shown here.

Making Shared Data Paths Unique

Another example regarding data paths is this:

rtcontrol --from stopped // --exec 'directory.set={{item.directory}}-{{item.hash}}'

That command ensures that items that would download into the same path get a unique name by appending the info hash, and assumes those items weren't started yet (i.e. added via load.normal).

Changing Announce URLs in Bulk

The next example replaces an active announce URL with a new one, which is necessary after a domain or passkey change. Compared to other methods like using sed on the files in your session directory, this does not require a client restart, and is also safer (the sed approach can easily make your session files unusable). This disables all old announce URLs in group 0 using a t.multicall, and then adds a new one:

rtcontrol \
    --exec 't.multicall=0,t.disable= ; d.tracker.insert=0,"http://new.example.com/announce" ; d.save_full_session=' \
    "tracker=http://old.example.com/announce"

The tracker.insert also shows that arguments to commands can be quoted.

Using Templates as Filter Values

As mentioned in filter-conditions, you can compare a string field to a template. This can be a brain twister, so just look at the following example, which replaces any download path in an item by the real storage path, but only if they differ.

# List any differences
rtcontrol path='*' is_multi_file=y 'directory!={{d.realpath|replace("[","[[]")}}' \
    -qo directory,realpath
rtcontrol path='*' is_multi_file=n 'directory!={{d.realpath|pathdir|replace("[","[[]")}}' \
    -qo directory,realpath.pathdir

# Fix any differences (i.e. resolve all symlinks for good)
rtcontrol path='*' is_multi_file=y 'directory!={{d.realpath|replace("[","[[]")}}' \
    --exec 'directory_base.set={{item.realpath}}'
rtcontrol path='*' is_multi_file=n 'directory!={{d.realpath|pathdir|replace("[","[[]")}}' \
    --exec 'directory.set={{item.realpath|pathdir}}'

As so often, 'multi' and 'single' items need a slightly different treatment.

Note that [ characters are escaped to [[], so that things like [2017] in a filename do not lead to unexpected results. * and ? though are kept intact and are used for glob matching as normal, because they match their own literal form if they appear in the field value (on the right-hand side).

Using 'rtxmlrpc'

Load Metafile with a Specific Data Path

The following shows how to load a metafile from any path in $metafile, not only a watch directory, with the data downloaded to $data_dir by adding a d.directory_base.set on-load command. You might need to change that to d.directory.set depending on your exact use-case.

rtxmlrpc -q load.normal '' "$metafile" \
    "d.directory_base.set=\"$data_dir\"" "d.priority.set=1"

Use load.start to start that item immediately. If the metafile has fast-resume information and the data is already there, no extra hashing is done.

And just to show you can add more on-load commands, the priority of the new item is set to low. Other common on-load commands are those that set custom values, e.g. the ruTorrent label.

General maintenance tasks

Here are some commands that can help with managing your rTorrent instance:

# Flush ALL session data NOW, use this before you make a backup of your session directory
rtxmlrpc session.save

Setting and checking throttles

To set the speed of the slow throttle, and then check your new limit and print the current download rate, use:

$ rtxmlrpc throttle.down '' slow 120
0
$ rtxmlrpc throttle.down.max '' slow
122880
$ rtxmlrpc throttle.down.rate '' slow
0

Note that the speed is specified in KiB/s as a string when setting it but returned in bytes/s as an integer on queries.

The following script makes this available in an easy usable form, e.g. throttle slow 42 -- it also shows the current rate and settings of all defined throttles when called without arguments:

#! /bin/bash
# Set speed of named throttle

#
# CONFIGURATION
#
throttle_name="seed" # default name
unit=1024 # KiB/s

#
# HERE BE DRAGONS!
#
down=false
if test "$1" = "-d"; then
    down=true
    shift
fi

if test -n "$(echo $1 | tr -d 0-9)"; then
    # Non-numeric $1 is a name
    throttle_name=$1
    shift
fi

if test -z "$1"; then
    echo >&2 "Usage: ${0/$HOME/~} [-d] [<throttle-name=$throttle_name>] <rate>"

    rtorrent_rc=~/.rtorrent.rc
    test -e "$rtorrent_rc" || rtorrent_rc="$(rtxmlrpc system.get_cwd)/rtorrent.rc"
    if test -e "$rtorrent_rc"; then
        throttles="$(egrep '^throttle[._](up|down)' $rtorrent_rc | tr ._=, ' ' | cut -f3 -d" " | sort | uniq)"
        echo
        echo "CURRENT THROTTLE SETTINGS"
        for throttle in $throttles; do
            echo -e "  $throttle\t" \
                "U: $(rtxmlrpc to_kb $(rtxmlrpc throttle.up.rate $throttle)) /" \
                "$(rtxmlrpc to_kb $(rtxmlrpc throttle.up.max $throttle | sed 's/^-1$/0/')) KiB/s\t" \
                "D: $(rtxmlrpc to_kb $(rtxmlrpc throttle.down.rate $throttle)) /" \
                "$(rtxmlrpc to_kb $(rtxmlrpc throttle.down.max $throttle | sed 's/^-1$/0/')) KiB/s"
        done
    fi
    exit 2
fi

rate=$(( $1 * $unit ))

# Set chosen bandwidth
if $down; then
    if test $(rtxmlrpc throttle.down.max $throttle_name) -ne $rate; then
        rtxmlrpc -q throttle.down $throttle_name $(( $rate / 1024 ))
        echo "Throttle '$throttle_name' download rate changed to" \
             "$(( $(rtxmlrpc throttle.down.max $throttle_name) / 1024 )) KiB/s"
    fi
else
    if test $(rtxmlrpc throttle.up.max $throttle_name) -ne $rate; then
        rtxmlrpc -q throttle.up $throttle_name $(( $rate / 1024 ))
        echo "Throttle '$throttle_name' upload rate changed to" \
             "$(( $(rtxmlrpc throttle.up.max $throttle_name) / 1024 )) KiB/s"
    fi
fi

Global throttling when other computers are up

If you want to be loved by your house-mates, try this:

#! /bin/bash
# Throttle bittorrent when certain hosts are up

#
# CONFIGURATION
#
hosts_to_check="${1:-mom dad}"
full_up=62
full_down=620
nice_up=42
nice_down=123
unit=1024 # KiB/s

#
# HERE BE DRAGONS!
#

# Check if any prioritized hosts are up
up=$(( $full_up * $unit ))
down=$(( $full_down * $unit ))
hosts=""

for host in $hosts_to_check; do
    if ping -c1 $host >/dev/null 2>&1; then
        up=$(( $nice_up * $unit ))
        down=$(( $nice_down * $unit ))
        hosts="$hosts $host"
    fi
done

reason="at full throttle"
test -z "$hosts" || reason="for$hosts"

# Set chosen bandwidth
if test $(rtxmlrpc throttle.global_up.max_rate) -ne $up; then
    echo "Setting upload rate to $(( $up / 1024 )) KiB/s $reason"
    rtxmlrpc -q throttle.global_up.max_rate.set_kb $(( $up / 1024 ))
fi
if test $(rtxmlrpc throttle.global_down.max_rate) -ne $down; then
    echo "Setting download rate to $(( $down / 1024 )) KiB/s $reason"
    rtxmlrpc -q throttle.global_down.max_rate.set_kb $(( $down / 1024 ))
fi

Add it to your crontab and run it every few minutes.

Throttling rTorrent for a limited time

If you want to slow down rTorrent to use your available bandwidth on foreground tasks like browsing, but usually forget to return the throttle settings back to normal, then you can use the provided rt-backseat script. It will register a job via at, so that command must be installed on the machine for it to work. The default throttle speed and timeout can be set at the top of the script.

#!/bin/bash
# Throttle rTorrent for a certain amount of time

#
# CONFIGURATION
#
timeout="now + 10 minutes" # default timeout
throttled=42 # throttled speed
unit=1024 # unit on command line, default KiB/s
queue=r

#
# HERE BE DRAGONS!
#
set -e
set +x

case "$1" in
    -h | --help)
        echo >&2 "Usage: $0 [«speed» [«timespec»]]"
        exit 1
        ;;
    *) : ;;
esac

if test -n "$(echo "$1" | tr -d 0-9)"; then
    echo >&2 "ERROR: Non-numeric speed"
    exit 1
fi

if test -n "$1"; then
    throttled="$1"
    shift
fi
throttled=$(( throttled * unit ))

if test -n "$1"; then
    timeout="$*"
fi

if test -n "$(atq -q $queue)"; then
    # If there are jobs pending, run 1st one now, and then delete them
    at -c "$(atq -q $queue | cut -f1 | head -n1)" | /bin/sh
    atrm "$(atq -q $queue | cut -f1)"
fi

current=$(rtxmlrpc throttle.global_down.max_rate)

# Schedule new job to reset rate, and then throttle it
result=$(at -q $queue "$timeout" <<EOF 2>&1
rtxmlrpc -q throttle.global_down.max_rate.set '' $current
EOF
) || :
if [[ $result =~ .*(error|arbled).* ]]; then
    echo >&2 "ERROR: $result"
    exit 1
fi
echo "$result" | sed -re "s~warning: commands will be executed using /bin/sh~~"
rtxmlrpc -q throttle.global_down.max_rate.set '' $throttled

echo "Speed throttled to $(( throttled / 1024 )) KiB/s," \
     "back to $(( current / 1024 )) KiB/s at $timeout."