Task 734: .TORRENT File Format
Task 734: .TORRENT File Format
1. List of Properties in the .TORRENT File Format
The .TORRENT file format is a bencoded dictionary (using the Bencode encoding scheme) that contains metadata for BitTorrent transfers. Bencode supports dictionaries, lists, integers, and byte strings. The format is specified in BitTorrent Enhancement Proposal 3 (BEP-3), with additional common extensions documented in community references. Below is a comprehensive list of the intrinsic properties (keys) in the file format, organized by the root dictionary and the nested 'info' dictionary. Each property includes its type, optionality, and description. Note that the format distinguishes between single-file and multi-file modes within the 'info' dictionary.
Root Dictionary Properties
- announce: Type - string; Required; Description - The URL of the primary tracker server.
- announce-list: Type - list of lists of strings; Optional; Description - A tiered list of alternative tracker URLs for redundancy.
- comment: Type - string; Optional; Description - Free-form textual comments about the torrent.
- created by: Type - string; Optional; Description - The name and version of the software used to create the torrent.
- creation date: Type - integer; Optional; Description - The creation timestamp in UNIX epoch format (seconds since January 1, 1970, UTC).
- encoding: Type - string; Optional; Description - The string encoding format used (typically UTF-8).
- info: Type - dictionary; Required; Description - Contains detailed metadata about the file(s) being shared (detailed below).
'info' Dictionary Properties (Common to Both Single-File and Multi-File Modes)
- piece length: Type - integer; Required; Description - The size in bytes of each piece into which the file(s) are divided (typically a power of 2).
- pieces: Type - byte string; Required; Description - A concatenation of 20-byte SHA-1 hashes, one for each piece.
- private: Type - integer (0 or 1); Optional; Description - If set to 1, restricts peer discovery to trackers only; otherwise, allows extensions like DHT.
'info' Dictionary Properties (Single-File Mode Only)
- length: Type - integer; Required (in single-file mode); Description - The total length of the file in bytes.
- md5sum: Type - string; Optional; Description - A 32-character hexadecimal MD5 checksum of the file (not used by the BitTorrent protocol itself).
- name: Type - string; Required; Description - The suggested filename for saving the file.
'info' Dictionary Properties (Multi-File Mode Only)
- files: Type - list of dictionaries; Required (in multi-file mode); Description - A list where each entry is a dictionary describing one file, with the following sub-properties:
- length: Type - integer; Required; Description - The length of the file in bytes.
- md5sum: Type - string; Optional; Description - A 32-character hexadecimal MD5 checksum of the file (not used by the BitTorrent protocol itself).
- path: Type - list of strings; Required; Description - The file's path as a list of directory and filename components.
- name: Type - string; Required; Description - The suggested directory name for saving the files.
All strings are UTF-8 encoded. The format does not include file system-specific attributes like permissions or timestamps beyond the listed properties.
2. Two Direct Download Links for .TORRENT Files
- https://releases.ubuntu.com/25.10/ubuntu-25.10-desktop-amd64.iso.torrent
- https://releases.ubuntu.com/24.04/ubuntu-24.04.3-desktop-amd64.iso.torrent
These links provide torrent files for Ubuntu desktop ISO images, which are legal and publicly distributed.
3. HTML/JavaScript for Drag-and-Drop .TORRENT File Parser
The following is a self-contained HTML document with embedded JavaScript that allows users to drag and drop a .TORRENT file. Upon dropping, it parses the bencoded content and displays all properties listed in section 1 on the screen. This uses the FileReader API and a custom Bencode parser.
4. Python Class for .TORRENT File Handling
The following Python class can open a .TORRENT file, decode its bencoded content, read and print all properties, and write a modified or new torrent back to a file.
import os
import struct
class TorrentFile:
def __init__(self, filepath=None):
self.data = {}
if filepath:
self.load(filepath)
def _bdecode(self, data):
def decode(pos):
if data[pos] == ord('d'):
pos += 1
d = {}
while data[pos] != ord('e'):
key, pos = decode(pos)
val, pos = decode(pos)
d[key] = val
return d, pos + 1
elif data[pos] == ord('l'):
pos += 1
l = []
while data[pos] != ord('e'):
val, pos = decode(pos)
l.append(val)
return l, pos + 1
elif data[pos] == ord('i'):
pos += 1
num = 0
sign = 1
if data[pos] == ord('-'):
sign = -1
pos += 1
while data[pos] != ord('e'):
num = num * 10 + (data[pos] - ord('0'))
pos += 1
return num * sign, pos + 1
else:
len_ = 0
while data[pos] != ord(':'):
len_ = len_ * 10 + (data[pos] - ord('0'))
pos += 1
pos += 1
str_ = data[pos:pos + len_].decode('utf-8', errors='replace')
return str_, pos + len_
return decode(0)[0]
def _bencode(self, obj):
if isinstance(obj, dict):
return b'd' + b''.join(self._bencode(k) + self._bencode(v) for k, v in sorted(obj.items())) + b'e'
elif isinstance(obj, list):
return b'l' + b''.join(self._bencode(i) for i in obj) + b'e'
elif isinstance(obj, int):
return f'i{obj}e'.encode()
elif isinstance(obj, str):
return f'{len(obj)}:{obj}'.encode()
else:
raise ValueError("Unsupported type for bencoding")
def load(self, filepath):
with open(filepath, 'rb') as f:
self.data = self._bdecode(f.read())
def save(self, filepath):
with open(filepath, 'wb') as f:
f.write(self._bencode(self.data))
def print_properties(self):
def print_dict(d, indent=''):
for k, v in d.items():
print(f"{indent}{k}: ", end='')
if isinstance(v, dict):
print("dictionary")
print_dict(v, indent + ' ')
elif isinstance(v, list):
print("list")
for i, item in enumerate(v):
print(f"{indent} [{i}]: ", end='')
if isinstance(item, dict):
print("dictionary")
print_dict(item, indent + ' ')
else:
print(item)
else:
print(v)
print_dict(self.data)
# Example usage:
# t = TorrentFile('example.torrent')
# t.print_properties()
# t.save('output.torrent')
5. Java Class for .TORRENT File Handling
The following Java class can open a .TORRENT file, decode its bencoded content, read and print all properties, and write a modified or new torrent back to a file. It requires Java 8 or later.
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
public class TorrentFile {
private Map<String, Object> data = new HashMap<>();
public TorrentFile(String filepath) throws IOException {
if (filepath != null) {
load(filepath);
}
}
private Object bdecode(byte[] bytes, MutableInt pos) {
char c = (char) bytes[pos.value];
if (c == 'd') {
pos.value++;
Map<String, Object> dict = new LinkedHashMap<>();
while ((char) bytes[pos.value] != 'e') {
String key = (String) bdecode(bytes, pos);
Object val = bdecode(bytes, pos);
dict.put(key, val);
}
pos.value++;
return dict;
} else if (c == 'l') {
pos.value++;
List<Object> list = new ArrayList<>();
while ((char) bytes[pos.value] != 'e') {
list.add(bdecode(bytes, pos));
}
pos.value++;
return list;
} else if (c == 'i') {
pos.value++;
long num = 0;
int sign = 1;
if ((char) bytes[pos.value] == '-') {
sign = -1;
pos.value++;
}
while ((char) bytes[pos.value] != 'e') {
num = num * 10 + (bytes[pos.value] - '0');
pos.value++;
}
pos.value++;
return num * sign;
} else {
int len = 0;
while ((char) bytes[pos.value] != ':') {
len = len * 10 + (bytes[pos.value] - '0');
pos.value++;
}
pos.value++;
String str = new String(bytes, pos.value, len);
pos.value += len;
return str;
}
}
private byte[] bencode(Object obj) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
encode(obj, baos);
} catch (IOException e) {
// Ignored for in-memory
}
return baos.toByteArray();
}
private void encode(Object obj, OutputStream os) throws IOException {
if (obj instanceof Map) {
os.write('d');
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) obj;
List<String> keys = new ArrayList<>(map.keySet());
Collections.sort(keys);
for (String key : keys) {
encode(key, os);
encode(map.get(key), os);
}
os.write('e');
} else if (obj instanceof List) {
os.write('l');
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) obj;
for (Object item : list) {
encode(item, os);
}
os.write('e');
} else if (obj instanceof Long) {
os.write(("i" + obj + "e").getBytes());
} else if (obj instanceof String) {
String str = (String) obj;
os.write((str.length() + ":" + str).getBytes());
} else {
throw new IllegalArgumentException("Unsupported type");
}
}
public void load(String filepath) throws IOException {
byte[] bytes = Files.readAllBytes(Paths.get(filepath));
MutableInt pos = new MutableInt(0);
data = (Map<String, Object>) bdecode(bytes, pos);
}
public void save(String filepath) throws IOException {
Files.write(Paths.get(filepath), bencode(data));
}
public void printProperties() {
printDict(data, "");
}
private void printDict(Map<String, Object> dict, String indent) {
for (Map.Entry<String, Object> entry : dict.entrySet()) {
System.out.print(indent + entry.getKey() + ": ");
Object val = entry.getValue();
if (val instanceof Map) {
System.out.println("dictionary");
@SuppressWarnings("unchecked")
Map<String, Object> subDict = (Map<String, Object>) val;
printDict(subDict, indent + " ");
} else if (val instanceof List) {
System.out.println("list");
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) val;
for (int i = 0; i < list.size(); i++) {
Object item = list.get(i);
System.out.print(indent + " [" + i + "]: ");
if (item instanceof Map) {
System.out.println("dictionary");
@SuppressWarnings("unchecked")
Map<String, Object> subDict = (Map<String, Object>) item;
printDict(subDict, indent + " ");
} else {
System.out.println(item);
}
}
} else {
System.out.println(val);
}
}
}
static class MutableInt {
int value;
MutableInt(int v) { value = v; }
}
// Example usage:
// public static void main(String[] args) throws IOException {
// TorrentFile t = new TorrentFile("example.torrent");
// t.printProperties();
// t.save("output.torrent");
// }
}
6. JavaScript Class for .TORRENT File Handling
The following JavaScript class (Node.js compatible) can open a .TORRENT file, decode its bencoded content, read and print all properties to the console, and write a modified or new torrent back to a file. It uses the 'fs' module.
const fs = require('fs');
class TorrentFile {
constructor(filepath) {
this.data = {};
if (filepath) {
this.load(filepath);
}
}
_bdecode(data) {
let pos = 0;
const decode = () => {
if (data[pos] === 100) { // 'd'
pos++;
const dict = {};
while (data[pos] !== 101) { // 'e'
const key = decode();
const val = decode();
dict[key] = val;
}
pos++;
return dict;
} else if (data[pos] === 108) { // 'l'
pos++;
const list = [];
while (data[pos] !== 101) {
list.push(decode());
}
pos++;
return list;
} else if (data[pos] === 105) { // 'i'
pos++;
let num = 0;
let sign = 1;
if (data[pos] === 45) { // '-'
sign = -1;
pos++;
}
while (data[pos] !== 101) {
num = num * 10 + (data[pos] - 48);
pos++;
}
pos++;
return num * sign;
} else {
let len = 0;
while (data[pos] !== 58) { // ':'
len = len * 10 + (data[pos] - 48);
pos++;
}
pos++;
const str = new TextDecoder().decode(data.slice(pos, pos + len));
pos += len;
return str;
}
};
return decode();
}
_bencode(obj) {
if (typeof obj === 'object' && !Array.isArray(obj)) {
let encoded = 'd';
Object.keys(obj).sort().forEach(key => {
encoded += this._bencode(key) + this._bencode(obj[key]);
});
return encoded + 'e';
} else if (Array.isArray(obj)) {
let encoded = 'l';
obj.forEach(item => {
encoded += this._bencode(item);
});
return encoded + 'e';
} else if (Number.isInteger(obj)) {
return `i${obj}e`;
} else if (typeof obj === 'string') {
return `${obj.length}:${obj}`;
} else {
throw new Error('Unsupported type for bencoding');
}
}
load(filepath) {
const buffer = fs.readFileSync(filepath);
this.data = this._bdecode(new Uint8Array(buffer));
}
save(filepath) {
fs.writeFileSync(filepath, Buffer.from(this._bencode(this.data), 'utf-8'));
}
printProperties() {
const printDict = (d, indent = '') => {
for (const key in d) {
const val = d[key];
console.log(`${indent}${key}: `, typeof val === 'object' ? (Array.isArray(val) ? 'list' : 'dictionary') : val);
if (typeof val === 'object') {
if (Array.isArray(val)) {
val.forEach((item, i) => {
console.log(`${indent} [${i}]: `, typeof item === 'object' ? (Array.isArray(item) ? 'list' : 'dictionary') : item);
if (typeof item === 'object') printDict(item, indent + ' ');
});
} else {
printDict(val, indent + ' ');
}
}
}
};
printDict(this.data);
}
}
// Example usage:
// const t = new TorrentFile('example.torrent');
// t.printProperties();
// t.save('output.torrent');
7. C++ Class for .TORRENT File Handling
The following C++ class can open a .TORRENT file, decode its bencoded content, read and print all properties to the console, and write a modified or new torrent back to a file. It uses standard libraries and requires C++11 or later.
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <variant>
#include <cctype>
using Variant = std::variant<long long, std::string, std::vector<Variant>, std::map<std::string, Variant>>;
class TorrentFile {
private:
std::map<std::string, Variant> data;
Variant bdecode(const std::string& bytes, size_t& pos) {
char c = bytes[pos];
if (c == 'd') {
pos++;
std::map<std::string, Variant> dict;
while (bytes[pos] != 'e') {
std::string key = std::get<std::string>(bdecode(bytes, pos));
Variant val = bdecode(bytes, pos);
dict[key] = val;
}
pos++;
return dict;
} else if (c == 'l') {
pos++;
std::vector<Variant> list;
while (bytes[pos] != 'e') {
list.push_back(bdecode(bytes, pos));
}
pos++;
return list;
} else if (c == 'i') {
pos++;
long long num = 0;
int sign = 1;
if (bytes[pos] == '-') {
sign = -1;
pos++;
}
while (bytes[pos] != 'e') {
num = num * 10 + (bytes[pos] - '0');
pos++;
}
pos++;
return num * sign;
} else {
size_t len = 0;
while (bytes[pos] != ':') {
len = len * 10 + (bytes[pos] - '0');
pos++;
}
pos++;
std::string str = bytes.substr(pos, len);
pos += len;
return str;
}
}
std::string bencode(const Variant& obj) {
struct Visitor {
std::string operator()(long long i) { return "i" + std::to_string(i) + "e"; }
std::string operator()(const std::string& s) { return std::to_string(s.size()) + ":" + s; }
std::string operator()(const std::vector<Variant>& l) {
std::string enc = "l";
for (const auto& item : l) enc += bencode(item);
return enc + "e";
}
std::string operator()(const std::map<std::string, Variant>& d) {
std::string enc = "d";
std::vector<std::string> keys;
for (const auto& p : d) keys.push_back(p.first);
std::sort(keys.begin(), keys.end());
for (const auto& key : keys) enc += bencode(key) + bencode(d.at(key));
return enc + "e";
}
};
return std::visit(Visitor{}, obj);
}
public:
TorrentFile(const std::string& filepath = "") {
if (!filepath.empty()) load(filepath);
}
void load(const std::string& filepath) {
std::ifstream file(filepath, std::ios::binary);
std::string bytes((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
size_t pos = 0;
data = std::get<std::map<std::string, Variant>>(bdecode(bytes, pos));
}
void save(const std::string& filepath) {
std::ofstream file(filepath, std::ios::binary);
file << bencode(data);
}
void printProperties() {
struct PrintVisitor {
std::string indent;
void operator()(long long i) { std::cout << i << std::endl; }
void operator()(const std::string& s) { std::cout << s << std::endl; }
void operator()(const std::vector<Variant>& l) {
std::cout << "list" << std::endl;
for (size_t i = 0; i < l.size(); ++i) {
std::cout << indent << " [" << i << "]: ";
std::visit(PrintVisitor{indent + " "}, l[i]);
}
}
void operator()(const std::map<std::string, Variant>& d) {
std::cout << "dictionary" << std::endl;
for (const auto& p : d) {
std::cout << indent << p.first << ": ";
std::visit(PrintVisitor{indent + " "}, p.second);
}
}
};
std::visit(PrintVisitor{""}, data);
}
};
// Example usage:
// int main() {
// TorrentFile t("example.torrent");
// t.printProperties();
// t.save("output.torrent");
// return 0;
// }