Common Configuration Use-Cases¶
After you went through Configuration Quick Start and got familiar with the handling of rTorrent, it’s time to look at settings that you should consider for your configuration, but which weren’t necessary to start using it.
The Common Tasks in rTorrent wiki page contains more of these typical configuration use-cases.
Load ‘Drop-In’ Config Fragments¶
The examples here and in the wiki are mostly short snippets written to serve a specific purpose. To easily add those by just dropping them into a new file, add this to your main configuration file (which then can be the last change you apply to it).
method.insert = cfg.drop_in, private|const|string, (cat, (cfg.basedir), "config.d")
execute.nothrow = bash, -c, (cat,\
"find ", (cfg.drop_in), " -name '*.rc' ",\
"| sort | sed -re 's/^/import=/' >", (cfg.drop_in), "/.import")
try_import = (cat, (cfg.drop_in), "/.import")
To test the change, execute these commands:
mkdir -p ~/rtorrent/config.d
echo 'print="Hello from config.d!"' >$_/hello.rc
Then restart rTorrent, and you should see Hello from config.d!
amongst the initial console messages.
Hint
Config drop-ins are very useful when you manage your systems in a state-of-the-art way, i.e. using a configuration management tool like Ansible. Then you can simply add files with customizations to a system, without having to fiddle with changing existing files.
Note
If a drop-in file just contains commands that can be repeated several times, they can be re-imported making them way easier to test after changes. For example, schedules can be redefined, but method definitions can not (under the same name).
Log Rotation, Archival, and Pruning¶
The following longer snippet adds logs that don’t endlessly grow, get archived after some days, and are finally deleted after a while. See rtorrent.d/15-logging.rc for the full snippet.
Warning
If you include this, take care to comment out any conflicting logging commands that you already have in your main configuration.
The time spans for archival and pruning are set using
pyro.log_archival.days
(default: 2) and
pyro.log_retention.days
(default: 7).
You can change these in your main configuration,
after including the snippet via import.
Log files are time stamped (see pyro.date_iso.log_stamp
and pyro.log_stamp.current
).
Full log file paths for different types are created using pyro.logfile_path
,
which takes the type as an argument.
The pyro.log_rotate
multi-method takes care of calculating a new time stamp,
and rotating all the log files by re-opening them with their new name.
A daily schedule calls this method and thus triggers the rotation.
Finally, two schedules take care of daily archival (1:10 AM) and pruning (1:20 AM),
passing the command built by pyro._logfile_find_cmd
to bash
for execution.
The pyro.log_rotate
method is used near the end to open log files at startup.
Rename Item Using its Tied-to File¶
The rename2tied.sh script overwrites an item’s name using the file name
of its tied-to file, when you press the R
key with a fresh unstarted item in focus.
This is useful when the metafile names generated by a tracker
contain more useful information than the info.name
of the metafile content.
Also, those metafile names typically have a common format,
which can help with properly organizing your downloads.
Warning
Right now, this only works for items that are not started yet,
i.e. were added using load.normal
and have no data files yet.
Also, the item needs to be loaded from a file, so there actually is a tied-to name – items loaded via ruTorrent do not have one!
Here is the core script code (minus some boilerplate):
hash="${1:?hash is missing}"
name="${2:?name is missing}"
path="${3}"
tied="${4}"
multi="${5:?is_multi_file is missing}"
fail() {
msg="$(echo -n "$@")"
rtxmlrpc print '' "ERROR: $msg [$name]"
exit 1
}
test -n "$path" || fail "Empty directory"
test -n "$tied" || fail "Empty tied file"
test ! -e "$path/$name" || fail "Cannot rename an item with existing data"
tracker="$(rtcontrol --from-view $hash // -qo alias)"
# Build new name
new_name="${tied##*/}" # Reduce path to basename
new_name="${new_name// /.}" # Replace spaces with dots
new_name="${new_name%.torrent}" # Remove extension
while test "$new_name" != "${new_name%[.0-9]}"; do
new_name="${new_name%[.0-9]}" # Remove trailing IDs etc.
done
# Remove bad directory name (that we want to replace) from multi-file item
new_full_path="${path%/}"
if test "$multi" -eq 1; then
new_full_path="${new_full_path%/*}"
fi
# Remove common extensions
for ext in mkv mp4 m4v avi; do
new_name="${new_name%.$ext}"
new_name="${new_name%.$(tr a-z A-Z <<<$ext)}"
done
# Change source tags to encode tags (when item has an encoded media type)
if egrep -i >/dev/null '\.[xh]\.?264' <<<"$new_name"; then
new_name=$(sed -re 's~\.DVD\.~.DVDRip.~' -e 's~\.Blu-ray\.~.BDRip.~' <<<"$new_name")
fi
# Add tracker as group if none is there
if ! egrep >/dev/null '.-[a-zA-Z0-9]+$' <<<"$new_name"; then
new_name="${new_name}-$tracker"
fi
# Rename / relocate item
new_full_path="${new_full_path%/}/$new_name"
rtxmlrpc d.directory_base.set $hash "$new_full_path"
rtxmlrpc d.custom.set $hash displayname "$new_name"
Install the full script by calling these commands:
gh_raw="https://raw.githubusercontent.com/rtorrent-community/rtorrent-docs"
mkdir -p ~/rtorrent/scripts
wget $gh_raw/master/docs/examples/rename2tied.sh -O $_/rename2tied.sh
chmod a+rx $_
Note that you also must have pyrocore installed,
so that the rtcontrol
and rtxmlrpc
commands are available.
This is the configuration snippet that binds calling the script to the R
key.
For key binding, you need rTorrent-PS though – otherwise leave out the
pyro.bind_key
command, and call pyro._rename2tied=
via a Ctrl-X
prompt.
method.insert = pyro._rename2tied, private|simple, \
"execute.nothrow = ~/rtorrent/scripts/rename2tied.sh, \
(d.hash), (d.name), (d.directory), (d.tied_to_file), (d.is_multi_file)"
pyro.bind_key = rename2tied, R, "pyro._rename2tied="
Depending on your needs, it can also make sense to call the script in
an inserted_new
event handler, or as a post-load command in a watch schedule.
If you do that, you should probably add some checks
that only apply changes for certain trackers,
or when the tied-to file name has a certain format.
Versatile Move on Completion¶
The completion-path.sh script allows you to perform very versatile completion moving, based on logic defined in a bash script
Calling the script with -h
prints full installation instructions
including the rTorrent config snippet shown further below.
gh_raw="https://raw.githubusercontent.com/rtorrent-community/rtorrent-docs"
wget -O /tmp/completion-path.sh $gh_raw/master/docs/examples/completion-path.sh
bash /tmp/completion-path.sh -h
Read on to learn how this works when added to your rTorrent instance.
The target path is determined in the set_target_path
function at the top
of the script:
local month=$(date +'%Y-%m')
# Only move data downloaded into a "work" directory
if egrep >/dev/null "/work/" <<<"${base_path}/"; then
# Make sure the target directory is on the same drive as "work", else leave it alone
work_dir=$(sed -re 's~(^.+/work/).*~\1~' <<<"${base_path}/")
test $(fs4path "$work_dir") == $(fs4path "$(dirname ${base_path})") || return
else
return # no "work" component in data path (pre-determined path)
fi
# "target_base" is used to complete a non-empty but relative "target" path
target_base=$(sed -re 's~^(.*)/work/.*~\1/done~' <<<"${base_path}")
target_tail=$(sed -re 's~^.*/work/(.*)~\1~' <<<"${base_path}")
test "$is_multi_file" -eq 1 || target_tail="$(dirname "$target_tail")"
test "$target_tail" != '.' || target_tail=""
# Move by label
test -n "$target" || case $(tr A-Z' ' a-z_ <<<"${label:-NOT_SET}") in
tv|hdtv) target="TV" ;;
movie*) target="Movies/$month" ;;
esac
# Move by name patterns (check both displayname and info.name)
for i in "$display_name" "$name"; do
test -n "$target" -o -z "$i" || case $(tr A-Z' ' a-z. <<<"${i}") in
*hdtv*|*pdtv*) target="TV" ;;
*.s[0-9][0-9].*) target="TV" ;;
*.s[0-9][0-9]e[0-9][0-9].*) target="TV" ;;
*pdf|*epub|*ebook*) target="eBooks/$month" ;;
esac
done
test -z "$target" && is_movie "$name" && target="Movies/$month" || :
test -z "$target" -a -n "$display_name" && is_movie "$display_name" && target="Movies/$month" || :
# Prevent duplication at end of path
if test -n "$target" -a "$is_multi_file" -eq 1 -a "$name" = "$target_tail"; then
target_tail=""
fi
# Append tail path if non-empty
test -z "$target" -o -z "$target_tail" || target="$target/$target_tail"
Change it according to your preferences. If you don’t assign a value to target
,
the item is not moved and remains in its default download location for later manual moving.
The is_movie
helper function uses an inline Python script to check for
typical names of movie releases using a regular expression:
import re
import sys
pattern = re.compile(
r"^(?P<title>.+?)[. ](?P<year>\d{4})"
r"(?:[._ ](?P<release>UNRATED|REPACK|INTERNAL|PROPER|LIMITED|RERiP))*"
r"(?:[._ ](?P<format>480p|576p|720p|1080p|1080i|2160p))?"
r"(?:[._ ](?P<srctag>[a-z]{1,9}))?"
r"(?:[._ ](?P<source>BDRip|BRRip|HDRip|DVDRip|DVD[59]?|PAL|NTSC|Web|WebRip|WEB-DL|Blu-ray|BluRay|BD25|BD50))"
r"(?:[._ ](?P<sound1>MP3|DD.?[25]\.[01]|AC3|AAC(?:2.0)?|FLAC(?:2.0)?|DTS(?:-HD)?))?"
r"(?:[._ ](?P<codec>xvid|divx|avc|x264|h\.?264|hevc|h\.?265))"
r"(?:[._ ](?P<sound2>MP3|DD.?[25]\.[01]|AC3|AAC(?:2.0)?|FLAC(?:2.0)?|DTS(?:-HD)?))?"
r"(?:[-.](?P<group>.+?))"
r"(?P<extension>\.avi|\.mkv|\.mp4|\.m4v)?$", re.I
)
title = ' '.join(sys.argv[1:])
sys.exit(not pattern.match(title))
The is_movie
check is done after the more reliable name checks.
For the script to be called and used as part of completion moving,
these commands need to be added to your rtorrent.rc
or
config.d/move_on_completion.rc
(see Load ‘Drop-In’ Config Fragments on how to get a config.d
directory):
# vim: ft=dosini
method.insert = completion_path, simple|private, "execute.capture = \
~/rtorrent/scripts/completion-path.sh, \
(directory.default), (session.path), \
(d.hash), (d.name), (d.directory), (d.base_path), (d.tied_to_file), \
(d.is_multi_file), (d.custom1), (d.custom, displayname)"
method.insert = completion_dirname, simple|private, \
"execute.capture = bash, -c, \"dirname \\\"$1\\\" | tr -d $'\\\\n'\", \
completion_dirname, (argument.0)"
method.insert = completion_move_print, simple|private, \
"print = \"MOVED »\", (argument.0), \"« to »\", (argument.1), «"
method.insert = completion_move_single, simple|private, \
"d.directory.set = (argument.1); \
execute.throw = mkdir, -p, (argument.1); \
execute.throw = mv, -u, (argument.0), (argument.1)"
method.insert = completion_move_multi, simple|private, \
"d.directory_base.set = (argument.1); \
execute.throw = mkdir, -p, (completion_dirname, (argument.1)); \
execute.throw = mv, -uT, (argument.0), (argument.1)"
method.insert = completion_move, simple|private, \
"branch=d.is_multi_file=, \
\"completion_move_multi = (argument.0), (argument.1)\", \
\"completion_move_single = (argument.0), (argument.1)\" ; \
d.save_full_session="
method.insert = completion_move_verbose, simple|private, \
"completion_move = (argument.0), (argument.1); \
completion_move_print = (argument.0), (argument.1)"
method.insert = completion_move_handler, simple|private, \
"branch=\"not=(equal, argument.0=, cat=)\", \
\"completion_move_verbose = (d.base_path), (argument.0)\""
method.set_key = event.download.finished, move_on_completion, \
"completion_move_handler = (completion_path)"
# END move_on_completion
In the completion_move_handler
method,
you can change completion_move_verbose
to just completion_move
,
if you don’t want the move logged.
The completion_path
method already passes the major item attributes to the script.
You can add more if you need to, but then you also need to extend the list of names
in arglist
at the top of the bash script.
arglist=( default session hash name directory base_path tied_to_file is_multi_file label display_name )
If you run rTorrent-PS, which has the d.tracker_domain
command,
you can use that command to add a rule for trackers dedicated to one specific content type.
Extend the last line of completion_path
to read …displayname), (d.tracker_domain)"
,
and add tracker_domain
to the end of arglist
.
Then add a rule like this to the body of set_target_path
:
# Move by tracker
test -n "$target" || case $(tr A-Z' ' a-z_ <<<"${tracker_domain:-NOT_SET}") in
linuxtracker.org) target="Software" ;;
esac
Delayed Completion Handling¶
The following config snippet defines a new event.download.finished_delayed trigger that works like the normal finished event, but only fires after a customizable delay.
One use-case for such a thing is to move a download from fast storage (RAM disk, SSD) to slow storage (HDD) for permanent seeding, after the initial rush in a swarm is over.
The following is the config you need to add to a config.d/event.download.finished_delayed.rc
file
(see Load ‘Drop-In’ Config Fragments on how to get a config.d
directory),
or else to your normal rtorrent.rc
file:
#############################################################################
# Add a "finished_delayed" event
#
# See https://github.com/rakshasa/rtorrent/issues/547
#############################################################################
# Delay in seconds
method.insert.value = event.download.finished_delayed.interval, 600
# Add persistent view (queue holding delayed items)
view.add = finished_delayed
view.persistent = finished_delayed
# Add new event for delayed completion handling
method.insert = event.download.finished_delayed, multi|rlookup|static
method.set_key = event.download.finished, !add_to_finished_delayed, \
"d.views.push_back_unique = finished_delayed ; \
view.filter_download = finished_delayed"
method.set_key = event.download.finished_delayed, !remove_from_finished_delayed, \
"d.views.remove = finished_delayed ; \
view.filter_download = finished_delayed"
# Call new event for items that passed the delay interval
schedule2 = event.download.finished_delayed, 60, 60, \
((d.multicall2, finished_delayed, \
"branch=\"elapsed.greater=(d.timestamp.finished),(event.download.finished_delayed.interval)\", \
event.download.finished_delayed="))
# For debugging…
method.set_key = event.download.finished_delayed, !debug, \
"print = \"DELAYED FINISH after \", (convert.elapsed_time, (d.timestamp.finished)), \
\" of \", (d.name)"
The last command adding a !debug
handler can be left out, if you want less verbosity.
Set a Download to “Seed Only”¶
The d.seed_only
command helps you to stop all download activity on an item.
Select any unfinished item, press Ctrl-X
, and enter d.seed_only=
followed by ⏎
.
Then all files in that item are set to off
, and any peers still sending you data are cut off.
The data you have is still seeded, as long as the item is not stopped.
method.insert = d.seed_only, private|simple,\
"f.multicall = *, f.priority.set=0 ;\
d.update_priorities= ;\
d.disconnect.seeders="
f.multicall calls f.priority.set on every file, d.update_priorities makes these changes known, and finally d.disconnect.seeders kicks any active seeders.
Scheduled Bandwidth Shaping¶
This example shows how to use schedule2 with absolute start times, to set the download rate depending on the wall clock time, at 10AM and 4PM. The result is a very simple form of bandwidth shaping, with full speed transfers enabled while you’re at work (about 16 MiB/s in the example), and only very moderate bandwidth usage when you’re at home.
schedule2 = throttle_full, 10:00:00, 24:00:00, ((throttle.global_down.max_rate.set_kb, 16000))
schedule2 = throttle_slow, 16:00:00, 24:00:00, ((throttle.global_down.max_rate.set_kb, 1000))
Use throttle.global_up.max_rate.set_kb for setting the upload rate.
If you call these commands via XMLRPC from an outside script, you can implement more complex rules, e.g. throttling when other computers are visible on the network.
External scripts should also be used when saving money is the goal,
in cases where you have to live with disadvantageous ISP plans with bandwidth caps.
Run such a script very regularly (via cron
),
to enforce the bandwidth rules continuously.