BitTorrent encoding implementation for Clojure.
Clojure implementation of Bencode, the encoding used by BitTorrent for storing and transmitting loosely structured data.
Add the following dependency to your project.clj file:
[bencode "0.2.5"]
First, import the bencode.core
namespace:
(use '[bencode.core])
At this point, you should be able to use bencode
and bdecode
functions for
encoding and decoding, respectively:
(bencode {:cow "moo" :spam ["info" 32]})
;; -> "d3:cow3:moo4:spaml4:infoi32eee"
(bdecode "d3:cow3:moo4:spaml4:infoi32eee")
;; -> {:cow "moo", :spam ["info" 32]}
According to the Bencoding spec, only strings, integers, lists and dictionaries should be supported. Furthermore, only strings can be used as keys in a dictionary, and the keys must appear in sorted order (sorted as raw strings, not alphanumerics).
On the Clojure side, keywords are encoded as strings, sets and vectors are encoded as lists, and all integers - byte, short, int, long, big integers - are encoded as integers with arbitrary size, and decoded to the smallest type which can hold the number without losing data.
The bencode
function also accepts an optional map:
(bencode "moo" {:raw-str? true})
;; -> #<byte[] [B@53c059f6>
These are the supported encoding options:
:to
- Instance of OutputStream
where the encoding result should be
written to. Default: nil
:raw-str?
- Whether the string being encoded should be returned as a
byte array. This option can only be used if the option :to
is absent.
Default: false
The bdecode
function also accepts an optional map:
(bdecode "d3:cow3:moo4:spaml4:infoi32eee", {:str-keys? true :raw-keys ["spam"]})
;; -> {"cow" "moo", "spam" [#<byte[] [B@74184b3b> 32]}
The input might be either a string, a byte array or an input stream.
These are the supported decoding options:
:str-keys?
- Whether strings should be used as dictionary keys instead of
keywords. Default: false
:raw-keys
- List containing all dictionary keys whose values should be
decoded as raw strings instead of UTF-8-encoded strings. Default: nil
A collection of useful functions for BitTorrent metainfo parsing are available
under the bencode.metainfo.reader
namespace:
(use '[bencode.metainfo.reader])
;; parsing an input stream
(def metainfo (parse-metainfo input-stream))
;; parsing a file given its path
(def metainfo (parse-metainfo-file "/file/path"))
To extract bits of information from this metainfo:
(torrent-name metainfo)
;; -> "my.supercool.torrent"
(public-torrent? metainfo)
;; -> true
(torrent-info-hash-str metainfo)
;; -> "b174c9c090275f858853ba5ea1b01762eaa59f9d"
It's also possible to generate the Magnet link for a metainfo:
(torrent-magnet-link metainfo)
;; -> "magnet:?xt=urn:btih:..."
Please check out the source code for a complete list of the available functions.
It's very easy to create a metainfo file:
(use '[bencode.metainfo.writer])
(def metainfo (create-metainfo :file file-obj
:created-by "You"
:announce-list [["tracker-1"] ["tracker-2"]]
:private? false
:name "optional.torrent.name"
:piece-length-power 7 ;; piece length = 2^7KiB
:n-threads 4 ;; for fast parallel piece hashing
:comment "Some torrent"))
This operation might take several minutes depending on the file size.
To be able to import this file in your favorite BitTorrent client, just bencode
metainfo
to a .torrent file and you're done:
(bencode metainfo {:to file-out-stream})
Copyright (C) Daniel Fernandes Martins
Distributed under the New BSD License. See COPYING for further details.