diff --git a/bindings/python/Jamfile b/bindings/python/Jamfile new file mode 100755 index 000000000..bad5ed2f5 --- /dev/null +++ b/bindings/python/Jamfile @@ -0,0 +1,27 @@ +import python ; + +python-extension libtorrent + : src/module.cpp + src/big_number.cpp + src/fingerprint.cpp + src/utility.cpp + src/session.cpp + src/entry.cpp + src/torrent_info.cpp + src/filesystem.cpp + src/torrent_handle.cpp + src/torrent_status.cpp + src/session_settings.cpp + src/version.cpp + src/alert.cpp + src/datetime.cpp + src/extensions.cpp + src/peer_plugin.cpp + src/docstrings.cpp + src/torrent.cpp + src/peer_info.cpp + /torrent//torrent + /boost/python//boost_python + : src + ; + diff --git a/bindings/python/client.py b/bindings/python/client.py new file mode 100755 index 000000000..51ff110ce --- /dev/null +++ b/bindings/python/client.py @@ -0,0 +1,277 @@ +#!/usr/bin/python + +# Copyright Daniel Wallin 2006. Use, modification and distribution is +# subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +import sys +import libtorrent as lt +import time +import Console +import os.path + +class PythonExtension(lt.torrent_plugin): + def __init__(self, alerts): + lt.torrent_plugin.__init__(self) + self.alerts = alerts + self.alerts.append('PythonExtension') + self.count = 0 + + def on_piece_pass(self, index): + self.alerts.append('got piece %d' % index) + + def on_piece_failed(self, index): + self.alerts.append('failed piece %d' % index) + + def tick(self): + self.count += 1 + if self.count >= 10: + self.count = 0 + self.alerts.append('PythonExtension tick') + +def write_line(console, line): + console.write(line) + +def add_suffix(val): + prefix = ['B', 'kB', 'MB', 'GB', 'TB'] + for i in range(len(prefix)): + if abs(val) < 1000: + if i == 0: + return '%5.3g%s' % (val, prefix[i]) + else: + return '%4.3g%s' % (val, prefix[i]) + val /= 1000 + + return '%6.3gPB' % val + +def progress_bar(progress, width): + progress_chars = int(progress * width + 0.5) + return progress_chars * '#' + (width - progress_chars) * '-' + +def print_peer_info(console, peers): + + out = ' down (total ) up (total ) q r flags block progress client\n' + + for p in peers: + + out += '%s/s ' % add_suffix(p.down_speed) + out += '(%s) ' % add_suffix(p.total_download) + out += '%s/s ' % add_suffix(p.up_speed) + out += '(%s) ' % add_suffix(p.total_upload) + out += '%2d ' % p.download_queue_length + out += '%2d ' % p.upload_queue_length + + if p.flags & lt.peer_info.interesting: out += 'I' + else: out += '.' + if p.flags & lt.peer_info.choked: out += 'C' + else: out += '.' + if p.flags & lt.peer_info.remote_interested: out += 'i' + else: out += '.' + if p.flags & lt.peer_info.remote_choked: out += 'c' + else: out += '.' + if p.flags & lt.peer_info.supports_extensions: out += 'e' + else: out += '.' + if p.flags & lt.peer_info.local_connection: out += 'l' + else: out += 'r' + out += ' ' + + if p.downloading_piece_index >= 0: + out += progress_bar(float(p.downloading_progress) / p.downloading_total, 15) + else: + out += progress_bar(0, 15) + out += ' ' + + if p.flags & lt.peer_info.handshake: + id = 'waiting for handshake' + elif p.flags & lt.peer_info.connecting: + id = 'connecting to peer' + elif p.flags & lt.peer_info.queued: + id = 'queued' + else: + id = p.client + + out += '%s\n' % id[:10] + + write_line(console, out) + +def main(): + from optparse import OptionParser + + parser = OptionParser() + + parser.add_option('-p', '--port', + type='int', help='set listening port') + + parser.add_option('-r', '--ratio', + type='float', help='set the preferred upload/download ratio. 0 means infinite. Values smaller than 1 are clamped to 1') + + parser.add_option('-d', '--max-download-rate', + type='float', help='the maximum download rate given in kB/s. 0 means infinite.') + + parser.add_option('-u', '--max-upload-rate', + type='float', help='the maximum upload rate given in kB/s. 0 means infinite.') + + parser.add_option('-s', '--save-path', + type='string', help='the path where the downloaded file/folder should be placed.') + + parser.add_option('-a', '--allocation-mode', + type='string', help='sets mode used for allocating the downloaded files on disk. Possible options are [full | compact]') + + parser.set_defaults( + port=6881 + , ratio=0 + , max_download_rate=0 + , max_upload_rate=0 + , save_path='./' + , allocation_mode='compact' + ) + + (options, args) = parser.parse_args() + + if options.port < 0 or options.port > 65525: + options.port = 6881 + + options.max_upload_rate *= 1000 + options.max_download_rate *= 1000 + + if options.max_upload_rate <= 0: + options.max_upload_rate = -1 + if options.max_download_rate <= 0: + options.max_download_rate = -1 + + compact_allocation = options.allocation_mode == 'compact' + + settings = lt.session_settings() + settings.user_agent = 'python_client/' + lt.version + + ses = lt.session() + ses.set_download_rate_limit(options.max_download_rate) + ses.set_upload_rate_limit(options.max_upload_rate) + ses.listen_on(options.port, options.port + 10) + ses.set_settings(settings) + ses.set_severity_level(lt.alert.severity_levels.info) + + handles = [] + alerts = [] + + # Extensions + # ses.add_extension(lambda x: PythonExtension(alerts)) + + for f in args: + e = lt.bdecode(open(f, 'rb').read()) + info = lt.torrent_info(e) + print 'Adding \'%s\'...' % info.name() + + try: + resume_data = lt.bdecode(open( + os.path.join(options.save_path, info.name() + '.fastresume'), 'rb').read()) + except: + resume_data = None + + h = ses.add_torrent(info, options.save_path, + resume_data=resume_data, compact_mode=compact_allocation) + + handles.append(h) + + h.set_max_connections(60) + h.set_max_uploads(-1) + h.set_ratio(options.ratio) + h.set_sequenced_download_threshold(15) + + import msvcrt + + console = Console.getconsole() + + alive = True + while alive: + console.page() + + out = '' + + for h in handles: + if h.has_metadata(): + name = h.torrent_info().name()[:40] + else: + name = '-' + out += 'name: %-40s\n' % name + + s = h.status() + + if s.state != lt.torrent_status.seeding: + state_str = ['queued', 'checking', 'connecting', 'downloading metadata', \ + 'downloading', 'finished', 'seeding', 'allocating'] + out += state_str[s.state] + ' ' + + out += '%5.4f%% ' % (s.progress*100) + out += progress_bar(s.progress, 49) + out += '\n' + + out += 'total downloaded: %d Bytes\n' % s.total_done + out += 'peers: %d seeds: %d distributed copies: %d\n' % \ + (s.num_peers, s.num_seeds, s.distributed_copies) + out += '\n' + + out += 'download: %s/s (%s) ' \ + % (add_suffix(s.download_rate), add_suffix(s.total_download)) + out += 'upload: %s/s (%s) ' \ + % (add_suffix(s.upload_rate), add_suffix(s.total_upload)) + out += 'ratio: %s\n' % '0' + + if s.state != lt.torrent_status.seeding: + out += 'info-hash: %s\n' % h.info_hash() + out += 'next announce: %s\n' % s.next_announce + out += 'tracker: %s\n' % s.current_tracker + + write_line(console, out) + + peers = h.get_peer_info() + print_peer_info(console, peers) + + if True and s.state != lt.torrent_status.seeding: + out = '\n' + fp = h.file_progress() + ti = h.torrent_info() + for f,p in zip(ti.files(), fp): + out += progress_bar(p, 20) + out += ' ' + f.path + '\n' + write_line(console, out) + + write_line(console, 76 * '-' + '\n') + write_line(console, '(q)uit), (p)ause), (u)npause), (r)eannounce\n') + write_line(console, 76 * '-' + '\n') + + while 1: + a = ses.pop_alert() + if not a: break + alerts.append(a) + + if len(alerts) > 8: + del alerts[:len(alerts) - 8] + + for a in alerts: + if type(a) == str: + write_line(console, a + '\n') + else: + write_line(console, a.msg() + '\n') + + time.sleep(0.5) + if msvcrt.kbhit(): + c = msvcrt.getch() + if c == 'r': + for h in handles: h.force_reannounce() + elif c == 'q': + alive = False + elif c == 'p': + for h in handles: h.pause() + elif c == 'u': + for h in handles: h.resume() + + for h in handles: + if not h.is_valid() or not h.has_metadata(): + continue + h.pause() + data = lt.bencode(h.write_resume_data()) + open(os.path.join(options.save_path, h.torrent_info().name() + '.fastresume'), 'wb').write(data) + +main() + diff --git a/bindings/python/src/alert.cpp b/bindings/python/src/alert.cpp new file mode 100755 index 000000000..7a0783050 --- /dev/null +++ b/bindings/python/src/alert.cpp @@ -0,0 +1,162 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +extern char const* alert_doc; +extern char const* alert_msg_doc; +extern char const* alert_severity_doc; +extern char const* listen_failed_alert_doc; +extern char const* file_error_alert_doc; +extern char const* tracker_announce_alert_doc; +extern char const* tracker_alert_doc; +extern char const* tracker_reply_alert_doc; +extern char const* tracker_warning_alert_doc; +extern char const* url_seed_alert_doc; +extern char const* hash_failed_alert_doc; +extern char const* peer_ban_alert_doc; +extern char const* peer_error_alert_doc; +extern char const* invalid_request_alert_doc; +extern char const* peer_request_doc; +extern char const* torrent_finished_alert_doc; +extern char const* metadata_failed_alert_doc; +extern char const* metadata_received_alert_doc; +extern char const* fastresume_rejected_alert_doc; + +void bind_alert() +{ + using boost::noncopyable; + + { + scope alert_scope = class_("alert", alert_doc, no_init) + .def( + "msg", &alert::msg, return_value_policy() + , alert_msg_doc + ) + .def("severity", &alert::severity, alert_severity_doc) + .def( + "__str__", &alert::msg, return_value_policy() + , alert_msg_doc + ) + ; + + enum_("severity_levels") + .value("debug", alert::severity_t::debug) + .value("info", alert::severity_t::info) + .value("warning", alert::severity_t::warning) + .value("critical", alert::severity_t::critical) + .value("fatal", alert::severity_t::fatal) + .value("none", alert::severity_t::none) + ; + } + + class_, noncopyable>( + "listen_failed_alert", listen_failed_alert_doc, no_init + ); + + class_, noncopyable>( + "file_error_alert", file_error_alert_doc, no_init + ) + .def_readonly("handle", &file_error_alert::handle) + ; + + class_, noncopyable>( + "tracker_announce_alert", tracker_announce_alert_doc, no_init + ) + .def_readonly("handle", &tracker_announce_alert::handle) + ; + + class_, noncopyable>( + "tracker_alert", tracker_alert_doc, no_init + ) + .def_readonly("handle", &tracker_alert::handle) + .def_readonly("times_in_row", &tracker_alert::times_in_row) + .def_readonly("status_code", &tracker_alert::status_code) + ; + + class_, noncopyable>( + "tracker_reply_alert", tracker_reply_alert_doc, no_init + ) + .def_readonly("handle", &tracker_reply_alert::handle) + ; + + class_, noncopyable>( + "tracker_warning_alert", tracker_warning_alert_doc, no_init + ) + .def_readonly("handle", &tracker_warning_alert::handle) + ; + + class_, noncopyable>( + "url_seed_alert", url_seed_alert_doc, no_init + ) + .def_readonly("url", &url_seed_alert::url) + ; + + class_, noncopyable>( + "hash_failed_alert", hash_failed_alert_doc, no_init + ) + .def_readonly("handle", &hash_failed_alert::handle) + .def_readonly("piece_index", &hash_failed_alert::piece_index) + ; + + class_, noncopyable>( + "peer_ban_alert", peer_ban_alert_doc, no_init + ) + .def_readonly("ip", &peer_ban_alert::ip) + .def_readonly("handle", &peer_ban_alert::handle) + ; + + class_, noncopyable>( + "peer_error_alert", peer_error_alert_doc, no_init + ) + .def_readonly("ip", &peer_error_alert::ip) + .def_readonly("pid", &peer_error_alert::pid) + ; + + class_, noncopyable>( + "invalid_request_alert", invalid_request_alert_doc, no_init + ) + .def_readonly("handle", &invalid_request_alert::handle) + .def_readonly("ip", &invalid_request_alert::ip) + .def_readonly("request", &invalid_request_alert::request) + .def_readonly("pid", &invalid_request_alert::pid) + ; + + class_("peer_request", peer_request_doc) + .def_readonly("piece", &peer_request::piece) + .def_readonly("start", &peer_request::start) + .def_readonly("length", &peer_request::length) + .def(self == self) + ; + + class_, noncopyable>( + "torrent_finished_alert", torrent_finished_alert_doc, no_init + ) + .def_readonly("handle", &torrent_finished_alert::handle) + ; + + class_, noncopyable>( + "metadata_failed_alert", metadata_failed_alert_doc, no_init + ) + .def_readonly("handle", &metadata_failed_alert::handle) + ; + + class_, noncopyable>( + "metadata_received_alert", metadata_received_alert_doc, no_init + ) + .def_readonly("handle", &metadata_received_alert::handle) + ; + + class_, noncopyable>( + "fastresume_rejected_alert", fastresume_rejected_alert_doc, no_init + ) + .def_readonly("handle", &fastresume_rejected_alert::handle) + ; +} + diff --git a/bindings/python/src/big_number.cpp b/bindings/python/src/big_number.cpp new file mode 100755 index 000000000..4e41df521 --- /dev/null +++ b/bindings/python/src/big_number.cpp @@ -0,0 +1,20 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +void bind_big_number() +{ + using namespace boost::python; + using namespace libtorrent; + + class_("big_number") + .def(self == self) + .def(self != self) + .def(self < self) + .def(self_ns::str(self)) + ; +} + diff --git a/bindings/python/src/converters.cpp b/bindings/python/src/converters.cpp new file mode 100755 index 000000000..f684cc4f3 --- /dev/null +++ b/bindings/python/src/converters.cpp @@ -0,0 +1,5 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + diff --git a/bindings/python/src/datetime.cpp b/bindings/python/src/datetime.cpp new file mode 100755 index 000000000..b4616839e --- /dev/null +++ b/bindings/python/src/datetime.cpp @@ -0,0 +1,76 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "optional.hpp" + +using namespace boost::python; + +// From Boost 1.34 +object import(str name) +{ + // should be 'char const *' but older python versions don't use 'const' yet. + char *n = extract(name); + handle<> module(borrowed(PyImport_ImportModule(n))); + return object(module); +} + +object datetime_timedelta; +object datetime_datetime; + +struct time_duration_to_python +{ + static PyObject* convert(boost::posix_time::time_duration const& d) + { + object result = datetime_timedelta( + 0 // days + , 0 // seconds + , d.total_microseconds() + ); + + return incref(result.ptr()); + } +}; + +struct ptime_to_python +{ + static PyObject* convert(boost::posix_time::ptime const& pt) + { + boost::gregorian::date date = pt.date(); + boost::posix_time::time_duration td = pt.time_of_day(); + + object result = datetime_datetime( + (int)date.year() + , (int)date.month() + , (int)date.day() + , td.hours() + , td.minutes() + , td.seconds() + ); + + return incref(result.ptr()); + } +}; + +void bind_datetime() +{ + object datetime = import("datetime").attr("__dict__"); + + datetime_timedelta = datetime["timedelta"]; + datetime_datetime = datetime["datetime"]; + + to_python_converter< + boost::posix_time::time_duration + , time_duration_to_python + >(); + + to_python_converter< + boost::posix_time::ptime + , ptime_to_python + >(); + + optional_to_python(); +} + diff --git a/bindings/python/src/docstrings.cpp b/bindings/python/src/docstrings.cpp new file mode 100755 index 000000000..c9a0c8a85 --- /dev/null +++ b/bindings/python/src/docstrings.cpp @@ -0,0 +1,266 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// -- torrent_status -------------------------------------------------------- + +char const* torrent_status_doc = + "Represents the current status for a torrent.\n" + "Returned by `torrent_handle.status()`."; + +char const* torrent_status_state_doc = + "The torrents current task. One of `torrent_status.states`."; + +char const* torrent_status_paused_doc = + "Indicates if this torrent is paused or not."; + +char const* torrent_status_progress_doc = + "A value in the range [0, 1], that represents the progress of\n" + "the torrent's current task."; + +char const* torrent_status_next_announce_doc = + "The time until the torrent will announce itself to the\n" + "tracker. An instance of `datetime.timedelta`."; + +char const* torrent_status_announce_interval_doc = + "The interval at which the torrent will reannounce itself to the\n" + "tracker. An instance of `datetime.timedelta`."; + +char const* torrent_status_current_tracker_doc = + "The URL of the last working tracker. If no tracker request has\n" + "been successful yet, it's set to an empty string."; + +char const* torrent_status_total_download_doc = ""; +char const* torrent_status_total_upload_doc = ""; +char const* torrent_status_total_payload_download_doc = ""; +char const* torrent_status_total_payload_upload_doc = ""; +char const* torrent_status_total_failed_bytes_doc = ""; + +// -- session_status -------------------------------------------------------- + +char const* session_status_doc = + ""; +char const* session_status_has_incoming_connections_doc = + ""; +char const* session_status_upload_rate_doc = + ""; +char const* session_status_download_rate_doc = + ""; +char const* session_status_payload_upload_rate_doc = + ""; +char const* session_status_payload_download_rate_doc = + ""; +char const* session_status_total_download_doc = + ""; +char const* session_status_total_upload_doc = + ""; +char const* session_status_total_payload_download_doc = + ""; +char const* session_status_total_payload_upload_doc = + ""; +char const* session_status_num_peers_doc = + ""; +char const* session_status_dht_nodes_doc = + ""; +char const* session_status_cache_nodes_doc = + ""; +char const* session_status_dht_torrents_doc = + ""; + +// -- session --------------------------------------------------------------- + +char const* session_doc = + ""; +char const* session_init_doc = + "The `fingerprint` is a short string that will be used in\n" + "the peer-id to identify the client and the client's version.\n" + "For more details see the `fingerprint` class.\n" + "The constructor that only takes a fingerprint will not open\n" + "a listen port for the session, to get it running you'll have\n" + "to call `session.listen_on()`."; + +char const* session_listen_on_doc = + ""; +char const* session_is_listening_doc = + ""; +char const* session_listen_port_doc = + ""; + +char const* session_status_m_doc = + "Returns an instance of `session_status` with session wide-statistics\n" + "and status"; + +char const* session_start_dht_doc = + ""; +char const* session_stop_dht_doc = + ""; +char const* session_dht_state_doc = + ""; + +char const* session_add_torrent_doc = + "Adds a new torrent to the session. Return a `torrent_handle`.\n" + "\n" + ":Parameters:\n" + " - `torrent_info`: `torrent_info` instance representing the torrent\n" + " you want to add.\n" + " - `save_path`: The path to the directory where files will be saved.\n" + " - `resume_data (optional)`: The resume data for this torrent, as decoded\n" + " with `bdecode()`. This can be acquired from a running torrent with\n" + " `torrent_handle.write_resume_data()`.\n" + " - `compact_mode (optional)`: If set to true (default), the storage\n" + " will grow as more pieces are downloaded, and pieces are rearranged\n" + " to finally be in their correct places once the entire torrent has\n" + " been downloaded. If it is false, the entire storage is allocated\n" + " before download begins. I.e. the files contained in the torrent\n" + " are filled with zeros, and each downloaded piece is put in its\n" + " final place directly when downloaded.\n" + " - `block_size (optional)`: Sets the preferred request size, i.e.\n" + " the number of bytes to request from a peer at a time. This block size\n" + " must be a divisor of the piece size, and since the piece size is an\n" + " even power of 2, so must the block size be. If the block size given\n" + " here turns out to be greater than the piece size, it will simply be\n" + " clamped to the piece size.\n" + "\n" + ":Exceptions:\n" + " - `duplicate_torrent`: If the torrent you are trying to add already\n" + " exists in the session (is either queued for checking, being checked\n" + " or downloading) `add_torrent()` will throw `duplicate_torrent`.\n"; + +char const* session_remove_torrent_doc = + "Close all peer connections associated with the torrent and tell the\n" + "tracker that we've stopped participating in the swarm."; + +char const* session_set_download_rate_limit_doc = + ""; +char const* session_set_upload_rate_limit_doc = + ""; +char const* session_set_max_uploads_doc = + ""; +char const* session_set_max_connections_doc = + ""; +char const* session_set_max_half_open_connections_doc = + "Sets the maximum number of half-open connections libtorrent will\n" + "have when connecting to peers. A half-open connection is one where\n" + "connect() has been called, but the connection still hasn't been\n" + "established (nor failed). Windows XP Service Pack 2 sets a default,\n" + "system wide, limit of the number of half-open connections to 10. So, \n" + "this limit can be used to work nicer together with other network\n" + "applications on that system. The default is to have no limit, and passing\n" + "-1 as the limit, means to have no limit. When limiting the number of\n" + "simultaneous connection attempts, peers will be put in a queue waiting\n" + "for their turn to get connected."; + +char const* session_set_settings_doc = + ""; +char const* session_set_severity_level_doc = + ""; +char const* session_pop_alert_doc = + ""; + +// -- alert ----------------------------------------------------------------- + +char const* alert_doc = + "Base class for all concrete alert classes."; + +char const* alert_msg_doc = + "Returns a string describing this alert."; + +char const* alert_severity_doc = + "Returns the severity level for this alert, one of `alert.severity_levels`."; + +char const* listen_failed_alert_doc = + "This alert is generated when none of the ports, given in the\n" + "port range, to `session` can be opened for listening. This alert\n" + "is generated as severity level `alert.severity_levels.fatal`."; + +char const* file_error_alert_doc = + "If the storage fails to read or write files that it needs access\n" + "to, this alert is generated and the torrent is paused. It is\n" + "generated as severity level `alert.severity_levels.fatal`."; + +char const* tracker_announce_alert_doc = + "This alert is generated each time a tracker announce is sent\n" + "(or attempted to be sent). It is generated at severity level `alert.severity_levels.info`."; + +char const* tracker_alert_doc = + "This alert is generated on tracker time outs, premature\n" + "disconnects, invalid response or a HTTP response other than\n" + "\"200 OK\". From the alert you can get the handle to the torrent\n" + "the tracker belongs to. This alert is generated as severity level\n" + "`alert.severity_levels.warning`."; + +char const* tracker_reply_alert_doc = + "This alert is only for informational purpose. It is generated when\n" + "a tracker announce succeeds. It is generated with severity level\n" + "`alert.severity_levels.info`."; + +char const* tracker_warning_alert_doc = + "This alert is triggered if the tracker reply contains a warning\n" + "field. Usually this means that the tracker announce was successful\n" + ", but the tracker has a message to the client. The message string in\n" + "the alert will contain the warning message from the tracker. It is\n" + "generated with severity level `alert.severity_levels.warning`."; + +char const* url_seed_alert_doc = + "This alert is generated when a HTTP seed name lookup fails. This\n" + "alert is generated as severity level `alert.severity_levels.warning`."; + +char const* hash_failed_alert_doc = + "This alert is generated when a finished piece fails its hash check.\n" + "You can get the handle to the torrent which got the failed piece\n" + "and the index of the piece itself from the alert. This alert is\n" + "generated as severity level `alert.severity_levels.info`."; + +char const* peer_ban_alert_doc = + "This alert is generated when a peer is banned because it has sent\n" + "too many corrupt pieces to us. It is generated at severity level\n" + "`alert.severity_levels.info`. The handle member is a `torrent_handle` to the torrent that\n" + "this peer was a member of."; + +char const* peer_error_alert_doc = + "This alert is generated when a peer sends invalid data over the\n" + "peer-peer protocol. The peer will be disconnected, but you get its\n" + "ip address from the alert, to identify it. This alert is generated\n" + "is severity level `alert.severity_levels.debug`."; + +char const* invalid_request_alert_doc = + "This is a debug alert that is generated by an incoming invalid\n" + "piece request. The handle is a handle to the torrent the peer\n" + "is a member of. Ip is the address of the peer and the request is\n" + "the actual incoming request from the peer. The alert is generated\n" + "as severity level `alert.severity_levels.debug`."; + +char const* peer_request_doc = + "The `peer_request` contains the values the client sent in its\n" + "request message. ``piece`` is the index of the piece it want data\n" + "from, ``start`` is the offset within the piece where the data should be\n" + "read, and ``length`` is the amount of data it wants."; + +char const* torrent_finished_alert_doc = + "This alert is generated when a torrent switches from being a\n" + "downloader to a seed. It will only be generated once per torrent.\n" + "It contains a `torrent_handle` to the torrent in question. This alert\n" + "is generated as severity level `alert.severity_levels.info`."; + +char const* metadata_failed_alert_doc = + "This alert is generated when the metadata has been completely\n" + "received and the info-hash failed to match it. i.e. the\n" + "metadata that was received was corrupt. libtorrent will\n" + "automatically retry to fetch it in this case. This is only\n" + "relevant when running a torrent-less download, with the metadata\n" + "extension provided by libtorrent. It is generated at severity\n" + "level `alert.severity_levels.info`."; + +char const* metadata_received_alert_doc = + "This alert is generated when the metadata has been completely\n" + "received and the torrent can start downloading. It is not generated\n" + "on torrents that are started with metadata, but only those that\n" + "needs to download it from peers (when utilizing the libtorrent\n" + "extension). It is generated at severity level `alert.severity_levels.info`."; + +char const* fastresume_rejected_alert_doc = + "This alert is generated when a fastresume file has been passed\n" + "to `session.add_torrent` but the files on disk did not match the\n" + "fastresume file. The string explains the reason why the resume\n" + "file was rejected. It is generated at severity level `alert.severity_levels.warning`."; + diff --git a/bindings/python/src/entry.cpp b/bindings/python/src/entry.cpp new file mode 100755 index 000000000..bccba78ce --- /dev/null +++ b/bindings/python/src/entry.cpp @@ -0,0 +1,132 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +struct entry_to_python +{ + static object convert(entry::list_type const& l) + { + list result; + + for (entry::list_type::const_iterator i(l.begin()), e(l.end()); i != e; ++i) + { + result.append(*i); + } + + return result; + } + + static object convert(entry::dictionary_type const& d) + { + dict result; + + for (entry::dictionary_type::const_iterator i(d.begin()), e(d.end()); i != e; ++i) + result[i->first] = i->second; + + return result; + } + + static object convert0(entry const& e) + { + switch (e.type()) + { + case entry::int_t: + return object(e.integer()); + case entry::string_t: + return object(e.string()); + case entry::list_t: + return convert(e.list()); + case entry::dictionary_t: + return convert(e.dict()); + } + + return object(); + } + + static PyObject* convert(entry const& e) + { + return incref(convert0(e).ptr()); + } +}; + +struct entry_from_python +{ + entry_from_python() + { + converter::registry::push_back( + &convertible, &construct, type_id() + ); + } + + static void* convertible(PyObject* e) + { + return e; + } + + static entry construct0(object e) + { + if (extract(e).check()) + { + dict d = extract(e); + list items(d.items()); + std::size_t length = extract(items.attr("__len__")()); + entry result(entry::dictionary_t); + + for (std::size_t i = 0; i < length; ++i) + { + result.dict().insert( + std::make_pair( + extract(items[i][0])() + , construct0(items[i][1]) + ) + ); + } + + return result; + } + else if (extract(e).check()) + { + list l = extract(e); + + std::size_t length = extract(l.attr("__len__")()); + entry result(entry::list_t); + + for (std::size_t i = 0; i < length; ++i) + { + result.list().push_back(construct0(l[i])); + } + + return result; + } + else if (extract(e).check()) + { + return entry(extract(e)()); + } + else if (extract(e).check()) + { + return entry(extract(e)()); + } + + return entry(); + } + + static void construct(PyObject* e, converter::rvalue_from_python_stage1_data* data) + { + void* storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; + new (storage) entry(construct0(object(borrowed(e)))); + data->convertible = storage; + } +}; + +void bind_entry() +{ + to_python_converter(); + entry_from_python(); +} + diff --git a/bindings/python/src/extensions.cpp b/bindings/python/src/extensions.cpp new file mode 100755 index 000000000..75efbbe6f --- /dev/null +++ b/bindings/python/src/extensions.cpp @@ -0,0 +1,139 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include "gil.hpp" + +using namespace boost::python; +using namespace libtorrent; + +namespace +{ + + struct torrent_plugin_wrap : torrent_plugin, wrapper + { + boost::shared_ptr new_connection(peer_connection* p) + { + lock_gil lock; + + if (override f = this->get_override("new_connection")) + return f(p); + return torrent_plugin::new_connection(p); + } + + boost::shared_ptr default_new_connection(peer_connection* p) + { + return this->torrent_plugin::new_connection(p); + } + + void on_piece_pass(int index) + { + lock_gil lock; + + if (override f = this->get_override("on_piece_pass")) + f(index); + else + torrent_plugin::on_piece_pass(index); + } + + void default_on_piece_pass(int index) + { + this->torrent_plugin::on_piece_pass(index); + } + + void on_piece_failed(int index) + { + lock_gil lock; + + if (override f = this->get_override("on_piece_failed")) + f(index); + else + torrent_plugin::on_piece_failed(index); + } + + void default_on_piece_failed(int index) + { + return this->torrent_plugin::on_piece_failed(index); + } + + void tick() + { + lock_gil lock; + + if (override f = this->get_override("tick")) + f(); + else + torrent_plugin::tick(); + } + + void default_tick() + { + return this->torrent_plugin::tick(); + } + + bool on_pause() + { + lock_gil lock; + + if (override f = this->get_override("on_pause")) + return f(); + return torrent_plugin::on_pause(); + } + + bool default_on_pause() + { + return this->torrent_plugin::on_pause(); + } + + bool on_resume() + { + lock_gil lock; + + if (override f = this->get_override("on_resume")) + return f(); + return torrent_plugin::on_resume(); + } + + bool default_on_resume() + { + return this->torrent_plugin::on_resume(); + } + }; + +} // namespace unnamed + +void bind_extensions() +{ + class_< + torrent_plugin_wrap, boost::shared_ptr, boost::noncopyable + >("torrent_plugin") + .def( + "new_connection" + , &torrent_plugin::new_connection, &torrent_plugin_wrap::default_new_connection + ) + .def( + "on_piece_pass" + , &torrent_plugin::on_piece_pass, &torrent_plugin_wrap::default_on_piece_pass + ) + .def( + "on_piece_failed" + , &torrent_plugin::on_piece_failed, &torrent_plugin_wrap::default_on_piece_failed + ) + .def( + "tick" + , &torrent_plugin::tick, &torrent_plugin_wrap::default_tick + ) + .def( + "on_pause" + , &torrent_plugin::on_pause, &torrent_plugin_wrap::default_on_pause + ) + .def( + "on_resume" + , &torrent_plugin::on_resume, &torrent_plugin_wrap::default_on_resume + ); +} + diff --git a/bindings/python/src/filesystem.cpp b/bindings/python/src/filesystem.cpp new file mode 100755 index 000000000..a5def7ea2 --- /dev/null +++ b/bindings/python/src/filesystem.cpp @@ -0,0 +1,49 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; + +struct path_to_python +{ + static PyObject* convert(boost::filesystem::path const& p) + { + return incref(object(p.string()).ptr()); + } +}; + +struct path_from_python +{ + path_from_python() + { + converter::registry::push_back( + &convertible, &construct, type_id() + ); + } + + static void* convertible(PyObject* x) + { + return PyString_Check(x) ? x : 0; + } + + static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data) + { + void* storage = ((converter::rvalue_from_python_storage< + boost::filesystem::path + >*)data)->storage.bytes; + new (storage) boost::filesystem::path(PyString_AsString(x)); + data->convertible = storage; + } +}; + +void bind_filesystem() +{ + to_python_converter(); + path_from_python(); + + boost::filesystem::path::default_name_check(boost::filesystem::native); +} + diff --git a/bindings/python/src/fingerprint.cpp b/bindings/python/src/fingerprint.cpp new file mode 100755 index 000000000..ca40f3d4d --- /dev/null +++ b/bindings/python/src/fingerprint.cpp @@ -0,0 +1,27 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +void bind_fingerprint() +{ + using namespace boost::python; + using namespace libtorrent; + + class_("fingerprint", no_init) + .def( + init( + (arg("id"), "major", "minor", "revision", "tag") + ) + ) + .def("__str__", &fingerprint::to_string) + .def_readonly("name", &fingerprint::name) + .def_readonly("major_version", &fingerprint::major_version) + .def_readonly("minor_version", &fingerprint::minor_version) + .def_readonly("revision_version", &fingerprint::revision_version) + .def_readonly("tag_version", &fingerprint::tag_version) + ; +} + diff --git a/bindings/python/src/gil.hpp b/bindings/python/src/gil.hpp new file mode 100755 index 000000000..d9534c92c --- /dev/null +++ b/bindings/python/src/gil.hpp @@ -0,0 +1,144 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef GIL_070107_HPP +# define GIL_070107_HPP + +# include +# include +# include +# include + +//namespace libtorrent { namespace python { + +// RAII helper to release GIL. +struct allow_threading_guard +{ + allow_threading_guard() + : save(PyEval_SaveThread()) + {} + + ~allow_threading_guard() + { + PyEval_RestoreThread(save); + } + + PyThreadState* save; +}; + +struct lock_gil +{ + lock_gil() + : state(PyGILState_Ensure()) + {} + + ~lock_gil() + { + PyGILState_Release(state); + } + + PyGILState_STATE state; +}; + +template +struct allow_threading +{ + allow_threading(F fn) + : fn(fn) + {} + + template + R operator()(A0& a0) + { + allow_threading_guard guard; + return (a0.*fn)(); + } + + template + R operator()(A0& a0, A1& a1) + { + allow_threading_guard guard; + return (a0.*fn)(a1); + } + + template + R operator()(A0& a0, A1& a1, A2& a2) + { + allow_threading_guard guard; + return (a0.*fn)(a1,a2); + } + + template + R operator()(A0& a0, A1& a1, A2& a2, A3& a3) + { + allow_threading_guard guard; + return (a0.*fn)(a1,a2,a3); + } + + template + R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) + { + allow_threading_guard guard; + return (a0.*fn)(a1,a2,a3,a4); + } + + template + R operator()(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4, A5& a5) + { + allow_threading_guard guard; + return (a0.*fn)(a1,a2,a3,a4,a5); + } + + F fn; +}; + +template +struct visitor : boost::python::def_visitor > +{ + visitor(F fn) + : fn(fn) + {} + + template + void visit_aux( + Class& cl, char const* name + , Options const& options, Signature const& signature) const + { + typedef typename boost::mpl::at_c::type return_type; + + cl.def( + name + , boost::python::make_function( + allow_threading(fn) + , options.policies() + , options.keywords() + , signature + ) + ); + } + + template + void visit(Class& cl, char const* name, Options const& options) const + { + this->visit_aux( + cl, name, options + , boost::python::detail::get_signature(fn, (typename Class::wrapped_type*)0) + ); + } + + F fn; +}; + +// Member function adaptor that releases and aqcuires the GIL +// around the function call. +template +visitor allow_threads(F fn) +{ + return visitor(fn); +} + +//}} // namespace libtorrent::python + +#endif // GIL_070107_HPP + diff --git a/bindings/python/src/module.cpp b/bindings/python/src/module.cpp new file mode 100755 index 000000000..5c8d891d2 --- /dev/null +++ b/bindings/python/src/module.cpp @@ -0,0 +1,48 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +void bind_utility(); +void bind_fingerprint(); +void bind_big_number(); +void bind_session(); +void bind_entry(); +void bind_torrent_info(); +void bind_filesystem(); +void bind_torrent_handle(); +void bind_torrent_status(); +void bind_session_settings(); +void bind_version(); +void bind_alert(); +void bind_datetime(); +void bind_extensions(); +void bind_peer_plugin(); +void bind_torrent(); +void bind_peer_info(); + +BOOST_PYTHON_MODULE(libtorrent) +{ + Py_Initialize(); + PyEval_InitThreads(); + + bind_utility(); + bind_fingerprint(); + bind_big_number(); + bind_entry(); + bind_session(); + bind_torrent_info(); + bind_filesystem(); + bind_torrent_handle(); + bind_torrent_status(); + bind_session_settings(); + bind_version(); + bind_alert(); + bind_datetime(); + bind_extensions(); + bind_peer_plugin(); + bind_torrent(); + bind_peer_info(); +} + diff --git a/bindings/python/src/optional.hpp b/bindings/python/src/optional.hpp new file mode 100755 index 000000000..63138cc68 --- /dev/null +++ b/bindings/python/src/optional.hpp @@ -0,0 +1,31 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef OPTIONAL_070108_HPP +# define OPTIONAL_070108_HPP + +# include +# include + +template +struct optional_to_python +{ + optional_to_python() + { + boost::python::to_python_converter< + boost::optional, optional_to_python + >(); + } + + static PyObject* convert(boost::optional const& x) + { + if (!x) + return boost::python::incref(Py_None); + + return boost::python::incref(boost::python::object(*x).ptr()); + } +}; + +#endif // OPTIONAL_070108_HPP + diff --git a/bindings/python/src/peer_info.cpp b/bindings/python/src/peer_info.cpp new file mode 100755 index 000000000..f4fdfc0a5 --- /dev/null +++ b/bindings/python/src/peer_info.cpp @@ -0,0 +1,51 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +void bind_peer_info() +{ + scope pi = class_("peer_info") + .def_readonly("flags", &peer_info::flags) + .def_readonly("ip", &peer_info::ip) + .def_readonly("up_speed", &peer_info::up_speed) + .def_readonly("down_speed", &peer_info::down_speed) + .def_readonly("payload_up_speed", &peer_info::payload_up_speed) + .def_readonly("payload_down_speed", &peer_info::payload_down_speed) + .def_readonly("total_download", &peer_info::total_download) + .def_readonly("total_upload", &peer_info::total_upload) + .def_readonly("pid", &peer_info::pid) + .def_readonly("pieces", &peer_info::pieces) + .def_readonly("seed", &peer_info::seed) + .def_readonly("upload_limit", &peer_info::upload_limit) + .def_readonly("download_limit", &peer_info::download_limit) + .def_readonly("load_balancing", &peer_info::load_balancing) + .def_readonly("download_queue_length", &peer_info::download_queue_length) + .def_readonly("upload_queue_length", &peer_info::upload_queue_length) + .def_readonly("downloading_piece_index", &peer_info::downloading_piece_index) + .def_readonly("downloading_block_index", &peer_info::downloading_block_index) + .def_readonly("downloading_progress", &peer_info::downloading_progress) + .def_readonly("downloading_total", &peer_info::downloading_total) + .def_readonly("client", &peer_info::client) + .def_readonly("connection_type", &peer_info::connection_type) + ; + + pi.attr("interesting") = (int)peer_info::interesting; + pi.attr("choked") = (int)peer_info::choked; + pi.attr("remote_interested") = (int)peer_info::remote_interested; + pi.attr("remote_choked") = (int)peer_info::remote_choked; + pi.attr("supports_extensions") = (int)peer_info::supports_extensions; + pi.attr("local_connection") = (int)peer_info::local_connection; + pi.attr("handshake") = (int)peer_info::handshake; + pi.attr("connecting") = (int)peer_info::connecting; + pi.attr("queued") = (int)peer_info::queued; + + pi.attr("standard_bittorrent") = 0; + pi.attr("web_seed") = 1; +} + diff --git a/bindings/python/src/peer_plugin.cpp b/bindings/python/src/peer_plugin.cpp new file mode 100755 index 000000000..94b5fde66 --- /dev/null +++ b/bindings/python/src/peer_plugin.cpp @@ -0,0 +1,344 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +namespace +{ + struct peer_plugin_wrap : peer_plugin, wrapper + { + void add_handshake(entry& e) + { + if (override f = this->get_override("add_handshake")) + e = call(f.ptr(), e); + else + peer_plugin::add_handshake(e); + } + + void default_add_handshake(entry& e) + { + this->peer_plugin::add_handshake(e); + } + + bool on_handshake() + { + if (override f = this->get_override("on_handshake")) + return f(); + else + return peer_plugin::on_handshake(); + } + + bool default_on_handshake() + { + return this->peer_plugin::on_handshake(); + } + + bool on_extension_handshake(entry const& e) + { + if (override f = this->get_override("on_extension_handshake")) + return f(e); + else + return peer_plugin::on_extension_handshake(e); + } + + bool default_on_extension_handshake(entry const& e) + { + return this->peer_plugin::on_extension_handshake(e); + } + + bool on_choke() + { + if (override f = this->get_override("on_choke")) + return f(); + else + return peer_plugin::on_choke(); + } + + bool default_on_choke() + { + return this->peer_plugin::on_choke(); + } + + bool on_unchoke() + { + if (override f = this->get_override("on_unchoke")) + return f(); + else + return peer_plugin::on_unchoke(); + } + + bool default_on_unchoke() + { + return this->peer_plugin::on_unchoke(); + } + + bool on_interested() + { + if (override f = this->get_override("on_interested")) + return f(); + else + return peer_plugin::on_interested(); + } + + bool default_on_interested() + { + return this->peer_plugin::on_interested(); + } + + bool on_not_interested() + { + if (override f = this->get_override("on_not_interested")) + return f(); + else + return peer_plugin::on_not_interested(); + } + + bool default_on_not_interested() + { + return this->peer_plugin::on_not_interested(); + } + + bool on_have(int index) + { + if (override f = this->get_override("on_have")) + return f(index); + else + return peer_plugin::on_have(index); + } + + bool default_on_have(int index) + { + return this->peer_plugin::on_have(index); + } + + bool on_bitfield(std::vector const& bitfield) + { + if (override f = this->get_override("on_bitfield")) + return f(bitfield); + else + return peer_plugin::on_bitfield(bitfield); + } + + bool default_on_bitfield(std::vector const& bitfield) + { + return this->peer_plugin::on_bitfield(bitfield); + } + + bool on_request(peer_request const& req) + { + if (override f = this->get_override("on_request")) + return f(req); + else + return peer_plugin::on_request(req); + } + + bool default_on_request(peer_request const& req) + { + return this->peer_plugin::on_request(req); + } + + bool on_piece(peer_request const& piece, char const* data) + { + if (override f = this->get_override("on_piece")) + return f(piece, data); + else + return peer_plugin::on_piece(piece, data); + } + + bool default_on_piece(peer_request const& piece, char const* data) + { + return this->peer_plugin::on_piece(piece, data); + } + + bool on_cancel(peer_request const& req) + { + if (override f = this->get_override("on_cancel")) + return f(req); + else + return peer_plugin::on_cancel(req); + } + + bool default_on_cancel(peer_request const& req) + { + return this->peer_plugin::on_cancel(req); + } + + bool on_extended(int length, int msg, buffer::const_interval body) + { + if (override f = this->get_override("on_extended")) + return f(length, msg, body); + else + return peer_plugin::on_extended(length, msg, body); + } + + bool default_on_extended(int length, int msg, buffer::const_interval body) + { + return this->peer_plugin::on_extended(length, msg, body); + } + + bool on_unknown_message(int length, int msg, buffer::const_interval body) + { + if (override f = this->get_override("on_unknown_message")) + return f(length, msg, body); + else + return peer_plugin::on_unknown_message(length, msg, body); + } + + bool default_on_unknown_message(int length, int msg, buffer::const_interval body) + { + return this->peer_plugin::on_unknown_message(length, msg, body); + } + + void on_piece_pass(int index) + { + if (override f = this->get_override("on_piece_pass")) + f(index); + else + peer_plugin::on_piece_pass(index); + } + + void default_on_piece_pass(int index) + { + this->peer_plugin::on_piece_pass(index); + } + + void on_piece_failed(int index) + { + if (override f = this->get_override("on_piece_failed")) + f(index); + else + peer_plugin::on_piece_failed(index); + } + + void default_on_piece_failed(int index) + { + this->peer_plugin::on_piece_failed(index); + } + + void tick() + { + if (override f = this->get_override("tick")) + f(); + else + peer_plugin::tick(); + } + + void default_tick() + { + this->peer_plugin::tick(); + } + + bool write_request(peer_request const& req) + { + if (override f = this->get_override("write_request")) + return f(req); + else + return peer_plugin::write_request(req); + } + + bool default_write_request(peer_request const& req) + { + return this->peer_plugin::write_request(req); + } + }; + + object get_buffer() + { + static char const data[] = "foobar"; + return object(handle<>(PyBuffer_FromMemory((void*)data, 6))); + } + +} // namespace unnamed + +void bind_peer_plugin() +{ + class_< + peer_plugin_wrap, boost::shared_ptr, boost::noncopyable + >("peer_plugin") + .def( + "add_handshake" + , &peer_plugin::add_handshake, &peer_plugin_wrap::default_add_handshake + ) + .def( + "on_handshake" + , &peer_plugin::on_handshake, &peer_plugin_wrap::default_on_handshake + ) + .def( + "on_extension_handshake" + , &peer_plugin::on_extension_handshake + , &peer_plugin_wrap::default_on_extension_handshake + ) + .def( + "on_choke" + , &peer_plugin::on_choke, &peer_plugin_wrap::default_on_choke + ) + .def( + "on_unchoke" + , &peer_plugin::on_unchoke, &peer_plugin_wrap::default_on_unchoke + ) + .def( + "on_interested" + , &peer_plugin::on_interested, &peer_plugin_wrap::default_on_interested + ) + .def( + "on_not_interested" + , &peer_plugin::on_not_interested, &peer_plugin_wrap::default_on_not_interested + ) + .def( + "on_have" + , &peer_plugin::on_have, &peer_plugin_wrap::default_on_have + ) + .def( + "on_bitfield" + , &peer_plugin::on_bitfield, &peer_plugin_wrap::default_on_bitfield + ) + .def( + "on_request" + , &peer_plugin::on_request, &peer_plugin_wrap::default_on_request + ) + .def( + "on_piece" + , &peer_plugin::on_piece, &peer_plugin_wrap::default_on_piece + ) + .def( + "on_cancel" + , &peer_plugin::on_cancel, &peer_plugin_wrap::default_on_cancel + ) + .def( + "on_piece_pass" + , &peer_plugin::on_piece_pass, &peer_plugin_wrap::default_on_piece_pass + ) + .def( + "on_piece_failed" + , &peer_plugin::on_piece_failed, &peer_plugin_wrap::default_on_piece_failed + ) + .def( + "tick" + , &peer_plugin::tick, &peer_plugin_wrap::default_tick + ) + .def( + "write_request" + , &peer_plugin::write_request, &peer_plugin_wrap::default_write_request + ) + // These seem to make VC7.1 freeze. Needs special handling. + + /*.def( + "on_extended" + , &peer_plugin::on_extended, &peer_plugin_wrap::default_on_extended + ) + .def( + "on_unknown_message" + , &peer_plugin::on_unknown_message, &peer_plugin_wrap::default_on_unknown_message + )*/ + ; + + def("get_buffer", &get_buffer); +} + diff --git a/bindings/python/src/session.cpp b/bindings/python/src/session.cpp new file mode 100755 index 000000000..01b9c50aa --- /dev/null +++ b/bindings/python/src/session.cpp @@ -0,0 +1,204 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "gil.hpp" + +using namespace boost::python; +using namespace libtorrent; + +extern char const* session_status_doc; +extern char const* session_status_has_incoming_connections_doc; +extern char const* session_status_upload_rate_doc; +extern char const* session_status_download_rate_doc; +extern char const* session_status_payload_upload_rate_doc; +extern char const* session_status_payload_download_rate_doc; +extern char const* session_status_total_download_doc; +extern char const* session_status_total_upload_doc; +extern char const* session_status_total_payload_download_doc; +extern char const* session_status_total_payload_upload_doc; +extern char const* session_status_num_peers_doc; +extern char const* session_status_dht_nodes_doc; +extern char const* session_status_cache_nodes_doc; +extern char const* session_status_dht_torrents_doc; + +extern char const* session_doc; +extern char const* session_init_doc; +extern char const* session_listen_on_doc; +extern char const* session_is_listening_doc; +extern char const* session_listen_port_doc; +extern char const* session_status_m_doc; +extern char const* session_start_dht_doc; +extern char const* session_stop_dht_doc; +extern char const* session_dht_state_doc; +extern char const* session_add_torrent_doc; +extern char const* session_remove_torrent_doc; +extern char const* session_set_download_rate_limit_doc; +extern char const* session_set_upload_rate_limit_doc; +extern char const* session_set_max_uploads_doc; +extern char const* session_set_max_connections_doc; +extern char const* session_set_max_half_open_connections_doc; +extern char const* session_set_settings_doc; +extern char const* session_set_severity_level_doc; +extern char const* session_pop_alert_doc; + +namespace +{ + + bool listen_on(session& s, int min_, int max_, char const* interface) + { + allow_threading_guard guard; + return s.listen_on(std::make_pair(min_, max_), interface); + } + + struct invoke_extension_factory + { + invoke_extension_factory(object const& callback) + : cb(callback) + {} + + boost::shared_ptr operator()(torrent* t) + { + lock_gil lock; + return extract >(cb(ptr(t)))(); + } + + object cb; + }; + + void add_extension(session& s, object const& e) + { +// allow_threading_guard guard; + s.add_extension(invoke_extension_factory(e)); + } + +} // namespace unnamed + +void bind_session() +{ + class_("session_status", session_status_doc) + .def_readonly( + "has_incoming_connections", &session_status::has_incoming_connections + , session_status_has_incoming_connections_doc + ) + .def_readonly( + "upload_rate", &session_status::upload_rate + , session_status_upload_rate_doc + ) + .def_readonly( + "download_rate", &session_status::download_rate + , session_status_download_rate_doc + ) + .def_readonly( + "payload_upload_rate", &session_status::payload_upload_rate + , session_status_payload_upload_rate_doc + ) + .def_readonly( + "payload_download_rate", &session_status::payload_download_rate + , session_status_payload_download_rate_doc + ) + .def_readonly( + "total_download", &session_status::total_download + , session_status_total_download_doc + ) + .def_readonly( + "total_upload", &session_status::total_upload + , session_status_total_upload_doc + ) + .def_readonly( + "total_payload_download", &session_status::total_payload_download + , session_status_total_payload_download_doc + ) + .def_readonly( + "total_payload_upload", &session_status::total_payload_upload + , session_status_total_payload_upload_doc + ) + .def_readonly( + "num_peers", &session_status::num_peers + , session_status_num_peers_doc + ) +#ifndef TORRENT_DISABLE_DHT + .def_readonly( + "dht_nodes", &session_status::dht_nodes + , session_status_dht_nodes_doc + ) + .def_readonly( + "dht_cache_nodes", &session_status::dht_node_cache + , session_status_cache_nodes_doc + ) + .def_readonly( + "dht_torrents", &session_status::dht_torrents + , session_status_dht_torrents_doc + ) +#endif + ; + + torrent_handle (session::*add_torrent0)( + torrent_info const& + , boost::filesystem::path const& + , entry const& + , bool + , int + ) = &session::add_torrent; + + class_("session", session_doc, no_init) + .def( + init(arg("fingerprint")=fingerprint("LT",0,1,0,0), session_init_doc) + ) + .def( + "listen_on", &listen_on + , (arg("min"), "max", arg("interface") = (char const*)0) + , session_listen_on_doc + ) + .def("is_listening", allow_threads(&session::is_listening), session_is_listening_doc) + .def("listen_port", allow_threads(&session::listen_port), session_listen_port_doc) + .def("status", allow_threads(&session::status), session_status_m_doc) +#ifndef TORRENT_DISABLE_DHT + .def("start_dht", allow_threads(&session::start_dht), session_start_dht_doc) + .def("stop_dht", allow_threads(&session::stop_dht), session_stop_dht_doc) + .def("dht_state", allow_threads(&session::dht_state), session_dht_state_doc) +#endif + .def( + "add_torrent", allow_threads(add_torrent0) + , ( + arg("torrent_info"), "save_path", arg("resume_data") = entry() + , arg("compact_mode") = true, arg("block_size") = 16 * 1024 + ) + , session_add_torrent_doc + ) + .def("remove_torrent", allow_threads(&session::remove_torrent), session_remove_torrent_doc) + .def( + "set_download_rate_limit", allow_threads(&session::set_download_rate_limit) + , session_set_download_rate_limit_doc + ) + .def( + "set_upload_rate_limit", allow_threads(&session::set_upload_rate_limit) + , session_set_upload_rate_limit_doc + ) + .def( + "set_max_uploads", allow_threads(&session::set_max_uploads) + , session_set_max_uploads_doc + ) + .def( + "set_max_connections", allow_threads(&session::set_max_connections) + , session_set_max_connections_doc + ) + .def( + "set_max_half_open_connections", allow_threads(&session::set_max_half_open_connections) + , session_set_max_half_open_connections_doc + ) + .def("set_settings", allow_threads(&session::set_settings), session_set_settings_doc) + .def( + "set_severity_level", allow_threads(&session::set_severity_level) + , session_set_severity_level_doc + ) + .def("pop_alert", allow_threads(&session::pop_alert), session_pop_alert_doc) + .def("add_extension", &add_extension) + ; + + register_ptr_to_python >(); +} + diff --git a/bindings/python/src/session_settings.cpp b/bindings/python/src/session_settings.cpp new file mode 100755 index 000000000..69dd62785 --- /dev/null +++ b/bindings/python/src/session_settings.cpp @@ -0,0 +1,32 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +void bind_session_settings() +{ + class_("session_settings") + .def_readwrite("proxy_ip", &session_settings::proxy_ip) + .def_readwrite("proxy_port", &session_settings::proxy_port) + .def_readwrite("proxy_login", &session_settings::proxy_login) + .def_readwrite("proxy_password", &session_settings::proxy_password) + .def_readwrite("user_agent", &session_settings::user_agent) + .def_readwrite("tracker_completion_timeout", &session_settings::tracker_completion_timeout) + .def_readwrite("tracker_receive_timeout", &session_settings::tracker_receive_timeout) + .def_readwrite("tracker_maximum_response_length", &session_settings::tracker_maximum_response_length) + .def_readwrite("piece_timeout", &session_settings::piece_timeout) + .def_readwrite("request_queue_time", &session_settings::request_queue_time) + .def_readwrite("max_allowed_in_request_queue", &session_settings::max_allowed_in_request_queue) + .def_readwrite("max_out_request_queue", &session_settings::max_out_request_queue) + .def_readwrite("whole_pieces_threshold", &session_settings::whole_pieces_threshold) + .def_readwrite("peer_timeout", &session_settings::peer_timeout) + .def_readwrite("urlseed_timeout", &session_settings::urlseed_timeout) + .def_readwrite("urlseed_pipeline_size", &session_settings::urlseed_pipeline_size) + ; +} + diff --git a/bindings/python/src/torrent.cpp b/bindings/python/src/torrent.cpp new file mode 100755 index 000000000..7b2ba76b4 --- /dev/null +++ b/bindings/python/src/torrent.cpp @@ -0,0 +1,15 @@ +// Copyright Daniel Wallin 2007. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +void bind_torrent() +{ + class_("torrent", no_init); +} + diff --git a/bindings/python/src/torrent_handle.cpp b/bindings/python/src/torrent_handle.cpp new file mode 100755 index 000000000..9f071abab --- /dev/null +++ b/bindings/python/src/torrent_handle.cpp @@ -0,0 +1,128 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "gil.hpp" + +using namespace boost::python; +using namespace libtorrent; + +namespace +{ + + std::vector::const_iterator begin_trackers(torrent_handle& i) + { + allow_threading_guard guard; + return i.trackers().begin(); + } + + + std::vector::const_iterator end_trackers(torrent_handle& i) + { + allow_threading_guard guard; + return i.trackers().end(); + } + +} // namespace unnamed + +list file_progress(torrent_handle& handle) +{ + std::vector p; + + { + allow_threading_guard guard; + p.reserve(handle.get_torrent_info().num_files()); + handle.file_progress(p); + } + + list result; + + for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) + result.append(*i); + + return result; +} + +list get_peer_info(torrent_handle const& handle) +{ + std::vector pi; + + { + allow_threading_guard guard; + handle.get_peer_info(pi); + } + + list result; + + for (std::vector::iterator i = pi.begin(); i != pi.end(); ++i) + { + result.append(*i); + } + + return result; +} + +void replace_trackers(torrent_handle& info, object trackers) +{ + object iter(trackers.attr("__iter__")()); + + std::vector result; + + for (;;) + { + handle<> entry(allow_null(PyIter_Next(iter.ptr()))); + + if (entry == handle<>()) + break; + + result.push_back(extract(object(entry))); + } + + allow_threading_guard guard; + info.replace_trackers(result); +} + +void bind_torrent_handle() +{ + void (torrent_handle::*force_reannounce0)() const = &torrent_handle::force_reannounce; + void (torrent_handle::*force_reannounce1)(boost::posix_time::time_duration) const + = &torrent_handle::force_reannounce; + + return_value_policy copy; + +#define _ allow_threads + + class_("torrent_handle") + .def("status", _(&torrent_handle::status)) + .def("torrent_info", _(&torrent_handle::get_torrent_info), return_internal_reference<>()) + .def("is_valid", _(&torrent_handle::is_valid)) + .def("write_resume_data", _(&torrent_handle::write_resume_data)) + .def("force_reannounce", _(force_reannounce0)) + .def("force_reannounce", _(force_reannounce1)) + .def("set_tracker_login", _(&torrent_handle::set_tracker_login)) + .def("add_url_seed", _(&torrent_handle::add_url_seed)) + .def("set_ratio", _(&torrent_handle::set_ratio)) + .def("set_max_uploads", _(&torrent_handle::set_max_uploads)) + .def("set_max_connections", _(&torrent_handle::set_max_connections)) + .def("set_upload_limit", _(&torrent_handle::set_upload_limit)) + .def("set_download_limit", _(&torrent_handle::set_download_limit)) + .def("set_sequenced_download_threshold", _(&torrent_handle::set_sequenced_download_threshold)) + .def("pause", _(&torrent_handle::pause)) + .def("resume", _(&torrent_handle::resume)) + .def("is_paused", _(&torrent_handle::is_paused)) + .def("is_seed", _(&torrent_handle::is_seed)) + .def("filter_piece", _(&torrent_handle::filter_piece)) + .def("is_piece_filtered", _(&torrent_handle::is_piece_filtered)) + .def("has_metadata", _(&torrent_handle::has_metadata)) + .def("save_path", _(&torrent_handle::save_path)) + .def("move_storage", _(&torrent_handle::move_storage)) + .def("info_hash", _(&torrent_handle::info_hash), copy) + .def("file_progress", file_progress) + .def("trackers", range(begin_trackers, end_trackers)) + .def("replace_trackers", replace_trackers) + .def("get_peer_info", get_peer_info) + ; +} + diff --git a/bindings/python/src/torrent_info.cpp b/bindings/python/src/torrent_info.cpp new file mode 100755 index 000000000..1c31ec185 --- /dev/null +++ b/bindings/python/src/torrent_info.cpp @@ -0,0 +1,103 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +namespace +{ + + std::vector::const_iterator begin_trackers(torrent_info& i) + { + return i.trackers().begin(); + } + + + std::vector::const_iterator end_trackers(torrent_info& i) + { + return i.trackers().end(); + } + + void add_node(torrent_info& ti, char const* hostname, int port) + { + ti.add_node(std::make_pair(hostname, port)); + } + + list nodes(torrent_info const& ti) + { + list result; + + typedef std::vector > list_type; + + for (list_type::const_iterator i = ti.nodes().begin(); i != ti.nodes().end(); ++i) + { + result.append(make_tuple(i->first, i->second)); + } + + return result; + } + +} // namespace unnamed + +void bind_torrent_info() +{ + return_value_policy copy; + + class_("torrent_info") + .def(init()) + .def(init()) + + .def("create_torrent", &torrent_info::create_torrent) + .def("set_comment", &torrent_info::set_comment) + .def("set_piece_size", &torrent_info::set_piece_size) + .def("set_creator", &torrent_info::set_creator) + .def("set_hash", &torrent_info::set_hash) + .def("add_tracker", &torrent_info::add_tracker, (arg("url"), arg("tier")=0)) + .def("add_file", &torrent_info::add_file) + .def("add_url_seed", &torrent_info::add_url_seed) + + .def("name", &torrent_info::name, copy) + .def("comment", &torrent_info::comment, copy) + .def("creator", &torrent_info::creator, copy) + .def("total_size", &torrent_info::total_size) + .def("piece_length", &torrent_info::piece_length) + .def("num_pieces", &torrent_info::num_pieces) + .def("info_hash", &torrent_info::info_hash, copy) + + .def("hash_for_piece", &torrent_info::hash_for_piece, copy) + .def("piece_size", &torrent_info::piece_size) + + .def("file_at", &torrent_info::file_at, return_internal_reference<>()) + .def("files", range(&torrent_info::begin_files, &torrent_info::end_files)) + + .def("priv", &torrent_info::priv) + .def("set_priv", &torrent_info::set_priv) + .def("trackers", range(begin_trackers, end_trackers)) + + .def("creation_date", &torrent_info::creation_date) + + .def("add_node", &add_node) + .def("nodes", &nodes) + ; + + class_("file_entry") + .add_property( + "path" + , make_getter( + &file_entry::path, return_value_policy() + ) + ) + .def_readonly("offset", &file_entry::offset) + .def_readonly("size", &file_entry::size) + ; + + class_("announce_entry", init()) + .def_readwrite("url", &announce_entry::url) + .def_readwrite("tier", &announce_entry::tier) + ; +} + diff --git a/bindings/python/src/torrent_status.cpp b/bindings/python/src/torrent_status.cpp new file mode 100755 index 000000000..c321f8bb2 --- /dev/null +++ b/bindings/python/src/torrent_status.cpp @@ -0,0 +1,107 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +object pieces(torrent_status const& s) +{ + list result; + + for (std::vector::const_iterator i(s.pieces->begin()), e(s.pieces->end()); i != e; ++i) + result.append(*i); + + return result; +} + +extern char const* torrent_status_doc; +extern char const* torrent_status_state_doc; +extern char const* torrent_status_paused_doc; +extern char const* torrent_status_progress_doc; +extern char const* torrent_status_next_announce_doc; +extern char const* torrent_status_announce_interval_doc; +extern char const* torrent_status_current_tracker_doc; +extern char const* torrent_status_total_download_doc; +extern char const* torrent_status_total_upload_doc; +extern char const* torrent_status_total_payload_download_doc; +extern char const* torrent_status_total_payload_upload_doc; +extern char const* torrent_status_total_failed_bytes_doc; + +void bind_torrent_status() +{ + scope status = class_("torrent_status", torrent_status_doc) + .def_readonly("state", &torrent_status::state, torrent_status_state_doc) + .def_readonly("paused", &torrent_status::paused, torrent_status_paused_doc) + .def_readonly("progress", &torrent_status::progress, torrent_status_progress_doc) + .add_property( + "next_announce" + , make_getter( + &torrent_status::next_announce, return_value_policy() + ) + , torrent_status_next_announce_doc + ) + .add_property( + "announce_interval" + , make_getter( + &torrent_status::announce_interval, return_value_policy() + ) + , torrent_status_announce_interval_doc + ) + .def_readonly( + "current_tracker", &torrent_status::current_tracker + , torrent_status_current_tracker_doc + ) + .def_readonly( + "total_download", &torrent_status::total_download + , torrent_status_total_download_doc + ) + .def_readonly( + "total_upload", &torrent_status::total_upload + , torrent_status_total_upload_doc + ) + .def_readonly( + "total_payload_download", &torrent_status::total_payload_download + , torrent_status_total_payload_download_doc + ) + .def_readonly( + "total_payload_upload", &torrent_status::total_payload_upload + , torrent_status_total_payload_upload_doc + ) + .def_readonly( + "total_failed_bytes", &torrent_status::total_failed_bytes + , torrent_status_total_failed_bytes_doc + ) + .def_readonly("total_redundant_bytes", &torrent_status::total_redundant_bytes) + .def_readonly("download_rate", &torrent_status::download_rate) + .def_readonly("upload_rate", &torrent_status::upload_rate) + .def_readonly("download_payload_rate", &torrent_status::download_payload_rate) + .def_readonly("upload_payload_rate", &torrent_status::upload_payload_rate) + .def_readonly("num_peers", &torrent_status::num_peers) + .def_readonly("num_complete", &torrent_status::num_complete) + .def_readonly("num_incomplete", &torrent_status::num_incomplete) + .add_property("pieces", pieces) + .def_readonly("num_pieces", &torrent_status::num_pieces) + .def_readonly("total_done", &torrent_status::total_done) + .def_readonly("total_wanted_done", &torrent_status::total_wanted_done) + .def_readonly("total_wanted", &torrent_status::total_wanted) + .def_readonly("num_seeds", &torrent_status::num_seeds) + .def_readonly("distributed_copies", &torrent_status::distributed_copies) + .def_readonly("block_size", &torrent_status::block_size) + ; + + enum_("states") + .value("queued_for_checking", torrent_status::queued_for_checking) + .value("checking_files", torrent_status::checking_files) + .value("connecting_to_tracker", torrent_status::connecting_to_tracker) + .value("downloading", torrent_status::downloading) + .value("finished", torrent_status::finished) + .value("seeding", torrent_status::seeding) + .value("allocating", torrent_status::allocating) + .export_values() + ; +} + diff --git a/bindings/python/src/utility.cpp b/bindings/python/src/utility.cpp new file mode 100755 index 000000000..9ae2d2a93 --- /dev/null +++ b/bindings/python/src/utility.cpp @@ -0,0 +1,37 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +using namespace boost::python; +using namespace libtorrent; + +object client_fingerprint_(peer_id const& id) +{ + boost::optional result = client_fingerprint(id); + return result ? object(*result) : object(); +} + +entry bdecode_(std::string const& data) +{ + return bdecode(data.begin(), data.end()); +} + +std::string bencode_(entry const& e) +{ + std::string result; + bencode(std::back_inserter(result), e); + return result; +} + +void bind_utility() +{ + def("identify_client", &libtorrent::identify_client); + def("client_fingerprint", &client_fingerprint_); + def("bdecode", &bdecode_); + def("bencode", &bencode_); +} + diff --git a/bindings/python/src/version.cpp b/bindings/python/src/version.cpp new file mode 100755 index 000000000..aaeafd900 --- /dev/null +++ b/bindings/python/src/version.cpp @@ -0,0 +1,16 @@ +// Copyright Daniel Wallin 2006. Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +using namespace boost::python; + +void bind_version() +{ + scope().attr("version") = LIBTORRENT_VERSION; + scope().attr("version_major") = LIBTORRENT_VERSION_MAJOR; + scope().attr("version_minor") = LIBTORRENT_VERSION_MINOR; +} +