Skip to content

Instantly share code, notes, and snippets.

@johncf
Created March 28, 2018 19:24
Show Gist options
  • Save johncf/f1606e33562b51f67aa53ffdddf2183c to your computer and use it in GitHub Desktop.
Save johncf/f1606e33562b51f67aa53ffdddf2183c to your computer and use it in GitHub Desktop.
Torrent: download specific pieces with libtorrent
#!/bin/env python3
import libtorrent as lt
with open("/path/to/file.torrent", "rb") as f:
e = lt.bdecode(f.read())
info = lt.torrent_info(e)
print(info.num_pieces(), 'pieces')
files = info.files()
def humanize(size_bytes):
KB = 1 << 10
MB = 1 << 20
GB = 1 << 30
if size_bytes < KB:
return '{} B'.format(size_bytes)
elif size_bytes < MB:
return '{:.1f} KiB'.format(size_bytes/KB)
elif size_bytes < GB:
return '{:.1f} MiB'.format(size_bytes/MB)
else:
return '{:.1f} GiB'.format(size_bytes/GB)
for i, f in enumerate(info.files()):
print(i, ':', f.path)
print(' size: {}, first piece: {}'.format(humanize(f.size), info.map_file(i, 0, 1).piece))
#!/bin/env python3
# TIP: Instead of executing this script from shell, do the following from an
# interactive python session (python shell):
# >>> exec(open("./torrent-dl.py").read())
# after which do `h.resume()` to resume. Use `graceful_exit()` to quit properly.
# Modify the list `pp` as needed and do `h.prioritize_pieces(pp)`.
# TIP: Use https://stackoverflow.com/a/26102379/2849934 to generate a torrent file
# from a magnet link.
import libtorrent as lt
import os.path
import signal
import sys
import time
import __main__
torr_path = '/path/to/file.torrent'
save_path = os.path.dirname(torr_path)
resume_path = os.path.splitext(torr_path)[0] + '.fastresume'
ses = lt.session()
ses.listen_on(7881, 7891)
with open(torr_path, "rb") as f:
e = lt.bdecode(f.read())
info = lt.torrent_info(e)
params = { 'save_path': save_path,
'storage_mode': lt.storage_mode_t.storage_mode_sparse,
'ti': info,
'flags': lt.add_torrent_params_flags_t.flag_paused,
}
try:
with open(resume_path, 'rb') as f:
params['resume_data'] = f.read()
except:
pass
h = ses.add_torrent(params)
pp = [0]*info.num_pieces()
beg, end = 0, info.num_pieces()
# first and last pieces
p1 = (end - beg)//100
pp[:p1] = [7]*p1
pp[-p1:] = [7]*p1
sz = (end - beg)//20
# more pieces
mid = beg + (end - beg)//2
pp[mid:mid+sz] = [6]*sz
h.prioritize_pieces(pp)
tqt = beg + 3*(end - beg)//4
pp[tqt:tqt+2*sz] = [5]*(2*sz)
pp[tqt-2*sz:tqt] = [5]*(2*sz)
h.prioritize_pieces(pp)
def check_alerts():
alerts = ses.pop_alerts()
for a in alerts:
print('alert:', a)
def graceful_exit():
ses.pause()
check_alerts()
print("saving resume data...")
if h.is_valid():
h.save_resume_data()
while True:
ses.wait_for_alert(3000)
a = ses.pop_alert()
if a is None:
print('alert wait timed out')
break
print('alert:', a)
if type(a) == lt.save_resume_data_alert:
data = lt.bencode(a.resume_data)
with open(resume_path, 'wb') as f:
f.write(data)
break
elif type(a) == lt.save_resume_data_failed_alert:
print('failed to save resume data')
break
sys.exit(0)
def print_status():
check_alerts()
s = h.status()
print('{:.1f}% complete (down: {:.1f} kB/s up: {:.1f} kB/s peers: {}) {}'.format(
s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000,
s.num_peers, s.state))
if hasattr(__main__, '__file__'): # if not in an interactive session
def signal_handler(_sig, _frm):
graceful_exit()
signal.signal(signal.SIGINT, signal_handler)
h.resume()
while True:
print_status()
time.sleep(1)
@johncf
Copy link
Author

johncf commented Jul 8, 2022

@Msameim181

Do you know how I should config the pp to download a specific file with known size and first piece ?

pp (piece-priority) is the list of priorities for every piece in the torrent. If you know which pieces a file belongs in, simply set high priority for those pieces. That is, you need to know the first piece and the number of pieces in the file, which can be calculated with file size divided by the size of a piece.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment