Torrent Management System with rTorrent and Bash

I do a fair amount of torrenting, mostly for TV shows. A college student’s schedule doesn’t always allow for sitting in front of the TV on a certain night during a certain timeslot :) . Nor does it allow for micromanaging my torrents — making sure I have enough, but not too many, torrents running at a given time; making sure torrents are properly seeded once downloaded; making sure the files get moved to the right place on my external HDD (so XBMC can pull them over the network). Just too much to make sure it all happens.

There are a lot of GUI clients that claim to do a lot of this stuff, but I prefer the KDE-native KTorrent as my desktop client. For a management system, though, the GUI is almost irrelevant — a set-it-and-forget-it system needs to sit in the background until called to the foreground, and rTorrent does this very nicely. Coupled with an always-on-and-connected home server, some bash scripting, and the linux “screen” program, rTorrent is fully capable of automating torrent downloads.

In researching others’ systems, I came across “rTorrent as a download manager“, a text file detailing what the author expected from his management system, and how he went about obtaining the desired results. I also came across an rTorrent and libtorrent user guide on the official rTorrent site. Unfortunately, there is little up-to-date, detailed info on all the options offered for rTorrent customization.

According to the first resource, the (initial) main goals of the TMS are:

  1. Limit the number of concurrent active torrents
  2. Queue and start inactive torrents
  3. Move downloaded files to another directory and remove the associated .torrent file
  4. Manage bandwidth usage to keep the DSL connection useful

These are all well and good, and relatively simple to achieve with little bash scripting, but rTorrent is capable of more. When a torrent is finished, I want to move it to another directory before I continue seeding. When it’s done seeding, I want to move it to another directory, so I can know, simply by looking at directory contents, which files are completely done. And, of course, I want to move the downloaded files to the appropriate directories. So the complete list of TMS requirements is:

  1. Limit the number of concurrent active torrents
  2. Queue and start inactive torrents
  3. Move downloaded files to a second directory and continue to seed the torrent to a specific ratio
  4. Move seeded files to a third directory and remove associated .torrent files
  5. Manage bandwidth usage to keep the DSL connection useful
  6. Move files from the final directory to the appropriate external locations

The first two can be accomplished with rTorrent’s directory watching and a simple bash script. The next three can be implemented solely within rTorrent. And the final point can be implemented with a more complicated bash script that takes advantage of community torrent naming conventions.

First, the directory structure. Let us assume that the base directory for torrent activity is /torrents. Limiting torrents is a simple matter of making sure that rTorrent’s watched directory never contains more than N .torrent files, where N is our maximum. This gives us two directories: /torrents/watch and /torrents/loading. The watch directory is the one rTorrent will watch for torrents, and the loading directory will be a holding cell for inactive torrents. Points 3 and 4 give us three more directories, /torrents/active, /torrents/seeding, and /torrents/finished. These are for active, downloaded (seeding), and completely finished torrents, respectively. Another directory to use is /torrents/session, for use with rTorrent’s session feature. Finally, I have a hidden directory /torrents/.archive where I send copies of activated .torrent files.

Once the directory structure is in place, rTorrent needs to be told how to manage things. This is done in the .rtorrent.rc file, which, by default, is in the initiating user’s home directory. Here is the section of my .rtorrent.rc file that handles torrent management (make sure each command is on one line):

#************** Automagic Torrent Management  ****************#

# Maintain session info in /torrents/session
session=/torrents/session

# Drop .torrent files in /torrents/loading (or /torrents, if starttor can handle "misguided" files)

# Use "starttor" script on cronjob to keep /torrents/watch occupied with three or fewer .torrents

# Load torrents in "watch" directory (check every 5sec)
schedule = watch_directory,5,5,load_start=/torrents/watch/*.torrent

# Download torrents to "active" directory
directory = /torrents/active

# Move to "seeding" directory and seed when downloaded
on_finished = seed,"execute=mv,-u,$d.get_base_path=,/torrents/seeding/ ;d.set_directory=/torrents/seeding/"

# Close when seed ratio reached (check every 60sec)
schedule = ratio,60,60,"close_on_ratio=110"

# Move to "finished" on close (when both the file is downloaded and the ratio is reached)
on_close = move_fin,"execute=mv,-u,$d.get_base_path=,/torrents/finished/ ;d.set_directory=/torrents/finished/"

# Delete .torrent files in /torrents/watch when torrent is closed
on_close = remove_fin,"execute=rm,$d.get_tied_to_file="

# Torrent is downloaded, seeded, and done with.  Use custom script to move to appropriate directory.

#*************************************************************#
 

All other “schedule” and “on_whatever” commands that were in the example .rtorrent.rc file are commented out. This defines the session, watch, and active download directories; specifies that downloaded files should be moved to the seeding directory and seeded; specifies what ratio to stop a seeding torrent at; and specifies that completely finished torrents should be moved to the finished directory. Getting finished files to be moved before seeding was fairly straightforward; in fact, there were already examples floating around the internet on how to do just that. Getting the files moved when the torrent was finished was tricky. All the examples I could find only showed “stop_on_ratio=” for the ratio schedule. Unfortunately, this leaves the torrent sitting in rTorrent as “inactive”, and, were I to use the “on_stop” command, the torrent would be moved when Ctrl-D was pushed (which temporarily stops the torrent). So I decided to try “close_on_ratio” instead, and that particular command happened to exist. Thanks to this, the torrent is closed (and moved according to the following on_close command) when my 1.10 ratio is reached, freeing up space for the next torrent. The only downside is if you are the original seed for a torrent (or are trying to seed a torrent beyond 1.10) and try to temporarily stop that torrent, it will be moved if the ratio is above 1.10. Other commands in .rtorrent.rc (upload_rate and download_rate) allow bandwidth control.

Once rTorrent knows how to manage the torrents (and you can test this by starting rTorrent and dropping a torrent in the watch directory), you need to implement the “queuing system”. Here is the “starttor” bash script that I use:

#!/bin/bash

# Root dir (for finding "misguided" torrents)
root="/torrents"
# Loading dir (for inactives)
loading="/torrents/loading"
# Watching dir (for actives)
watch="/torrents/watch"
# Archive dir - leave blank (archive="") if you don’t use one
archive="/torrents/.archive"
# max # torrents at once
N=3

# max-1 for comparisons
N=$(($N-1))

# Move all "misguided" torrents (thrown into the root dir) to the "loading" dir
misguided=`ls "$root" | grep .*\.torrent | replace " " "~"`
for torrent in $misguided; do
        torrent=`echo $torrent | replace "~" " "`
        # Make all .torrent files rw/r/r
        chmod 644 "$root/$torrent"
        mv "$root/$torrent" "$loading/"
done

# If N or more active torrents, don’t do anything
active=`ls "$watch" | grep .*\.torrent | grep ".*" -c`
if [ $active -gt $N ]; then
        exit 0;
fi

# If no inactive torrents, do nothing
inactive=`ls "$loading" | grep .*\.torrent | grep ".*" -c`
if [ $inactive -eq 0 ]; then
        exit 0;
fi

# While we have inactive torrents and less than 3 active, copy over inactives
tostart=`ls "$loading" | grep .*\.torrent | replace " " "~"`
for torrent in $tostart; do
        torrent=`echo "$loading/$torrent" | replace "~" " "`
        active=`ls "$watch" | grep .*\.torrent | grep ".*" -c`
        if [ $active -gt $N ]; then
                exit 0;
        fi
        if [ -n $archive ]; then
                cp "$torrent" "$archive/"
        fi
        mv "$torrent" "$watch/"
done
 

This script moves any stray torrents from the root directory (the easiest place to drop them) to the loading directory; I refer to these as “misguided” torrents, since they are not in the main loading directory. It then checks for the number of active torrents; if the number is greater than or equal to the max, the script exits, doing nothing. If there’s room for more, it checks for any torrents in the loading directory. If there are torrents there, it will move them, one at a time, into the watch directory, until the max is reached. If the archive variable is set, it will also save a copy to the specified directory. You can use this script with cron; I use this line in root’s crontab to run it every ten minutes:

*/10 * * * * /home/andrew/bin/starttor
 

Moving the finished files to their appropriate places is more complicated. Basically, you have to write a script that takes into account the common layout of filenames as well as your specific organizational directory tree. For example, I like to download TV shows. My directory structure is TV > Show Name > Season #. Most TV torrent files follow the conventional naming scheme of TV.Show.Name.S01E03.Episode.Name.Release.Group.avi. My script recursively goes through all the .avi files in the finished directory, for each file pulling out everything up to the season number (in this case, “TV.Show.Name.S01″). It then splits it at the S##, extracting the show name and season number. It then replaces all periods, underscores, and dashes in the show name with spaces, and reduces multiple consecutive spaces to one space each. Once the show name is prepped, the script checks if the directory “TV Show Name” exists in the TV directory. If it does, it checks for “Season #” (if that doesn’t exist, it checks for “Season#”, without the space, just in case). If that exists, the file is moved to that Season directory. If not, the file is left alone, for me to move manually if I need to. This leaves other, non-TV torrents in the finished directory for me to work with later. I also have this script on cron, but it is set to run every hour.

RTorrent makes building a TMS easy, as long as you know what settings to use. To really automate the whole thing, I could add a script which daily (or weekly) checks various RSS feeds for new torrents of shows I like, but I haven’t gotten that far yet — I’m still manually hunting down my shows. Since copying and pasting the file contents I’ve given is not ideal, copies of the files I use are available for download, just be sure to rename and chmod them as appropriate.

If you feel I left out any details, feel free to ask questions in the comments, and I’ll answer to the best of my ability.

2 Comments:

  1. Came across this looking around on the net for some stuff while troubleshooting rtorrent. Great write up…

    If you haven’t got the “automation” down, try out pytvshows. It’s a great tool and seems flawless to me so far.

  2. Thanks for the .rtorrent.rc file, I was looking to implement a similar system, but couldn’t find the the rtorrent commands. Great tool, but the docs aren’t that clear.

No Pingbacks

Leave a Comment

You need to enable javascript in order to use Simple CAPTCHA.