forked from premiere/premiere-libtorrent
Fix python client (#1512)
extend python test to include examples. add piece index and block index python bindings
This commit is contained in:
parent
4ddd4b72c6
commit
32e0028e32
|
@ -218,15 +218,12 @@ def main():
|
||||||
if options.max_download_rate <= 0:
|
if options.max_download_rate <= 0:
|
||||||
options.max_download_rate = -1
|
options.max_download_rate = -1
|
||||||
|
|
||||||
settings = lt.session_settings()
|
ses = lt.session({'user_agent': 'python_client/' + lt.__version__,
|
||||||
settings.user_agent = 'python_client/' + lt.version
|
'listen_interfaces':'0.0.0.0:' + str(options.port),
|
||||||
|
'download_rate_limit': int(options.max_download_rate),
|
||||||
ses = lt.session()
|
'upload_rate_limit': int(options.max_upload_rate),
|
||||||
ses.set_download_rate_limit(int(options.max_download_rate))
|
'alert_mask' : 0xfffffff
|
||||||
ses.set_upload_rate_limit(int(options.max_upload_rate))
|
})
|
||||||
ses.listen_on(options.port, options.port + 10)
|
|
||||||
ses.set_settings(settings)
|
|
||||||
ses.set_alert_mask(0xfffffff)
|
|
||||||
|
|
||||||
if options.proxy_host != '':
|
if options.proxy_host != '':
|
||||||
ps = lt.proxy_settings()
|
ps = lt.proxy_settings()
|
||||||
|
@ -281,14 +278,13 @@ def main():
|
||||||
out = ''
|
out = ''
|
||||||
|
|
||||||
for h in handles:
|
for h in handles:
|
||||||
if h.has_metadata():
|
s = h.status()
|
||||||
name = h.get_torrent_info().name()[:40]
|
if s.has_metadata:
|
||||||
|
name = h.torrent_file().name()[:40]
|
||||||
else:
|
else:
|
||||||
name = '-'
|
name = '-'
|
||||||
out += 'name: %-40s\n' % name
|
out += 'name: %-40s\n' % name
|
||||||
|
|
||||||
s = h.status()
|
|
||||||
|
|
||||||
if s.state != lt.torrent_status.seeding:
|
if s.state != lt.torrent_status.seeding:
|
||||||
state_str = ['queued', 'checking', 'downloading metadata',
|
state_str = ['queued', 'checking', 'downloading metadata',
|
||||||
'downloading', 'finished', 'seeding',
|
'downloading', 'finished', 'seeding',
|
||||||
|
@ -335,14 +331,7 @@ def main():
|
||||||
write_line(console, '(q)uit), (p)ause), (u)npause), (r)eannounce\n')
|
write_line(console, '(q)uit), (p)ause), (u)npause), (r)eannounce\n')
|
||||||
write_line(console, 76 * '-' + '\n')
|
write_line(console, 76 * '-' + '\n')
|
||||||
|
|
||||||
while 1:
|
alerts = ses.pop_alerts()
|
||||||
a = ses.pop_alert()
|
|
||||||
if not a:
|
|
||||||
break
|
|
||||||
alerts.append(a)
|
|
||||||
|
|
||||||
if len(alerts) > 8:
|
|
||||||
del alerts[:len(alerts) - 8]
|
|
||||||
|
|
||||||
for a in alerts:
|
for a in alerts:
|
||||||
if type(a) == str:
|
if type(a) == str:
|
||||||
|
@ -369,7 +358,7 @@ def main():
|
||||||
|
|
||||||
ses.pause()
|
ses.pause()
|
||||||
for h in handles:
|
for h in handles:
|
||||||
if not h.is_valid() or not h.has_metadata():
|
if not h.is_valid() or not s.has_metadata:
|
||||||
continue
|
continue
|
||||||
data = lt.bencode(h.write_resume_data())
|
data = lt.bencode(h.write_resume_data())
|
||||||
open(os.path.join(options.save_path, h.get_torrent_info().name() +
|
open(os.path.join(options.save_path, h.get_torrent_info().name() +
|
||||||
|
|
|
@ -20,6 +20,11 @@ fs = libtorrent.file_storage()
|
||||||
|
|
||||||
parent_input = os.path.split(input)[0]
|
parent_input = os.path.split(input)[0]
|
||||||
|
|
||||||
|
# if we have a single file, use it because os.walk does not work on a single files
|
||||||
|
if os.path.isfile(input):
|
||||||
|
size = os.path.getsize(input)
|
||||||
|
fs.add_file(input, size)
|
||||||
|
|
||||||
for root, dirs, files in os.walk(input):
|
for root, dirs, files in os.walk(input):
|
||||||
# skip directories starting with .
|
# skip directories starting with .
|
||||||
if os.path.split(root)[1][0] == '.':
|
if os.path.split(root)[1][0] == '.':
|
||||||
|
@ -48,8 +53,8 @@ t = libtorrent.create_torrent(fs, 0, 4 * 1024 * 1024)
|
||||||
t.add_tracker(sys.argv[2])
|
t.add_tracker(sys.argv[2])
|
||||||
t.set_creator('libtorrent %s' % libtorrent.__version__)
|
t.set_creator('libtorrent %s' % libtorrent.__version__)
|
||||||
|
|
||||||
libtorrent.set_piece_hashes(t, parent_input, lambda x: sys.stderr.write('.'))
|
libtorrent.set_piece_hashes(t, parent_input, lambda x: sys.stdout.write('.'))
|
||||||
sys.stderr.write('\n')
|
sys.stdout.write('\n')
|
||||||
|
|
||||||
f = open('out.torrent', 'wb+')
|
f = open('out.torrent', 'wb+')
|
||||||
f.write(libtorrent.bencode(t.generate()))
|
f.write(libtorrent.bencode(t.generate()))
|
||||||
|
|
|
@ -8,14 +8,14 @@ import libtorrent as lt
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
ses = lt.session()
|
ses = lt.session({'listen_interfaces':'0.0.0.0:6881'})
|
||||||
ses.listen_on(6881, 6891)
|
|
||||||
|
|
||||||
info = lt.torrent_info(sys.argv[1])
|
info = lt.torrent_info(sys.argv[1])
|
||||||
h = ses.add_torrent({'ti': info, 'save_path': '.'})
|
h = ses.add_torrent({'ti': info, 'save_path': '.'})
|
||||||
print('starting', h.name())
|
s = h.status()
|
||||||
|
print('starting', s.name)
|
||||||
|
|
||||||
while (not h.is_seed()):
|
while (not s.is_seeding):
|
||||||
s = h.status()
|
s = h.status()
|
||||||
|
|
||||||
state_str = [
|
state_str = [
|
||||||
|
|
|
@ -356,7 +356,7 @@ void bind_alert()
|
||||||
|
|
||||||
class_<hash_failed_alert, bases<torrent_alert>, noncopyable>(
|
class_<hash_failed_alert, bases<torrent_alert>, noncopyable>(
|
||||||
"hash_failed_alert", no_init)
|
"hash_failed_alert", no_init)
|
||||||
.def_readonly("piece_index", &hash_failed_alert::piece_index)
|
.add_property("piece_index", make_getter(&hash_failed_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<peer_ban_alert, bases<peer_alert>, noncopyable>(
|
class_<peer_ban_alert, bases<peer_alert>, noncopyable>(
|
||||||
|
@ -389,13 +389,13 @@ void bind_alert()
|
||||||
|
|
||||||
class_<piece_finished_alert, bases<torrent_alert>, noncopyable>(
|
class_<piece_finished_alert, bases<torrent_alert>, noncopyable>(
|
||||||
"piece_finished_alert", no_init)
|
"piece_finished_alert", no_init)
|
||||||
.def_readonly("piece_index", &piece_finished_alert::piece_index)
|
.add_property("piece_index", make_getter(&piece_finished_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<block_finished_alert, bases<peer_alert>, noncopyable>(
|
class_<block_finished_alert, bases<peer_alert>, noncopyable>(
|
||||||
"block_finished_alert", no_init)
|
"block_finished_alert", no_init)
|
||||||
.def_readonly("block_index", &block_finished_alert::block_index)
|
.add_property("block_index", make_getter(&block_finished_alert::block_index, by_value()))
|
||||||
.def_readonly("piece_index", &block_finished_alert::piece_index)
|
.add_property("piece_index", make_getter(&block_finished_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<block_downloading_alert, bases<peer_alert>, noncopyable>(
|
class_<block_downloading_alert, bases<peer_alert>, noncopyable>(
|
||||||
|
@ -403,8 +403,8 @@ void bind_alert()
|
||||||
#ifndef TORRENT_NO_DEPRECATE
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
.def_readonly("peer_speedmsg", &block_downloading_alert::peer_speedmsg)
|
.def_readonly("peer_speedmsg", &block_downloading_alert::peer_speedmsg)
|
||||||
#endif
|
#endif
|
||||||
.def_readonly("block_index", &block_downloading_alert::block_index)
|
.add_property("block_index", make_getter(&block_downloading_alert::block_index, by_value()))
|
||||||
.def_readonly("piece_index", &block_downloading_alert::piece_index)
|
.add_property("piece_index", make_getter(&block_downloading_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<storage_moved_alert, bases<torrent_alert>, noncopyable>(
|
class_<storage_moved_alert, bases<torrent_alert>, noncopyable>(
|
||||||
|
@ -693,20 +693,20 @@ void bind_alert()
|
||||||
|
|
||||||
class_<request_dropped_alert, bases<peer_alert>, noncopyable>(
|
class_<request_dropped_alert, bases<peer_alert>, noncopyable>(
|
||||||
"request_dropped_alert", no_init)
|
"request_dropped_alert", no_init)
|
||||||
.def_readonly("block_index", &request_dropped_alert::block_index)
|
.add_property("block_index", make_getter(&request_dropped_alert::block_index, by_value()))
|
||||||
.def_readonly("piece_index", &request_dropped_alert::piece_index)
|
.add_property("piece_index", make_getter(&request_dropped_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<block_timeout_alert, bases<peer_alert>, noncopyable>(
|
class_<block_timeout_alert, bases<peer_alert>, noncopyable>(
|
||||||
"block_timeout_alert", no_init)
|
"block_timeout_alert", no_init)
|
||||||
.def_readonly("block_index", &block_timeout_alert::block_index)
|
.add_property("block_index", make_getter(&block_timeout_alert::block_index, by_value()))
|
||||||
.def_readonly("piece_index", &block_timeout_alert::piece_index)
|
.add_property("piece_index", make_getter(&block_timeout_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<unwanted_block_alert, bases<peer_alert>, noncopyable>(
|
class_<unwanted_block_alert, bases<peer_alert>, noncopyable>(
|
||||||
"unwanted_block_alert", no_init)
|
"unwanted_block_alert", no_init)
|
||||||
.def_readonly("block_index", &unwanted_block_alert::block_index)
|
.add_property("block_index", make_getter(&unwanted_block_alert::block_index, by_value()))
|
||||||
.def_readonly("piece_index", &unwanted_block_alert::piece_index)
|
.add_property("piece_index", make_getter(&unwanted_block_alert::piece_index, by_value()))
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<torrent_delete_failed_alert, bases<torrent_alert>, noncopyable>(
|
class_<torrent_delete_failed_alert, bases<torrent_alert>, noncopyable>(
|
||||||
|
|
|
@ -47,6 +47,7 @@ list get_pieces(peer_info const& pi)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using by_value = return_value_policy<return_by_value>;
|
||||||
void bind_peer_info()
|
void bind_peer_info()
|
||||||
{
|
{
|
||||||
scope pi = class_<peer_info>("peer_info")
|
scope pi = class_<peer_info>("peer_info")
|
||||||
|
@ -82,8 +83,8 @@ void bind_peer_info()
|
||||||
.def_readonly("download_queue_length", &peer_info::download_queue_length)
|
.def_readonly("download_queue_length", &peer_info::download_queue_length)
|
||||||
.def_readonly("upload_queue_length", &peer_info::upload_queue_length)
|
.def_readonly("upload_queue_length", &peer_info::upload_queue_length)
|
||||||
.def_readonly("failcount", &peer_info::failcount)
|
.def_readonly("failcount", &peer_info::failcount)
|
||||||
.def_readonly("downloading_piece_index", &peer_info::downloading_piece_index)
|
.add_property("downloading_piece_index", make_getter(&peer_info::downloading_piece_index, by_value()))
|
||||||
.def_readonly("downloading_block_index", &peer_info::downloading_block_index)
|
.add_property("downloading_block_index", make_getter(&peer_info::downloading_block_index, by_value()))
|
||||||
.def_readonly("downloading_progress", &peer_info::downloading_progress)
|
.def_readonly("downloading_progress", &peer_info::downloading_progress)
|
||||||
.def_readonly("downloading_total", &peer_info::downloading_total)
|
.def_readonly("downloading_total", &peer_info::downloading_total)
|
||||||
.def_readonly("client", &peer_info::client)
|
.def_readonly("client", &peer_info::client)
|
||||||
|
|
|
@ -341,6 +341,7 @@ void add_piece(torrent_handle& th, piece_index_t piece, char const *data, int fl
|
||||||
th.add_piece(piece, data, flags);
|
th.add_piece(piece, data, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using by_value = return_value_policy<return_by_value>;
|
||||||
void bind_torrent_handle()
|
void bind_torrent_handle()
|
||||||
{
|
{
|
||||||
// arguments are: number of seconds and tracker index
|
// arguments are: number of seconds and tracker index
|
||||||
|
@ -473,7 +474,7 @@ void bind_torrent_handle()
|
||||||
;
|
;
|
||||||
|
|
||||||
class_<pool_file_status>("pool_file_status")
|
class_<pool_file_status>("pool_file_status")
|
||||||
.def_readonly("file_index", &pool_file_status::file_index)
|
.add_property("file_index", make_getter((&pool_file_status::file_index), by_value()))
|
||||||
.def_readonly("last_use", &pool_file_status::last_use)
|
.def_readonly("last_use", &pool_file_status::last_use)
|
||||||
.def_readonly("open_mode", &pool_file_status::open_mode)
|
.def_readonly("open_mode", &pool_file_status::open_mode)
|
||||||
;
|
;
|
||||||
|
|
|
@ -208,6 +208,7 @@ std::shared_ptr<torrent_info> bencoded_constructor1(entry const& ent)
|
||||||
return bencoded_constructor0(ent, 0);
|
return bencoded_constructor0(ent, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using by_value = return_value_policy<return_by_value>;
|
||||||
void bind_torrent_info()
|
void bind_torrent_info()
|
||||||
{
|
{
|
||||||
return_value_policy<copy_const_reference> copy;
|
return_value_policy<copy_const_reference> copy;
|
||||||
|
@ -218,7 +219,7 @@ void bind_torrent_info()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class_<file_slice>("file_slice")
|
class_<file_slice>("file_slice")
|
||||||
.def_readwrite("file_index", &file_slice::file_index)
|
.add_property("file_index", make_getter((&file_slice::file_index), by_value()))
|
||||||
.def_readwrite("offset", &file_slice::offset)
|
.def_readwrite("offset", &file_slice::offset)
|
||||||
.def_readwrite("size", &file_slice::size)
|
.def_readwrite("size", &file_slice::size)
|
||||||
;
|
;
|
||||||
|
|
|
@ -8,6 +8,13 @@ import time
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import binascii
|
import binascii
|
||||||
|
import subprocess as sub
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# include terminal interface for travis parallel executions of scripts which use
|
||||||
|
# terminal features: fix multiple stdin assignment at termios.tcgetattr
|
||||||
|
if os.name != 'nt':
|
||||||
|
import pty
|
||||||
|
|
||||||
|
|
||||||
class test_create_torrent(unittest.TestCase):
|
class test_create_torrent(unittest.TestCase):
|
||||||
|
@ -263,6 +270,109 @@ class test_sha1hash(unittest.TestCase):
|
||||||
|
|
||||||
class test_session(unittest.TestCase):
|
class test_session(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_post_session_stats(self):
|
||||||
|
s = lt.session({'alert_mask': lt.alert.category_t.stats_notification, 'enable_dht': False})
|
||||||
|
s.post_session_stats()
|
||||||
|
a = s.wait_for_alert(1000)
|
||||||
|
self.assertTrue(isinstance(a, lt.session_stats_alert))
|
||||||
|
self.assertTrue(isinstance(a.values, dict))
|
||||||
|
self.assertTrue(len(a.values) > 0)
|
||||||
|
|
||||||
|
def test_add_torrent(self):
|
||||||
|
s = lt.session({'alert_mask': lt.alert.category_t.stats_notification, 'enable_dht': False})
|
||||||
|
h = s.add_torrent({'ti': lt.torrent_info('base.torrent'),
|
||||||
|
'save_path': '.',
|
||||||
|
'dht_nodes': [('1.2.3.4', 6881), ('4.3.2.1', 6881)],
|
||||||
|
'http_seeds': ['http://test.com/seed'],
|
||||||
|
'peers': [('5.6.7.8', 6881)],
|
||||||
|
'banned_peers': [('8.7.6.5', 6881)],
|
||||||
|
'file_priorities': [1,1,1,2,0]})
|
||||||
|
|
||||||
|
def test_unknown_settings(self):
|
||||||
|
try:
|
||||||
|
s = lt.session({'unexpected-key-name': 42})
|
||||||
|
self.assertFalse('should have thrown an exception')
|
||||||
|
except KeyError as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def test_apply_settings(self):
|
||||||
|
|
||||||
|
s = lt.session({'enable_dht': False})
|
||||||
|
s.apply_settings({'num_want': 66, 'user_agent': 'test123'})
|
||||||
|
self.assertEqual(s.get_settings()['num_want'], 66)
|
||||||
|
self.assertEqual(s.get_settings()['user_agent'], 'test123')
|
||||||
|
|
||||||
|
class test_example_client(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_execute_client(self):
|
||||||
|
if os.name == 'nt':
|
||||||
|
# TODO: fix windows includes of client.py
|
||||||
|
return
|
||||||
|
my_stdin = sys.stdin
|
||||||
|
if os.name != 'nt':
|
||||||
|
master_fd, slave_fd = pty.openpty()
|
||||||
|
# slave_fd fix multiple stdin assignment at termios.tcgetattr
|
||||||
|
my_stdin = slave_fd
|
||||||
|
|
||||||
|
process = sub.Popen(
|
||||||
|
[sys.executable,"client.py","url_seed_multi.torrent"],
|
||||||
|
stdin=my_stdin, stdout=sub.PIPE, stderr=sub.PIPE)
|
||||||
|
# python2 has no Popen.wait() timeout
|
||||||
|
time.sleep(5)
|
||||||
|
returncode = process.poll()
|
||||||
|
if returncode == None:
|
||||||
|
# this is an expected use-case
|
||||||
|
process.kill()
|
||||||
|
err = process.stderr.read().decode("utf-8")
|
||||||
|
self.assertEqual('', err, 'process throw errors: \n' + err)
|
||||||
|
# check error code if process did unexpected end
|
||||||
|
if returncode != None:
|
||||||
|
# in case of error return: output stdout if nothing was on stderr
|
||||||
|
if returncode != 0:
|
||||||
|
print("stdout:\n" + process.stdout.read().decode("utf-8"))
|
||||||
|
self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
|
||||||
|
+ "stderr: empty\n"
|
||||||
|
+ "some configuration does not output errors like missing module members,"
|
||||||
|
+ "try to call it manually to get the error message\n")
|
||||||
|
|
||||||
|
def test_execute_simple_client(self):
|
||||||
|
process = sub.Popen(
|
||||||
|
[sys.executable,"simple_client.py","url_seed_multi.torrent"],
|
||||||
|
stdout=sub.PIPE, stderr=sub.PIPE)
|
||||||
|
# python2 has no Popen.wait() timeout
|
||||||
|
time.sleep(5)
|
||||||
|
returncode = process.poll()
|
||||||
|
if returncode == None:
|
||||||
|
# this is an expected use-case
|
||||||
|
process.kill()
|
||||||
|
err = process.stderr.read().decode("utf-8")
|
||||||
|
self.assertEqual('', err, 'process throw errors: \n' + err)
|
||||||
|
# check error code if process did unexpected end
|
||||||
|
if returncode != None:
|
||||||
|
# in case of error return: output stdout if nothing was on stderr
|
||||||
|
if returncode != 0:
|
||||||
|
print("stdout:\n" + process.stdout.read().decode("utf-8"))
|
||||||
|
self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
|
||||||
|
+ "stderr: empty\n"
|
||||||
|
+ "some configuration does not output errors like missing module members,"
|
||||||
|
+ "try to call it manually to get the error message\n")
|
||||||
|
|
||||||
|
def test_execute_make_torrent(self):
|
||||||
|
process = sub.Popen(
|
||||||
|
[sys.executable,"make_torrent.py","url_seed_multi.torrent",
|
||||||
|
"http://test.com/test"], stdout=sub.PIPE, stderr=sub.PIPE)
|
||||||
|
returncode = process.wait()
|
||||||
|
# python2 has no Popen.wait() timeout
|
||||||
|
err = process.stderr.read().decode("utf-8")
|
||||||
|
self.assertEqual('', err, 'process throw errors: \n' + err)
|
||||||
|
# in case of error return: output stdout if nothing was on stderr
|
||||||
|
if returncode != 0:
|
||||||
|
print("stdout:\n" + process.stdout.read().decode("utf-8"))
|
||||||
|
self.assertEqual(returncode, 0, "returncode: " + str(returncode) + "\n"
|
||||||
|
+ "stderr: empty\n"
|
||||||
|
+ "some configuration does not output errors like missing module members,"
|
||||||
|
+ "try to call it manually to get the error message\n")
|
||||||
|
|
||||||
def test_post_session_stats(self):
|
def test_post_session_stats(self):
|
||||||
s = lt.session({'alert_mask': lt.alert.category_t.stats_notification,
|
s = lt.session({'alert_mask': lt.alert.category_t.stats_notification,
|
||||||
'enable_dht': False})
|
'enable_dht': False})
|
||||||
|
|
Loading…
Reference in New Issue