From df694996a9ee3dae463615645c35fad52abfee26 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 5 Dec 2006 16:19:48 +0000 Subject: [PATCH] --- library/pytorrent.py | 138 +++++++++++++++++++++---------------- library/pytorrent_core.cpp | 16 ++--- library/test.py | 3 +- 3 files changed, 89 insertions(+), 68 deletions(-) diff --git a/library/pytorrent.py b/library/pytorrent.py index 62fbf66c5..cb2300c10 100644 --- a/library/pytorrent.py +++ b/library/pytorrent.py @@ -25,6 +25,13 @@ # pytorrent should be visible, and only it should be imported, in the client. # +# Documentation: +# Torrents have 3 structures: +# 1. torrent_info - persistent data, like name, upload speed cap, etc. +# 2. core_torrent_state - transient state data from the core. This may take +# time to calculate, so we do if efficiently +# 3. supp_torrent_state - supplementary torrent data, from pytorrent + import pytorrent_core import os, shutil @@ -63,16 +70,16 @@ class PyTorrentError(Exception): return repr(self.value) -# Information for a single torrent +# Persistent information for a single torrent -class torrent: +class torrent_info: def __init__(self, filename, save_dir, compact): self.filename = filename self.save_dir = save_dir self.compact = compact - self.user_paused = False # start out unpaused - self.uploaded_memory = 0 + self.user_paused = False # start out unpaused + self.uploaded_memory = 0 self.filter_out = [] @@ -112,11 +119,14 @@ class manager: self.constants = pytorrent_core.constants() # Unique IDs are NOT in the state, since they are temporary for each session - self.unique_IDs = {} # unique_ID -> a torrent object + self.unique_IDs = {} # unique_ID -> a torrent object, i.e. persistent data - # Saved torrent states. We do not poll the core in a costly manner, necessarily - self.saved_torrent_states = {} # unique_ID -> torrent_state - self.saved_torrent_states_timestamp = {} # time of creation + # Saved torrent core_states. We do not poll the core in a costly manner, necessarily + self.saved_torrent_core_states = {} # unique_ID -> torrent_state + self.saved_torrent_core_states_timestamp = {} # time of creation + + # supplementary torrent states + self.supp_torrent_states = {} # unique_ID->dict of data # Unpickle the preferences, or create a new one try: @@ -165,6 +175,15 @@ class manager: # Shutdown torrent core pytorrent_core.quit() + # This is the EXTERNAL function, for the GUI. It returns the core_state + supp_state + def get_torrent_state(self, unique_ID): + ret = self.get_torrent_core_state(unique_ID, True).copy() + + if self.get_supp_torrent_state(unique_ID) is not None: + ret.update(self.get_supp_torrent_state(unique_ID)) + + return ret + def get_pref(self, key): # If we have a value, return, else fallback on default_prefs, else raise an error # the fallback is useful if the source has newer prefs than the existing pref state, @@ -243,25 +262,6 @@ class manager: ret['DHT_nodes'] = pytorrent_core.get_DHT_info() return ret - # Efficient: use a saved state, if it hasn't expired yet - def get_torrent_state(self, unique_ID, efficiently = False): - if efficiently: - try: - if time.time() < self.saved_torrent_states_timestamp[unique_ID] + \ - TORRENT_STATE_EXPIRATION: - return self.saved_torrent_states[unique_ID] - except KeyError: - pass - - self.saved_torrent_states_timestamp[unique_ID] = time.time() - self.saved_torrent_states[unique_ID] = pytorrent_core.get_state(unique_ID) - - return self.saved_torrent_states[unique_ID] - - def mark_state_dirty(self, unique_ID): - del self.saved_torrent_states[unique_ID] - del self.saved_torrent_states_timestamp[unique_ID] - def queue_up(self, unique_ID): curr_index = self.get_queue_index(unique_ID) if curr_index > 0: @@ -284,7 +284,7 @@ class manager: def clear_completed(self): for unique_ID in self.unique_IDs: - torrent_state = self.get_torrent_state(unique_ID, True) + torrent_state = self.get_torrent_core_state(unique_ID) if torrent_state['progress'] == 100.0: self.remove_torrent_ns(unique_ID) @@ -304,13 +304,13 @@ class manager: # This should be called after changes to relevant parameters (user_pausing, or # altering max_active_torrents), or just from time to time # ___ALL queuing code should be in this function, and ONLY here___ - def apply_queue(self): + def apply_queue(self, efficient = True): # Handle autoseeding - downqueue as needed if self.auto_seed_ratio != -1: for unique_ID in self.unique_IDs: - if self.get_torrent_state(unique_ID, True)['is_seed']: - torrent_state = self.get_torrent_state(unique_ID, True) + if self.get_torrent_core_state(unique_ID, efficient)['is_seed']: + torrent_state = self.get_torrent_core_state(unique_ID, efficient) ratio = self.calc_ratio(unique_ID, torrent_state) if ratio >= self.auto_seed_ratio: self.queue_bottom(unique_ID) @@ -319,10 +319,10 @@ class manager: for index in range(len(self.state.queue)): unique_ID = self.state.queue[index] if (index < self.state.max_active_torrents or self.state_max_active_torrents == -1) \ - and self.get_torrent_state(unique_ID, True)['is_paused'] \ + and self.get_torrent_core_state(unique_ID, efficient)['is_paused'] \ and not self.is_user_paused(unique_ID): pytorrent_core.resume(unique_ID) - elif not self.get_torrent_state(unique_ID, True)['is_paused'] and \ + elif not self.get_torrent_core_state(unique_ID, efficient)['is_paused'] and \ (index >= self.state.max_active_torrents or self.is_user_paused(unique_ID)): pytorrent_core.pause(unique_ID) @@ -340,49 +340,69 @@ class manager: def get_num_torrents(self): return pytorrent_core.get_num_torrents() + # Handle them for the backend's purposes, but still send them up in case the client + # wants to do something - show messages, for example def handle_events(self): + ret = [] + event = pytorrent_core.pop_event() while event is not None: # print "EVENT: ", event + ret.append(event) + if event['event_type'] is self.constants['EVENT_FINISHED']: # If we are autoseeding, then we need to apply the queue if self.auto_seed_ratio == -1: - self.mark_state_dirty(event['unique_ID']) # So the queuing will be to new data - self.apply_queue() - - elif event['event_type'] is self.constants['EVENT_PEER_ERROR']: -# self.parent.addMessage(_("Peer Error") + ": " + str(event), "I") # Debug only! - pass - elif event['event_type'] is self.constants['EVENT_INVALID_REQUEST']: - print 'self.parent.addMessage(_("Invalid request") + ": " + str(event), "W") # Maybe "I"?' - elif event['event_type'] is self.constants['EVENT_FILE_ERROR']: -# dc.debugmsg("File error! " + str(event)) - print 'self.parent.addMessage(_("File error") + "! " + str(event), "F")' - elif event['event_type'] is self.constants['EVENT_HASH_FAILED_ERROR']: - print 'self.parent.addMessage(_("Hash failed") + ": " + str(event), "I")' - elif event['event_type'] is self.constants['EVENT_PEER_BAN_ERROR']: - print 'self.parent.addMessage(_("Peer banned") + ": " + str(event), "I")' - elif event['event_type'] is self.constants['EVENT_FASTRESUME_REJECTED_ERROR']: -# dc.debugmsg("Fastresume rejected: " + str(event)) - print 'self.parent.addMessage(_("Fastresume rejected") + ": " + str(event), "W")' + self.apply_queue(efficient = False) # To work on current data elif event['event_type'] is self.constants['EVENT_TRACKER']: - print event['tracker_status'], event['message'] - elif event['event_type'] is self.constants['EVENT_OTHER']: - print 'self.parent.addMessage(_("Event") + ": " + str(event), "W")' - else: -# dc.debugmsg("Internal error, undefined event type") -# dc.debugmsg("No such event error. Raw data: " + str(event)) - print 'self.parent.addMessage(_("Event") + ": " + str(event), "C")' + self.set_supp_torrent_state_val( event['unique_ID'], + "tracker_status", + event['tracker_status']) + self.set_supp_torrent_state_val( event['unique_ID'], + "tracker_message", + event['message']) event = pytorrent_core.pop_event() + return ret + #################### # Internal functions #################### + # Efficient: use a saved state, if it hasn't expired yet + def get_torrent_core_state(self, unique_ID, efficiently=True): + if efficiently: + try: + if time.time() < self.saved_torrent_core_states_timestamp[unique_ID] + \ + TORRENT_STATE_EXPIRATION: + return self.saved_torrent_core_states[unique_ID] + except KeyError: + pass + + self.saved_torrent_core_states_timestamp[unique_ID] = time.time() + self.saved_torrent_core_states[unique_ID] = pytorrent_core.get_torrent_state(unique_ID) + + return self.saved_torrent_core_states[unique_ID] + + def get_supp_torrent_state(self, unique_ID): + try: + return self.supp_torrent_states[unique_ID] + except KeyError: + return None + + def set_supp_torrent_state_val(self, unique_ID, key, val): + try: + if self.supp_torrent_states[unique_ID] is None: + self.supp_torrent_states[unique_ID] = {} + except KeyError: + self.supp_torrent_states[unique_ID] = {} + + self.supp_torrent_states[unique_ID][key] = val + # Non-syncing functions. Used when we loop over such events, and sync manually at the end def add_torrent_ns(self, filename, save_dir, compact): @@ -399,7 +419,7 @@ class manager: shutil.copy(filename, full_new_name) # Create torrent object - new_torrent = torrent(full_new_name, save_dir, compact) + new_torrent = torrent_info(full_new_name, save_dir, compact) self.state.torrents.append(new_torrent) def remove_torrent_ns(self, unique_ID): diff --git a/library/pytorrent_core.cpp b/library/pytorrent_core.cpp index 18ae0214c..43476fee1 100755 --- a/library/pytorrent_core.cpp +++ b/library/pytorrent_core.cpp @@ -567,7 +567,7 @@ static PyObject *torrent_get_torrent_info(PyObject *self, PyObject *args) ); } -static PyObject *torrent_get_state(PyObject *self, PyObject *args) +static PyObject *torrent_get_torrent_state(PyObject *self, PyObject *args) { python_long unique_ID; if (!PyArg_ParseTuple(args, "i", &unique_ID)) @@ -971,7 +971,7 @@ static PyObject *torrent_start_DHT(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &DHT_path)) return NULL; - printf("Loading DHT state from %s\r\n", DHT_path); +// printf("Loading DHT state from %s\r\n", DHT_path); boost::filesystem::path tempPath(DHT_path, empty_name_check); boost::filesystem::ifstream DHT_state_file(tempPath, std::ios_base::binary); @@ -982,10 +982,10 @@ static PyObject *torrent_start_DHT(PyObject *self, PyObject *args) DHT_state = bdecode(std::istream_iterator(DHT_state_file), std::istream_iterator()); M_ses->start_dht(DHT_state); - printf("DHT state recovered.\r\n"); +// printf("DHT state recovered.\r\n"); - // Print out the state data from the FILE (not the session!) - printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state)); +// // Print out the state data from the FILE (not the session!) +// printf("Number of DHT peers in recovered state: %ld\r\n", count_DHT_peers(DHT_state)); } catch (std::exception&) { printf("No DHT file to resume\r\n"); @@ -1008,14 +1008,14 @@ static PyObject *torrent_stop_DHT(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &DHT_path)) return NULL; - printf("Saving DHT state to %s\r\n", DHT_path); +// printf("Saving DHT state to %s\r\n", DHT_path); boost::filesystem::path tempPath = boost::filesystem::path(DHT_path, empty_name_check); try { entry DHT_state = M_ses->dht_state(); - printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state)); +// printf("Number of DHT peers in state, saving: %ld\r\n", count_DHT_peers(DHT_state)); boost::filesystem::ofstream out(tempPath, std::ios_base::binary); out.unsetf(std::ios_base::skipws); @@ -1185,7 +1185,7 @@ static PyMethodDef pytorrent_core_methods[] = { {"pause", torrent_pause, METH_VARARGS, "."}, {"resume", torrent_resume, METH_VARARGS, "."}, {"get_torrent_info", torrent_get_torrent_info, METH_VARARGS, "."}, - {"get_state", torrent_get_state, METH_VARARGS, "."}, + {"get_torrent_state", torrent_get_torrent_state, METH_VARARGS, "."}, {"pop_event", torrent_pop_event, METH_VARARGS, "."}, {"get_session_info", torrent_get_session_info, METH_VARARGS, "."}, {"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."}, diff --git a/library/test.py b/library/test.py index 8a25940d5..8a2c77149 100644 --- a/library/test.py +++ b/library/test.py @@ -16,7 +16,7 @@ import os manager = pytorrent.manager("PT", "0500", "pytorrent - testing only", os.path.expanduser("~") + "/Temp") -manager.set_pref('max_upload_rate', 6*1024) +#manager.set_pref('max_upload_rate', 6*1024) #my_torrent = manager.add_torrent("xubuntu-6.10-desktop-i386.iso.torrent", ".", True) @@ -34,4 +34,5 @@ try: print "" sleep(2) except KeyboardInterrupt: + print "Shutting down:" manager.quit()