Last active
May 21, 2025 16:37
-
-
Save marcinczenko/a87baf506bb0fd4bdff752cda4c685a1 to your computer and use it in GitHub Desktop.
A simple script to bencode BitTorrent info dictionary (v1, single file)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import itertools as it | |
import os | |
import json | |
from hashlib import sha1 | |
def to_hex(b): | |
return [block.hex() for block in b] | |
def encode(obj): | |
""" | |
bencodes given object. Given object should be a int, | |
bytes, list or dict. If a str is given, it'll be | |
encoded as UTF-8. | |
>>> [encode(i) for i in (-2, 42, b"answer", b"")] \ | |
== [b'i-2e', b'i42e', b'6:answer', b'0:'] | |
True | |
>>> encode([b'a', 42, [13, 14]]) == b'l1:ai42eli13ei14eee' | |
True | |
>>> encode({b'bar': b'spam', b'foo': 42, b'mess': [1, b'c']}) \ | |
== b'd3:bar4:spam3:fooi42e4:messli1e1:cee' | |
True | |
""" | |
if isinstance(obj, int): | |
return b"i" + str(obj).encode() + b"e" | |
elif isinstance(obj, bytes): | |
return str(len(obj)).encode() + b":" + obj | |
elif isinstance(obj, str): | |
return encode(obj.encode("utf-8")) | |
elif isinstance(obj, list): | |
return b"l" + b"".join(map(encode, obj)) + b"e" | |
elif isinstance(obj, dict): | |
if all(isinstance(i, bytes) for i in obj.keys()): | |
items = list(obj.items()) | |
items.sort() | |
return b"d" + b"".join(map(encode, it.chain(*items))) + b"e" | |
else: | |
raise ValueError("dict keys should be bytes " + str(obj.keys())) | |
raise ValueError("Allowed types: int, bytes, list, dict; not %s", type(obj)) | |
class Torrent: | |
data = { | |
"name": "data1M.bin", | |
"piece length": 262144, | |
"pieces": [ | |
"111421FEBA308CD51E9ACF88417193A9EA60F0F84646", | |
"11143D4A8279853DA2DA355A574740217D446506E8EB", | |
"11141AD686B48B9560B15B8843FD00E7EC1B59624B09", | |
"11145015E7DA0C40350624C6B5A1FED1DB39720B726C" | |
], | |
"length": 1048576 | |
} | |
announce = 'http://example.com/announce' | |
def __init__(self, path): | |
if path is not None: | |
path = os.path.normpath(path) | |
if os.path.exists(path): | |
with open(path, 'r') as f: | |
torrentJson = json.load(f) | |
self.data = torrentJson['info'] | |
if 'announce' in torrentJson: | |
self.announce = torrentJson['announce'] | |
print(f"Using values from file: {path}") | |
else: | |
print(f"File does not exist: {path}. Using example values:") | |
else: | |
print("No file provided. Using example values:") | |
# Convert to JSON string | |
json_data = json.dumps(self.data, indent=2) | |
print(json_data) | |
self.name = self.data['name'] | |
self.piece_length = self.data['piece length'] | |
self.pieces = self.data['pieces'] | |
self.length = self.data['length'] | |
# convert the pieces to bytes | |
self.pieces = [bytes.fromhex(h) for h in self.pieces] | |
# flatten the piece hashes into a single bytes object | |
self.pieces = bytes([byte for piece in self.pieces for byte in piece]) | |
def createV1(self): | |
""" | |
Create a v1 metainfo dictionary. | |
:param tracker: tracker URL | |
:param hybrid: Also generate v1 fields for backwards compatibility | |
""" | |
info = {b'name': self.name, b'length': self.length, b'piece length': self.piece_length, b'pieces': self.pieces} | |
self.infov1 = info | |
return {b'announce': self.announce, b'info': info} | |
def info_hash_v1(self): | |
return sha1(encode(self.infov1)).hexdigest() | |
if __name__ == "__main__": | |
import argparse | |
parser = argparse.ArgumentParser(description='BitTorrent v1 b-encoder') | |
parser.add_argument('path', nargs='?', | |
default=None, help='JSON file info dictionary to be used - when not provided example values will be used') | |
args = parser.parse_args() | |
t = Torrent(args.path) | |
open(t.name + '.torrent', 'wb').write(encode(t.createV1())) | |
print("v1 infohash {0:s}".format(t.info_hash_v1())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment