Torrent

Written by Amar Singh

Inspired from: Guile-Torrent

This is how I have structured the module.

img

What can it do?

  1. Provide a language for writing torrents.

Read bencode to S-bencode (source-expression), and write s-bencode to bencode. From a S-metainfo, generate a .torrent file (in progress). I haven’t really made it usable. This was an experiment for me to figure out how bittorrent works, and it’s still not finished yet.

I can say with some confidence that I’d like to work more on this module a bit later. There is a lot that can be improved. Hopefully, this will become useful for someone, for a better implementation see Guile-Torrent. Though that link did not work the last time i checked, here is the homepage of the author of Guile-Torrent, in case he makes Guile-Torrent available elsewhere on the web.

This is how you would write a torrent(if you were so inclined) using S-metainfo sub-language. Omitting the pieces field, because it’s just too big for this sample.

(define sample (s-metainfo (announce "http://inferno.demonoid.me:3414/announce")
          (announce-list "http://inferno.demonoid.me:3414/announce"
                         "udp://tracker.yify-torrents.com/announce"
                         "udp://tracker.1337x.org:80/announce"
                         "http://exodus.desync.com:6969/announce"
                         "udp://tracker.openbittorrent.com:80/announce"
                         "http://retracker.hotplug.ru:2710/announce"
                         "http://tracker.yify-torrents.com/announce")
          (created-by "uTorrent/2210")
          (creation-date 1332536549)
          (encoding "UTF-8")
          (info (files (file (blength 353)
                             (path "Other" "Torrent Downloaded From ExtraTorrent.com.txt"))
                       (file (blength 59)
                             (path "Other" "AhaShare.com.txt"))
                       (file (blength 47)
                             (path "Other" "Torrent downloaded from Demonoid.com - Copy.txt"))
                       (file (blength 837425564)
                             (path "Starship.Troopers.1997.720p.BrRip.x264.YIFY.mp4"))
                       (file (blength 130677)
                             (path "WWW.YIFY-TORRENTS.COM.jpg"))
                       (file (blength 106394)
                             (path "Starship.Troopers.1997.720p.BrRip.x264.YIFY.srt")))
                (name "Starship Troopers (1997)")
                (piece-length 1048576)
                (pieces "3103...<big-base16-string>..."))))

This is ‘compiled down’ to a lower level form, henceforth dubbed S-Bencode.

(define intermediate (s-metainfo->s-bencode sample))

This intermediate form looks like the following:

(dict (str "announce")
      (str "http://inferno.demonoid.me:3414/announce")
      (str "announce-list")
      (blist (blist (str "http://inferno.demonoid.me:3414/announce"))
             (blist (str "udp://tracker.yify-torrents.com/announce"))
             (blist (str "udp://tracker.1337x.org:80/announce"))
             (blist (str "http://exodus.desync.com:6969/announce"))
             (blist (str "udp://tracker.openbittorrent.com:80/announce"))
             (blist (str "http://retracker.hotplug.ru:2710/announce"))
             (blist (str "http://tracker.yify-torrents.com/announce")))
      (str "created by")
      (str "uTorrent/2210")
      (str "creation date")
      (int 1332536549)
      (str "encoding")
      (str "UTF-8")
      (str "info")
      (dict (str "files")
            (blist (dict (str "length")
                         (int 353)
                         (str "path")
                         (blist (str "Other")
                                (str "Torrent Downloaded From ExtraTorrent.com.txt")))
                   (dict (str "length")
                         (int 59)
                         (str "path")
                         (blist (str "Other")
                                (str "AhaShare.com.txt")))
                   (dict (str "length")
                         (int 47)
                         (str "path")
                         (blist (str "Other")
                                (str "Torrent downloaded from Demonoid.com - Copy.txt")))
                   (dict (str "length")
                         (int 837425564)
                         (str "path")
                         (blist (str "Starship.Troopers.1997.720p.BrRip.x264.YIFY.mp4")))
                   (dict (str "length")
                         (int 130677)
                         (str "path")
                         (blist (str "WWW.YIFY-TORRENTS.COM.jpg")))
                   (dict (str "length")
                         (int 106394)
                         (str "path")
                         (blist (str "Starship.Troopers.1997.720p.BrRip.x264.YIFY.srt"))))
            (str "name")
            (str "Starship Troopers (1997)")
            (str "piece length")
            (int 1048576)
            (str "pieces")
            (byt "3103...<big-base16-string>...")))

The final result looks like the following, again omitting the full expression, only because of the lack of space for it.

"d8:announce40:http://inferno.demonoid.me:3..."

Eval & Apply

How does this work? Following a simple model. You need apply, eval, evlis and an environment. The task is to recursively translate the nested expressions as they are in ‘s-metainfo’ and ‘s-bencode’ examples into their substitutions based on an ‘environment’.

In my module i’ve dubbed these procedures taken from Lisp 1.5: apply(regular apply), beval, bevlis, and an array of environments each for a different purpose and target language. This was a useful insight to use Eval, the most general function in Lisp family to do your bidding.