BitTorrent utilities for querying trackers and peers
Project description
torrentlib
Python helpers for BitTorrent tracker queries, peer communication, and metadata inspection.
torrentlib is not a full BitTorrent client. It can:
- load
.torrentmetadata - announce to HTTP and UDP trackers
- collect IPv4 and IPv6 peer lists
- connect to peers that support the extension protocol
- download torrent metadata from peers via
ut_metadata - process peer exchange (
ut_pex) messages
It does not manage piece downloads/uploads for complete content transfer.
Installation
pip install torrentlib
Supports Python 3.10 through 3.14.
Public API
from torrentlib import Torrent, TorrentStatus, Peer
from torrentlib.Tracker import Check, Query
Working With Torrents
Create a Torrent from a .torrent file when you already have metadata:
from torrentlib import Torrent
torrent = Torrent.from_file("example.torrent")
print(torrent)
print(torrent.name)
print(torrent.info_hash)
print(torrent.total_size)
print(torrent.piece_length)
print(torrent.num_pieces)
files = torrent.get_files_info()
if files:
for file_hash, file_info in files.items():
print(file_hash, file_info["name"], file_info["length"])
Create a minimal Torrent when you only have an info hash, for example from a magnet link:
from torrentlib import Torrent, TorrentStatus
torrent = Torrent(
info_hash="1234567890abcdef1234567890abcdef12345678"
)
torrent = Torrent(
info_hash="1234567890abcdef1234567890abcdef12345678",
total_size=1145141919810,
left=1145141919810,
downloaded=0,
uploaded=0,
event=TorrentStatus.STOPPED,
name="example_file.iso",
piece_length=None,
num_pieces=None,
)
Tracker Health Checks
Check provides simple tracker reachability checks. Use Check.auto() when you want protocol detection from the URL scheme.
from torrentlib.Tracker import Check
print(Check.auto("http://tracker.example.com:8080/announce", timeout=5))
print(Check.auto("udp://tracker.example.com:6969/announce", timeout=5))
print(Check.http("http://tracker.example.com:8080/announce", timeout=5))
print(Check.udp("udp://tracker.example.com:6969/announce", timeout=5))
trackers = [
"http://tracker1.example.com:8080/announce",
"udp://tracker2.example.com:6969/announce",
]
results = Check.multiple(trackers, timeout=5)
for url, is_online in results.items():
print(url, is_online)
Tracker Queries
Tracker queries operate on a Torrent object, not a raw info_hash. The library reads torrent.left, torrent.downloaded, torrent.uploaded, and torrent.event from that object.
Query.single() chooses HTTP or UDP based on the tracker URL:
from torrentlib import Torrent, TorrentStatus
from torrentlib.Tracker import Query
torrent = Torrent(
info_hash="1234567890abcdef1234567890abcdef12345678",
event=TorrentStatus.STARTED,
)
peer_id = "-robots-testing12345"
response = Query.single(
torrent=torrent,
url="udp://tracker.opentrackr.org:1337/announce",
peer_id=peer_id,
port=6881,
timeout=10,
)
print(response["interval"])
print(response.get("seeders"))
print(response.get("leechers"))
print(len(response.get("peers", [])))
print(len(response.get("peers6", [])))
You can also query multiple trackers concurrently:
from torrentlib.Tracker import Query
responses = Query.multi(
torrent=torrent,
urls=[
"udp://tracker.opentrackr.org:1337/announce",
"http://tracker.example.com:8080/announce",
],
peer_id=peer_id,
port=6881,
timeout=10,
)
for url, result in responses.items():
if "error" in result:
print(url, result["error"])
else:
print(url, len(result.get("peers", [])))
Successful tracker queries automatically merge returned peers into:
torrent.peersfor IPv4 peerstorrent.peers6for IPv6 peers
Peer Communication
Use Peer as a context manager. Connecting performs the BitTorrent handshake. If the remote peer supports extensions, the library also sends an extension handshake and reads the initial extension messages.
from time import sleep
from torrentlib import Peer, Torrent
torrent = Torrent(info_hash="1234567890abcdef1234567890abcdef12345678")
peer_id = "-robots-testing12345"
peer_addr = ("127.0.0.1", 6881)
with Peer(peer_addr, torrent, peer_id) as peer:
print(peer)
print(peer.peer_supports_extensions)
print(peer.peer_extension_ids)
sleep(2)
peer.read_all()
print(len(torrent.peers))
print(len(torrent.peers6))
To keep long-lived connections open, call send_keep_alive() periodically yourself:
import threading
from time import sleep
from torrentlib import Peer, Torrent
from torrentlib.Peer.PeerCommunicationException import SocketClosedException
def keep_alive_loop(peer: Peer, stop_event: threading.Event, interval: int = 120):
while not stop_event.is_set():
sleep(interval)
try:
peer.send_keep_alive()
except SocketClosedException:
break
torrent = Torrent(info_hash="1234567890abcdef1234567890abcdef12345678")
peer_id = "-robots-testing12345"
peer_addr = ("127.0.0.1", 6881)
stop_event = threading.Event()
try:
with Peer(peer_addr, torrent, peer_id) as peer:
thread = threading.Thread(
target=keep_alive_loop,
args=(peer, stop_event),
daemon=True,
)
thread.start()
peer.read_all()
finally:
stop_event.set()
Metadata Download
If you start with only an info hash, you can fetch metadata from a peer that supports ut_metadata. Once the full metadata is assembled and verified, the library updates the existing Torrent object in place.
from torrentlib import Peer, Torrent
torrent = Torrent(info_hash="1234567890abcdef1234567890abcdef12345678")
peer_id = "-robots-testing12345"
peer_addr = ("127.0.0.1", 6881)
with Peer(peer_addr, torrent, peer_id) as peer:
if peer.metadata_size is not None:
peer.request_all_metadata()
peer.read_all()
if torrent.metadata is not None:
print("Metadata downloaded")
print(torrent.name)
print(torrent.total_size)
files = torrent.get_files_info()
if files:
for file_hash, file_info in files.items():
print(file_hash, file_info["name"], file_info["length"])
Magnet To Metadata Example
from torrentlib import Peer, Torrent, TorrentStatus
from torrentlib.Tracker import Query
info_hash = "1234567890abcdef1234567890abcdef12345678"
tracker_url = "udp://tracker.opentrackr.org:1337/announce"
peer_id = "-robots-testing12345"
torrent = Torrent(info_hash=info_hash, event=TorrentStatus.STARTED)
response = Query.single(
torrent=torrent,
url=tracker_url,
peer_id=peer_id,
port=6881,
)
for peer_addr in response.get("peers", [])[:5]:
try:
with Peer(peer_addr, torrent, peer_id) as peer:
if peer.metadata_size is None:
continue
peer.request_all_metadata()
peer.read_all()
if torrent.metadata is not None:
print(torrent)
files = torrent.get_files_info()
if files:
for _, file_info in files.items():
print(file_info["name"], file_info["length"])
break
except Exception:
continue
Error Handling
Tracker exceptions live in torrentlib.Tracker.TrackerQueryException:
from torrentlib import Torrent, TorrentStatus
from torrentlib.Tracker import Query
from torrentlib.Tracker.TrackerQueryException import (
TrackerQueryException,
TimeoutError,
BadRequestError,
InvalidResponseError,
UnexpectedError,
)
torrent = Torrent(
info_hash="1234567890abcdef1234567890abcdef12345678",
event=TorrentStatus.STARTED,
)
try:
Query.single(
torrent=torrent,
url="http://tracker.example.com/announce",
peer_id="-robots-testing12345",
)
except TimeoutError:
print("Tracker request timed out")
except BadRequestError:
print("Tracker rejected the request")
except InvalidResponseError:
print("Tracker returned malformed data")
except UnexpectedError as exc:
print(f"Unexpected tracker error: {exc}")
except TrackerQueryException as exc:
print(f"Tracker error: {exc}")
Peer communication exceptions live in torrentlib.Peer.PeerCommunicationException:
from torrentlib.Peer.PeerCommunicationException import (
PeerCommunicationException,
SocketClosedException,
InvalidResponseException,
)
Notes
TorrentStatuscurrently providesCOMPLETED,STARTED, andSTOPPED.Torrent.get_files_info()returnsNoneuntil metadata is available.Torrent.update_from_metadata()verifies that received metadata matches the original info hash.Query.single()andQuery.multi()update theTorrentpeer caches automatically.
License
MIT. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file torrentlib-1.0.3.tar.gz.
File metadata
- Download URL: torrentlib-1.0.3.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8216b2812027b2cbee6471bde07ec2942344594f7c20b1be0a3da024202ecc50
|
|
| MD5 |
5b11cd9a5caf2d48f7c5032ef408dc89
|
|
| BLAKE2b-256 |
e75aedaee5bda44c5e1011559408455b0bd743a3bfba4127696d59890937882b
|
File details
Details for the file torrentlib-1.0.3-py3-none-any.whl.
File metadata
- Download URL: torrentlib-1.0.3-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a151d3643b077605347133cd4d5d33b6ce5118b356f9901d9ddf2f5dcc2c804e
|
|
| MD5 |
c91011896e117d483d0f852fb351984e
|
|
| BLAKE2b-256 |
db9e83c7a22dc9151eed2081c216525cb8e7a4afc667fbcd25abc419e0acb40a
|