Tools for creating ".torrent" files

All other I2P Bittorrent related talk
User avatar
lgillis
Posts: 128
Joined: Mon May 09, 2022 8:40 am

Tools for creating ".torrent" files

Post by lgillis »

Two tools for creating ".torrent" files are transmission-create and mktorrent. Both programs are already included with many distributions or are easy to install.
Description-en: mktorrent is a text-based utility to create BitTorrent metainfo files used by trackers and torrent clients. It can create metainfo files for single files or complete directories in a fast way.

It supports:
  • multiple trackers,
  • embedding custom comments into torrent files,
  • multi-threaded hashing.
It also supports setting the "private" flag, which advises the BitTorrent agents to refrain from using alternative peer discovery mechanisms, such as Distributed Hash Table (DHT), Local Peer Discovery (LPD), or Peer Exchange.

Homepage: https://github.com/pobrn/mktorrent
transmission-create is in Debian e.g. in the transmission-cli package.
Description-en: lightweight BitTorrent client (command line programs). Transmission is a set of lightweight BitTorrent clients (in GUI, CLI and daemon form). All its incarnations feature a very simple, intuitive interface on top on an efficient, cross-platform back-end.

This package contains a (deprecated) stand-alone command-line client, transmission-remote to interface with transmission-daemon and tools to create, edit and inspect torrent files.

Homepage: https://transmissionbt.com/
The main differences (imo):
  • transmission-create calculates the size of the pieces itself, mktorrent uses a default value and leaves it up to the user to determine the size.
  • transmission-create has an "anonymize" switch to remove the date and program name. mktorrent can only omit the date. (But be careful, if "anonymize" is misspelled in transmission-create, processing is not aborted but the switch is simply discarded. In the end, a check should take place for both -> Torrent File Editor).
  • In terms of speed, mktorrent is slightly ahead, but only for relatively large files, because mktorrent uses all CPUs by default (multithreading). The decisive factor, however, is the throughput of the storage medium to be read - more than 100% utilization is unfortunately not possible.
Here is an simple example of the usage:

Code: Select all

transmission-create -o "Name of the.torrent" -t "http://tracker2.postman.i2p/announce.php" -c "No comment\!" --anonymize "The data source"

Code: Select all

mktorrent --output="Name of the.torrent" --announce="http://tracker2.postman.i2p/announce.php" --comment="No comment\!" "The data source"
As you can see, handling is not complicated and both tools are suitable for use in shell scripts. Several trackers can then be conveniently used as default settings.
The implosion of I2P is not a question of if, it is a question of when.
User avatar
lgillis
Posts: 128
Joined: Mon May 09, 2022 8:40 am

Re: Tools for creating ".torrent" files

Post by lgillis »

A note on creating torrents, regardless of the programs used.
  • Make sure that you only use active trackers.
  • Preferably use readable addresses. Use B32 or ADR addresses only if none are available.
  • Do not blindly use any tracker lists for your own torrents, but check the unknown URLs.
  • When using a mixture of readable, B32 or ADR addresses, make sure that the trackers are not duplicated.
  • Data minimization. Generally creates only one tracker group, whereby the announce tracker is given priority and all others are only used as backup trackers.
The implosion of I2P is not a question of if, it is a question of when.
anikey
Posts: 22
Joined: Thu Dec 07, 2023 9:22 pm

Re: Tools for creating ".torrent" files

Post by anikey »

Note for those who just cross-seed from clearnet:

You don't have to create new torrent files. You can just modify existing ones to remove clearnet trackers and add i2p trackers. This way the torrent will have the same infohash, which helps with deduplicating data.

(i thought this would be helpful so i write this).
ghost92
Posts: 12
Joined: Tue Sep 19, 2023 3:34 am

Re: Tools for creating ".torrent" files

Post by ghost92 »

Python tool for cross-seeding that converts clearnet to I2P: viewtopic.php?t=98
User avatar
cumlord
Posts: 28
Joined: Thu Oct 05, 2023 5:01 pm
Location: Erect, NC
Contact:

Re: Tools for creating ".torrent" files

Post by cumlord »

the tool posted at viewtopic.php?t=100 does decide the piece size before giving the command to mktorrent, so i've cut that part out and put it in this if anyone wants to use it for new torrents that aren't season packs lol.

it will take either a single file or a directory and put a .torrent file in that directory or the directory containing it (if you give it a folder). It will give you the size, command used on mktorrent, location of the .torrent, and the hash. Need to have mktorrent installed already of course.

If you want to get the hash you need to do "pip install bencoding" or use the second version that uses only the standard library. Copy and paste the code to a file named "mktorrent.py" then do python3 path/to/mktorrent.py to run, and paste the path of what you want to be a .torrent.

change the trackers as you see fit i've just copied the ones found here from ghost92.

for linux easiest might be to make a venv or use the second one w/o hash (something like this):

Code: Select all

sudo apt-get install -y mktorrent
python3 -m venv ~/scripts/mktorrent
source ~/scripts/mktorrent/bin/activate
pip install bencoding
cd ~/scripts/mktorrent
sudo nano mktorrent.py -->paste code, ctrl+x, y
then to run:

Code: Select all

source ~/scripts/mktorrent/bin/activate
python3 ~/scripts/mktorrent/mktorrent.py

Code: Select all

#!/usr/bin/env python3
import subprocess
import os
import hashlib
import bencoding

###################################
#Tracker list, edit here
###################################
tracker_list = [
"http://w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p/a",
"http://opentracker.dg2.i2p/a",
"http://tracker2.postman.i2p/announce.php",
"http://tu5skej67ftbxjghnx3r2txp6fqz6ulkolkejc77be2er5v5zrfq.b32.i2p/announce.php",
"http://lnQ6yoBTxQuQU8EQ1FlF395ITIQF-HGJxUeFvzETLFnoczNjQvKDbtSB7aHhn853zjVXrJBgwlB9sO57KakBDaJ50lUZgVPhjlI19TgJ-CxyHhHSCeKx5JzURdEW-ucdONMynr-b2zwhsx8VQCJwCEkARvt21YkOyQDaB9IdV8aTAmP~PUJQxRwceaTMn96FcVenwdXqleE16fI8CVFOV18jbJKrhTOYpTtcZKV4l1wNYBDwKgwPx5c0kcrRzFyw5~bjuAKO~GJ5dR7BQsL7AwBoQUS4k1lwoYrG1kOIBeDD3XF8BWb6K3GOOoyjc1umYKpur3G~FxBuqtHAsDRICkEbKUqJ9mPYQlTSujhNxiRIW-oLwMtvayCFci99oX8MvazPS7~97x0Gsm-onEK1Td9nBdmq30OqDxpRtXBimbzkLbR1IKObbg9HvrKs3L-kSyGwTUmHG9rSQSoZEvFMA-S0EXO~o4g21q1oikmxPMhkeVwQ22VHB0-LZJfmLr4SAAAA.i2p/announce.php",
"http://ahsplxkbhemefwvvml7qovzl5a2b5xo5i7lyai7ntdunvcyfdtna.b32.i2p/announce.php",
"http://opentracker.skank.i2p/a",
"http://opentracker.dg2.i2p/a",
"http://opentracker.r4sas.i2p/a",
"http://omitracker.i2p/announce.php",
"http://i32bdpbyflc5l7ibhqmq7f72gglflrd6sgnmlqj6xsdjis5yjuga.b32.i2p/announce",
"http://4izytry23uiwxs2epg6swoygyi4vvsd4hwvj5u6onodmoh7gfmha.b32.i2p/announce",
]
###################################
print("Will create a .torrent file either in the same directory as the file or the containing directory (if directory used). Sudo nano <path/to/mktorrent.py> to edit the list of trackers.")
season_path = input("Enter the path to the file or directory: ")
destination_directory_x = ""
class Color:
    RED = '\033[91m'
    GREEN = '\033[92m'
    RESET = '\033[0m'
total_size = 0
def process_path(path):
    global total_size
    global torrent_file_name
    global file_name_qouted
    global destination_directory_x
    global season_path
    global torrent_file_name_no_qoute
    if os.path.isfile(path):
        total_size = os.path.getsize(season_path) / 1000000000
        remove_extension = season_path.rfind('.')
        if remove_extension != -1:
            no_extension = season_path[:remove_extension]
        else:
            print(Color.RED + "No '.' found in string to remove extension" + Color.RESET)
        torrent_file_name = '"' + no_extension + ".torrent" + '"'
        torrent_file_name_no_qoute = torrent_file_name[1:-1]
        file_name_qouted = '"' + season_path + '"'
        destination_directory_x = season_path
    elif os.path.isdir(path):
        for dirpath, dirnames, filenames in os.walk(season_path):
            for filename in filenames:
                file_path = os.path.join(dirpath, filename)
                total_size += os.path.getsize(file_path)
        total_size = total_size / 1000000000
        destination_directory_x = season_path
        torrent_file_name = '"' + destination_directory_x + ".torrent" + '"'
        torrent_file_name_no_qoute = torrent_file_name[1:-1]
        file_name_qouted = '"' + destination_directory_x + '"'
    else:
        print(f"{Color.RED}{path} error, not a directory or file??????{Color.RESET}")
process_path(season_path)
if total_size < 0.512:
    piece_size = "-l 18"
if 0.512 <= total_size <= 1.024:    
    piece_size = "-l 19"
if 1.024 <= total_size <= 2:    
    piece_size = "-l 20"
if 2 <= total_size <= 4:    
    piece_size = "-l 21"
if 4 <= total_size <= 8:    
    piece_size = "-l 22"    
if 8 <= total_size <= 16:    
    piece_size = "-l 23"
if total_size > 16:
    piece_size = "-l 24"  
total_size_rounded = round(total_size, 2)          
# print("For size " + str(total_size_rounded) + "G, " + piece_size + " will be used in MKtorrent.")
print(Color.GREEN + "MKtorrent command: " + Color.RESET)
character_to_append = '-a '
modified_list = [character_to_append + element for element in tracker_list]
tracker_string = ' '.join(modified_list)
mktorrent_command = "mktorrent -v " + piece_size + " " + tracker_string + " -o " + torrent_file_name + " " + file_name_qouted
print(mktorrent_command)
try:
    completed_process = subprocess.Popen(mktorrent_command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    completed_process.wait()
    stdout, stderr = completed_process.communicate()
    print("MKtor Output:")
    print(stdout)
    
    if stderr == "":
        print(Color.GREEN + "Success. No errors reported." + Color.RESET)
    else:
        print("Standard Error:")    
        print(stderr)
except subprocess.CalledProcessError as e:
    print(f"{Color.RED}Command failed with return code {e.returncode}:{Color.RESET}")
    print(e.stderr)
objTorrentFile = open(torrent_file_name_no_qoute, "rb")
decodedDict = bencoding.bdecode(objTorrentFile.read())
info_hash = hashlib.sha1(bencoding.bencode(decodedDict[b"info"])).hexdigest()

print("Size of torrent: " + Color.GREEN + str(total_size_rounded) + "G" + Color.RESET)
print("MKtorrent peice size: " + Color.GREEN +  piece_size + " was used. Full command at beginning" + Color.RESET)
print(".torrent file location: " + Color.GREEN + torrent_file_name_no_qoute + Color.RESET)
print("Info hash: " + Color.GREEN + info_hash + Color.RESET)

standard library only (no hash given, don't need pip install bencoding)

Code: Select all

#!/usr/bin/env python3
import subprocess
import os
import hashlib
# import bencoding

###################################
#Tracker list, edit here
###################################
tracker_list = [
"http://w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p/a",
"http://opentracker.dg2.i2p/a",
"http://tracker2.postman.i2p/announce.php",
"http://tu5skej67ftbxjghnx3r2txp6fqz6ulkolkejc77be2er5v5zrfq.b32.i2p/announce.php",
"http://lnQ6yoBTxQuQU8EQ1FlF395ITIQF-HGJxUeFvzETLFnoczNjQvKDbtSB7aHhn853zjVXrJBgwlB9sO57KakBDaJ50lUZgVPhjlI19TgJ-CxyHhHSCeKx5JzURdEW-ucdONMynr-b2zwhsx8VQCJwCEkARvt21YkOyQDaB9IdV8aTAmP~PUJQxRwceaTMn96FcVenwdXqleE16fI8CVFOV18jbJKrhTOYpTtcZKV4l1wNYBDwKgwPx5c0kcrRzFyw5~bjuAKO~GJ5dR7BQsL7AwBoQUS4k1lwoYrG1kOIBeDD3XF8BWb6K3GOOoyjc1umYKpur3G~FxBuqtHAsDRICkEbKUqJ9mPYQlTSujhNxiRIW-oLwMtvayCFci99oX8MvazPS7~97x0Gsm-onEK1Td9nBdmq30OqDxpRtXBimbzkLbR1IKObbg9HvrKs3L-kSyGwTUmHG9rSQSoZEvFMA-S0EXO~o4g21q1oikmxPMhkeVwQ22VHB0-LZJfmLr4SAAAA.i2p/announce.php",
"http://ahsplxkbhemefwvvml7qovzl5a2b5xo5i7lyai7ntdunvcyfdtna.b32.i2p/announce.php",
"http://opentracker.skank.i2p/a",
"http://opentracker.dg2.i2p/a",
"http://opentracker.r4sas.i2p/a",
"http://omitracker.i2p/announce.php",
"http://i32bdpbyflc5l7ibhqmq7f72gglflrd6sgnmlqj6xsdjis5yjuga.b32.i2p/announce",
"http://4izytry23uiwxs2epg6swoygyi4vvsd4hwvj5u6onodmoh7gfmha.b32.i2p/announce",
]
###################################
print("Will create a .torrent file either in the same directory as the file or the containing directory (if directory used). Sudo nano <path/to/mktorrent.py> to edit the list of trackers.")
season_path = input("Enter the path to the file or directory: ")
destination_directory_x = ""
class Color:
    RED = '\033[91m'
    GREEN = '\033[92m'
    RESET = '\033[0m'
total_size = 0
def process_path(path):
    global total_size
    global torrent_file_name
    global file_name_qouted
    global destination_directory_x
    global season_path
    global torrent_file_name_no_qoute
    if os.path.isfile(path):
        total_size = os.path.getsize(season_path) / 1000000000
        remove_extension = season_path.rfind('.')
        if remove_extension != -1:
            no_extension = season_path[:remove_extension]
        else:
            print(Color.RED + "No '.' found in string to remove extension" + Color.RESET)
        torrent_file_name = '"' + no_extension + ".torrent" + '"'
        torrent_file_name_no_qoute = torrent_file_name[1:-1]
        file_name_qouted = '"' + season_path + '"'
        destination_directory_x = season_path
    elif os.path.isdir(path):
        for dirpath, dirnames, filenames in os.walk(season_path):
            for filename in filenames:
                file_path = os.path.join(dirpath, filename)
                total_size += os.path.getsize(file_path)
        total_size = total_size / 1000000000
        destination_directory_x = season_path
        torrent_file_name = '"' + destination_directory_x + ".torrent" + '"'
        torrent_file_name_no_qoute = torrent_file_name[1:-1]
        file_name_qouted = '"' + destination_directory_x + '"'
    else:
        print(f"{Color.RED}{path} error, not a directory or file??????{Color.RESET}")
process_path(season_path)
if total_size < 0.512:
    piece_size = "-l 18"
if 0.512 <= total_size <= 1.024:    
    piece_size = "-l 19"
if 1.024 <= total_size <= 2:    
    piece_size = "-l 20"
if 2 <= total_size <= 4:    
    piece_size = "-l 21"
if 4 <= total_size <= 8:    
    piece_size = "-l 22"    
if 8 <= total_size <= 16:    
    piece_size = "-l 23"
if total_size > 16:
    piece_size = "-l 24"  
total_size_rounded = round(total_size, 2)          
# print("For size " + str(total_size_rounded) + "G, " + piece_size + " will be used in MKtorrent.")
print(Color.GREEN + "MKtorrent command: " + Color.RESET)
character_to_append = '-a '
modified_list = [character_to_append + element for element in tracker_list]
tracker_string = ' '.join(modified_list)
mktorrent_command = "mktorrent -v " + piece_size + " " + tracker_string + " -o " + torrent_file_name + " " + file_name_qouted
print(mktorrent_command)
try:
    completed_process = subprocess.Popen(mktorrent_command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    completed_process.wait()
    stdout, stderr = completed_process.communicate()
    print("MKtor Output:")
    print(stdout)
    
    if stderr == "":
        print(Color.GREEN + "Success. No errors reported." + Color.RESET)
    else:
        print("Standard Error:")    
        print(stderr)
except subprocess.CalledProcessError as e:
    print(f"{Color.RED}Command failed with return code {e.returncode}:{Color.RESET}")
    print(e.stderr)
# objTorrentFile = open(torrent_file_name_no_qoute, "rb")
# decodedDict = bencoding.bdecode(objTorrentFile.read())
# info_hash = hashlib.sha1(bencoding.bencode(decodedDict[b"info"])).hexdigest()

print("Size of torrent: " + Color.GREEN + str(total_size_rounded) + "G" + Color.RESET)
print("MKtorrent peice size: " + Color.GREEN +  piece_size + " was used. Full command at beginning" + Color.RESET)
print(".torrent file location: " + Color.GREEN + torrent_file_name_no_qoute + Color.RESET)
# print("Info hash: " + Color.GREEN + info_hash + Color.RESET)
Post Reply