Strings are length-prefixed base ten followed by a colon and the string. For example 4:spam corresponds to ‘spam’.
Integers are represented by an ‘i’ followed by the number in base 10 followed by an ‘e’. For example i3e corresponds to 3 and i-3e corresponds to -3. Integers have no size limitation. i-0e is invalid. All encodings with a leading zero, such as i03e, are invalid, other than i0e, which of course corresponds to 0.
Lists are encoded as an ‘l’ followed by their elements (also bencoded) followed by an ‘e’. For example l4:spam4:eggse corresponds to [‘spam’, ‘eggs’].
Dictionaries are encoded as a ‘d’ followed by a list of alternating keys and their corresponding values followed by an ‘e’. For example, d3:cow3:moo4:spam4:eggse corresponds to {‘cow’: ‘moo’, ‘spam’: ‘eggs’} and d4:spaml1:a1:bee corresponds to {‘spam’: [‘a’, ‘b’]}. Keys must be strings and appear in sorted order (sorted as raw strings, not alphanumerics).
ifnot pieces: return s.decode("utf8") else: # pieces maps to a string whose length is a multiple of 20. # It is to be subdivided into strings of length 20, # each of which is the SHA1 hash of the piece at the corresponding index. result = [] for j inrange(0, length, 20): hash = s[j:j + 20] result.append(hash.hex().lower()) return result
definteger(self, timestamp=False): ifself.next() != "i": raise Exception("Integer must begin with i") val = self.num()
if timestamp: val = datetime.fromtimestamp(val).__str__()
ifself.next() != "e": raise Exception("Integer must end with e") return val
deflist(self): ifself.next() != "l": raise Exception("list must begin with l") result = [] whileself.peek() != 'e': result.append(self.element()) self.next() return result
defdic(self): ifself.next() != 'd': raise Exception("dic must begin with d") result = dict()
whileself.peek() != "e": key = self.string() val = None if key == "pieces": val = self.element(pieces=True) elif key == 'creation date': val = self.element(timestamp=True) else: info_start = None info_end = None if key == 'info': info_start = self.i val = self.element() if key == 'info': info_end = self.i result['info_hash'] = self.sha1(self.arr[info_start:info_end]) result[key] = val
self.next() return result
defsha1(self, info): import hashlib p = hashlib.sha1() p.update(info) return p.hexdigest()
defget_content(): path = "/Users/ym/tmp/venom.torrent" withopen(path, "rb") as f: return f.read()
defmain(): content = get_content() result = BDecode(content).parse() import pprint pprint.pprint(result)