The torrent client rtorrent
is the best I've found that runs on Unix
systems, but its built-in logic is somewhat lacking. Fortunately one
can extend this.
For example, it can say "seed until you've uploaded twice as much
as you downloaded, then stop seeding", which is fair enough. But I
compromise between kindness and disc space; if I've downloaded
something very unpopular (such as an old Linux distro that nobody else
wants any more) I'd like to reduce the ratio at which I'll stop
seeding, and after a couple of months I probably don't want to keep it
around at all. Meanwhile anything that has reached its ratio can be
deleted from the server. (I've already set up logic within rtorrent
that hard-links completed downloads to somewhere else, for downloading
from the remote torrenting machine to archive storage.)
But that's OK, rtorrent has a remote control interface. There's some
documentation on this at the rtorrent
wiki, but
it assumes that you want to link from a Unix domain socket to a web
server endpoint, which you then have to secure, and then plug in
something to that that talks HTTP/TCP. Other documentation I've found
makes the same assumption. I wanted a simpler approach, just talking
to the socket directly from a program running under the same userid on
the same machine.
First you need to tell rtorrent to create a listening socket. This
needs an explicit path; obviously change this to somewhere that exists
on your filesystem. (^x gets you a command prompt within rtorrent,
and/or add this to your ~/.rtorrent.rc
.)
network.scgi.open_local = /home/roger/rtorrent/rpc.socket
Now you need to talk to it. This is where other documentation goes off
into web servers and mod_scgi
and other such stuff. No need for any
of that! I did it in Perl, but basically anything that can talk to a
Unix socket will work, so rather than go into details of my code I'll
just talk about the algorithm.
So in your language of choice: connect to the socket (you may need to
specify that it's a SOCK_STREAM
).
The commands you'll be sending are in
XML-RPC, wrapped up in
SCGI.
In Perl at least, while there are modules to deal with all these
things, they tend to assume that you'll be using an HTTP transport and
a web server rather than a socket, so I ended up doing it the
straightforward way.
So start with your command, as it might be "download_list" ""
(using
a blank parameter to specify the "main" view). Turn that into a lump
of XML like:
<?xml version="1.0"?>
<methodCall>
<methodName>download_list</methodName>
<params>
<param>
<value><string></string></value>
</param>
</params>
</methodCall>
(XML-RPC has other parameter data types, but rtorrent appears to
accept everything as strings.)
That's the request body; let's say it's 179 characters long. Now that
gets formatted as an SCGI request, which starts as a series of
key-value pairs. The minimal version might look like:
CONTENT_LENGTH 179
SCGI 1
REQUEST_METHOD POST
REQUEST_URI /RPC
Turn these into a single string, with a NUL after each key and value.
CONTENT_LENGTH<00>179<00>SCGI<00>1<00>.../RPC<00>
Now netstring-encode it: prepend it with its total length (just this
header block, ignoring the request body) in decimal digits, and a
colon separator.
63:CONTENT_LENGTH<00>.../RPC<00>
Append to that string a comma, then the request body. Send all that
in one lump to the socket.
You'll get back a series of lines (terminated with CR LF); the first
blank line indicates the end of the headers, and everything after that
should be XML. You can parse this properly if you have a convenient
XMLRPC decoder, but for these purposes you can just use a generic XML
parser and snag the contents of every string
or i8
node (it may
emit other types which I haven't yet seen). In the case of this
download_list
command, you'll get a list of strings with hashes of
the torrents that rtorrent knows about.
I found that sending further commands to the socket left it
unresponsive, so I open and close a new socket connection for each
command. There is probably a better way of doing this.
Sending system.listMethods
will list every call that the system
supports. There's very little documentation beyond the source code,
but ones that apply to individual torrent downloads start with d. and
take the hash as (first?) parameter; some useful ones are:
- d.name - the torrent's name
- d.base_path - full path to download
- d.complete - 1 if it's complete, 0 if not
- d.ratio - 1000× the upload ratio
- d.state - 1 if running, 0 if not
- d.timestamp-started - unixtime when the download began
- d.timestamp-finished - unixtime when the download completed
- d.erase - remove the torrent from the client (not deleting its
files, you can do that in your own code)
- d.close - make the torrent inactive in the client
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.