* Added dump_torrent_info_file() to core to just list files in torrent by its

filename.
* Replaced all file filters in torrents with priorities. Nothing to see in UI
yet. Based on patch from eternalswd.
This commit is contained in:
Alex Dedul 2007-07-18 09:06:24 +00:00
parent 89972e1e54
commit 6a22d12975
5 changed files with 233 additions and 170 deletions

View file

@ -47,14 +47,13 @@
# time to calculate, so we do if efficiently
# 3. supp_torrent_state - supplementary torrent data, from Deluge
import deluge_core
import pickle
import os
import os.path
import shutil
import statvfs
import pickle
import time
import gettext
import deluge_core
import pref
# Constants
@ -167,8 +166,8 @@ class persistent_state:
self.torrents = []
# Prepare queue (queue is pickled, just like everything else)
self.queue = [] # queue[x] is the unique_ID of the x-th queue position. Simple.
# queue[x] is the unique_ID of the x-th queue position. Simple.
self.queue = []
# The manager for the torrent system
@ -236,15 +235,18 @@ class Manager:
# Unpickle the state, or create a new one
if not blank_slate:
try:
pkl_file = open(os.path.join(self.base_dir, STATE_FILENAME), 'rb')
pkl_file = open(os.path.join(self.base_dir, STATE_FILENAME),
'rb')
self.state = pickle.load(pkl_file)
pkl_file.close()
# Sync with the core: tell core about torrents, and get unique_IDs
# Sync with the core: tell core about torrents, and get
# unique_IDs
self.sync()
# Apply all the file filters, right after adding the torrents
self.apply_all_file_filters()
# Apply all the file priorities, right after adding the
# torrents
self.apply_all_file_priorities()
# Apply the queue at this time, after all is loaded and ready
self.apply_queue()
@ -262,10 +264,7 @@ class Manager:
self.config.save()
# Pickle the state
print "Pickling state..."
output = open(os.path.join(self.base_dir, STATE_FILENAME), 'wb')
pickle.dump(self.state, output)
output.close()
self.pickle_state()
# Stop DHT, if needed
self.set_DHT(False)
@ -278,6 +277,18 @@ class Manager:
print "Quitting the core..."
deluge_core.quit()
def pickle_state(self):
# Pickle the state so if we experience a crash, the latest state is
# available
print "Pickling state..."
#print self.state.torrents
#print self.state.queue
output = open(os.path.join(self.base_dir, STATE_FILENAME), 'wb')
pickle.dump(self.state, output)
output.close()
def pre_quitting(self):
# Save the uploaded data from this session to the existing upload memory
for unique_ID in self.unique_IDs.keys():
@ -306,6 +317,10 @@ class Manager:
if PREF_FUNCTIONS[key] is not None:
PREF_FUNCTIONS[key](value)
# Dump torrent info without adding
def dump_torrent_file_info(self, torrent):
return deluge_core.dump_file_info(torrent)
# Torrent addition and removal functions
def add_torrent(self, filename, save_dir, compact):
@ -393,14 +408,13 @@ class Manager:
# This is the EXTERNAL function, for the GUI. It returns the core_state + supp_state
def get_torrent_state(self, unique_ID):
# Check to see if unique_ID exists:
if self.state.queue.count(unique_ID) == 0:
if unique_ID not in self.state.queue:
raise InvalidUniqueIDError(_("Asked for a torrent that doesn't exist"))
ret = self.get_core_torrent_state(unique_ID, True).copy()
# Add the deluge-level things to the deluge_core data
if self.get_supp_torrent_state(unique_ID) is not None:
ret.update(self.get_supp_torrent_state(unique_ID))
ret.update(self.get_supp_torrent_state(unique_ID))
# Get queue position
ret['queue_pos'] = self.state.queue.index(unique_ID)
@ -604,30 +618,30 @@ class Manager:
return ret
# Filtering functions
def set_file_filter(self, unique_ID, file_filter):
assert(len(file_filter) == self.get_core_torrent_state(unique_ID, True)['num_files'])
self.unique_IDs[unique_ID].file_filter = file_filter[:]
deluge_core.set_filter_out(unique_ID, file_filter)
def get_file_filter(self, unique_ID):
try:
return self.unique_IDs[unique_ID].file_filter[:]
except AttributeError:
return None
# Priorities functions
def prioritize_files(self, unique_ID, priorities):
assert(len(priorities) == self.get_core_torrent_state(unique_ID,
True)['num_files'])
self.unique_IDs[unique_ID].priorities = priorities[:]
deluge_core.prioritize_files(unique_ID, priorities)
# Called when a session starts, to apply existing filters
def apply_all_file_filters(self):
def get_priorities(self, unique_ID):
try:
return self.unique_IDs[unique_ID].priorities[:]
except AttributeError:
# return normal priority for all files by default
num_files = self.get_core_torrent_state(unique_ID,
True)['num_files']
return [1] * num_files
# Called when a session starts, to apply existing priorities
def apply_all_file_priorities(self):
for unique_ID in self.unique_IDs.keys():
try:
self.set_file_filter(unique_ID, self.unique_IDs[unique_ID].file_filter)
self.prioritize_files(unique_ID,
self.get_priorities(unique_ID))
except AttributeError:
pass
@ -673,8 +687,9 @@ class Manager:
# Efficient: use a saved state, if it hasn't expired yet
def get_core_torrent_state(self, unique_ID, efficiently=True):
if unique_ID not in self.saved_core_torrent_states.keys():
self.saved_core_torrent_states[unique_ID] = cached_data(deluge_core.get_torrent_state, unique_ID)
if unique_ID not in self.saved_core_torrent_states:
self.saved_core_torrent_states[unique_ID] = \
cached_data(deluge_core.get_torrent_state, unique_ID)
return self.saved_core_torrent_states[unique_ID].get(efficiently)
@ -682,7 +697,7 @@ class Manager:
try:
return self.supp_torrent_states[unique_ID]
except KeyError:
return None
return {}
def set_supp_torrent_state_val(self, unique_ID, key, val):
try:
@ -754,19 +769,17 @@ class Manager:
no_space = False
# Add torrents to core and unique_IDs
torrents_with_unique_ID = self.unique_IDs.values()
for torrent in self.state.torrents:
if not os.path.exists(torrent.filename):
print "Missing file: %s" % torrent.filename
self.state.torrents.remove(torrent)
continue
if torrent not in torrents_with_unique_ID:
if torrent not in self.unique_IDs.values():
# print "Adding torrent to core:", torrent.filename, torrent.save_dir, torrent.compact
try:
unique_ID = deluge_core.add_torrent(torrent.filename,
torrent.save_dir,
torrent.compact)
torrent.save_dir,
torrent.compact)
except DelugeError, e:
print "Error:", e
self.state.torrents.remove(torrent)
@ -775,9 +788,7 @@ class Manager:
ret = unique_ID
self.unique_IDs[unique_ID] = torrent
# print torrents_with_unique_ID
# Remove torrents from core, unique_IDs and queue
to_delete = []
for unique_ID in self.unique_IDs.keys():
@ -800,8 +811,9 @@ class Manager:
# Add torrents to queue - at the end, of course
for unique_ID in self.unique_IDs.keys():
if unique_ID not in self.state.queue:
if (self.get_pref('queue_above_completed')) and len(self.state.queue) > 0:
for index in range(len(self.state.queue)):
if self.get_pref('queue_above_completed') and \
len(self.state.queue) > 0:
for index in xrange(len(self.state.queue)):
torrent_state = self.get_core_torrent_state(self.state.queue[index])
if torrent_state['progress'] == 1.0:
break
@ -827,11 +839,8 @@ class Manager:
#if no_space:
#self.apply_queue()
# Pickle the state so if we experience a crash, the latest state is available
print "Pickling state..."
output = open(os.path.join(self.base_dir, STATE_FILENAME), 'wb')
pickle.dump(self.state, output)
output.close()
self.pickle_state()
return ret

View file

@ -97,7 +97,6 @@ using namespace libtorrent;
//-----------------
typedef long unique_ID_t;
typedef std::vector<bool> filter_out_t;
typedef std::string torrent_name_t;
struct torrent_t
@ -179,6 +178,17 @@ long get_index_from_unique_ID(long unique_ID)
RAISE_INT(DelugeError, "No such unique_ID.");
}
torrent_info internal_dump_file_info(std::string const& torrent_name)
{
std::ifstream in(torrent_name.c_str(), std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
entry e;
e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
torrent_info t(e);
return t;
}
long internal_add_torrent(std::string const& torrent_name,
float preferred_ratio,
@ -512,6 +522,34 @@ static PyObject *torrent_set_max_connections(PyObject *self, PyObject *args)
Py_INCREF(Py_None); return Py_None;
}
static PyObject *torrent_dump_file_info(PyObject *self, PyObject *args)
{
const char *name;
if (!PyArg_ParseTuple(args, "s", &name))
return NULL;
torrent_info t = internal_dump_file_info(name);
PyObject *file_info;
long file_index = 0;
PyObject *ret = PyTuple_New(t.num_files());
for(torrent_info::file_iterator i = t.begin_files(); i != t.end_files(); ++i)
{
file_entry const &currFile = (*i);
file_info = Py_BuildValue(
"{s:s,s:L}",
"path", currFile.path.string().c_str(),
"size", currFile.size
);
PyTuple_SetItem(ret, file_index, file_info);
file_index++;
};
return ret;
}
static PyObject *torrent_add_torrent(PyObject *self, PyObject *args)
{
@ -1011,35 +1049,6 @@ static PyObject *torrent_get_file_info(PyObject *self, PyObject *args)
return ret;
};
static PyObject *torrent_set_filter_out(PyObject *self, PyObject *args)
{
python_long unique_ID;
PyObject *filter_out_object;
if (!PyArg_ParseTuple(args, "iO", &unique_ID, &filter_out_object))
return NULL;
long index = get_index_from_unique_ID(unique_ID);
if (PyErr_Occurred())
return NULL;
torrent_t &t = M_torrents->at(index);
long num_files = t.handle.get_torrent_info().num_files();
assert(PyList_Size(filter_out_object) == num_files);
filter_out_t filter_out(num_files);
for (long i = 0; i < num_files; i++)
{
filter_out.at(i) =
PyInt_AsLong(PyList_GetItem(filter_out_object, i));
};
t.handle.filter_files(filter_out);
Py_INCREF(Py_None); return Py_None;
}
/*static PyObject *torrent_get_unique_IDs(PyObject *self, PyObject *args)
{
PyObject *ret = PyTuple_New(M_torrents.size());
@ -1521,7 +1530,7 @@ static PyMethodDef deluge_core_methods[] =
{"get_session_info", torrent_get_session_info, METH_VARARGS, "."},
{"get_peer_info", torrent_get_peer_info, METH_VARARGS, "."},
{"get_file_info", torrent_get_file_info, METH_VARARGS, "."},
{"set_filter_out", torrent_set_filter_out, METH_VARARGS, "."},
{"dump_file_info", torrent_dump_file_info, METH_VARARGS, "."},
{"constants", torrent_constants, METH_VARARGS, "."},
{"start_DHT", torrent_start_DHT, METH_VARARGS, "."},
{"stop_DHT", torrent_stop_DHT, METH_VARARGS, "."},

View file

@ -202,22 +202,18 @@ class PreferencesDlg:
self.glade.get_widget('txt_tray_passwd').set_sensitive(value)
class FilesDlg:
def __init__(self, manager, unique_id):
def __init__(self, dumped_torrent):
self.glade = gtk.glade.XML(common.get_glade_file("files_dialog.glade"),
domain='deluge')
self.dialog = self.glade.get_widget("file_dialog")
self.dialog.set_icon_from_file(common.get_pixmap("deluge32.png"))
self.file_view = self.glade.get_widget("file_view")
self.manager = manager
self.unique_id = unique_id
self.files_manager = files.FilesManager(manager, False)
self.files_manager = files.FilesDialogManager(dumped_torrent)
self.files_manager.build_file_view(self.file_view)
self.files_manager.prepare_file_store()
def show(self):
self.files_manager.use_unique_id(self.unique_id)
self.files_manager.prepare_store()
#clear private setting
self.glade.get_widget("chk_setpriv").set_active(False)
@ -229,6 +225,9 @@ class FilesDlg:
self.manager.set_priv(self.unique_id, True)
return r
def get_priorities(self):
return self.files_manager.get_priorities()
class PluginDlg:
def __init__(self, plugins):

View file

@ -36,10 +36,10 @@ import common
from itertools import izip
import gobject
class FilesManager:
def __init__(self, manager, is_file_tab):
self.manager = manager
self.file_glade = gtk.glade.XML(common.get_glade_file("file_tab_menu.glade"), domain='deluge')
class FilesBaseManager(object):
def __init__(self, file_store):
self.file_glade = gtk.glade.XML(common.get_glade_file("file_tab_menu.glade"),
domain='deluge')
self.file_menu = self.file_glade.get_widget("file_tab_menu")
self.file_glade.signal_autoconnect({
"select_all": self.file_select_all,
@ -47,33 +47,25 @@ class FilesManager:
"check_selected": self.file_check_selected,
"uncheck_selected": self.file_uncheck_selected,
})
self.file_unique_id = -1
self.is_file_tab = is_file_tab
# Stores file path -> gtk.TreeIter's iter mapping for quick look up
# in self.update_torrent_info_widget
self.file_store_dict = {}
if self.is_file_tab:
self.file_store = gtk.ListStore(bool, str, gobject.TYPE_UINT64, float)
else:
self.file_store = gtk.ListStore(bool, str, gobject.TYPE_UINT64)
self.file_store = file_store
# We need file_store_sorted so original file_store keeps unchanged
# when file_view is sorted. And from file_store we have to pass
# files priorities to manager.prioritize_files() in the exact same
# order as we get files from manager.get_torrent_file_info()
self.file_store_sorted = gtk.TreeModelSort(self.file_store)
def use_unique_id(self, unique_id):
self.file_unique_id = unique_id
def build_file_view(self, file_view):
self.file_view = file_view
def percent(column, cell, model, iter, data):
percent = float(model.get_value(iter, data))
percent_str = "%.2f%%"%percent
cell.set_property("text", percent_str)
self.file_selected = []
self.toggle_column = dgtk.add_toggle_column(self.file_view, _("Download"), 0, toggled_signal=self.file_toggled)
self.toggle_column = dgtk.add_toggle_column(self.file_view,
_("Priority"), 0,
toggled_signal=self.file_toggled)
self.filename_column = dgtk.add_text_column(self.file_view, _("Filename"), 1)
self.filename_column.set_expand(True)
self.size_column = dgtk.add_func_column(self.file_view, _("Size"), dgtk.cell_data_size, 2)
if self.is_file_tab:
dgtk.add_func_column(self.file_view, _("Progress"), percent, 3)
self.file_view.set_model(self.file_store_sorted)
self.file_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.file_view.get_selection().set_select_function(self.file_clicked)
@ -82,33 +74,10 @@ class FilesManager:
def clear_file_store(self):
self.file_store.clear()
self.file_store_dict = {}
def prepare_store(self):
if not self.file_store_dict:
all_files = self.manager.get_torrent_file_info(self.file_unique_id)
file_filter = self.manager.get_file_filter(self.file_unique_id)
if file_filter is None:
file_filter = [False] * len(all_files)
if self.is_file_tab:
for file, filt in izip(all_files, file_filter):
iter = self.file_store.append([not filt, file['path'],
file['size'],
round(file['progress'], 2)])
self.file_store_dict[file['path']] = iter
else:
for file, filt in izip(all_files, file_filter):
iter = self.file_store.append([not filt, file['path'],
file['size']])
self.file_store_dict[file['path']] = iter
def update_store(self):
new_file_info = self.manager.get_torrent_file_info(self.file_unique_id)
for file in new_file_info:
iter = self.file_store_dict[file['path']]
if self.file_store.get_value(iter, 3) != round(file['progress'], 2):
self.file_store.set(iter, 3, file['progress'])
def prepare_file_store(self):
pass
def file_select_all(self, widget):
self.file_view.get_selection().select_all()
@ -117,11 +86,11 @@ class FilesManager:
def file_check_selected(self, widget):
self.file_view.get_selection().selected_foreach(self.file_toggle_selected, True)
self.file_toggled_update_filter()
self.file_toggled_update_priorities()
def file_uncheck_selected(self, widget):
self.file_view.get_selection().selected_foreach(self.file_toggle_selected, False)
self.file_toggled_update_filter()
self.file_toggled_update_priorities()
def file_clicked(self, path):
return not self.file_selected
@ -137,22 +106,97 @@ class FilesManager:
def file_toggle_selected(self, treemodel, path, selected_iter, value):
child_iter = self.file_store_sorted.convert_iter_to_child_iter(None,
selected_iter)
self.file_store_sorted.get_model().set_value(child_iter, 0, value)
if value:
new_value = 1
else:
new_value = 0
self.file_store_sorted.get_model().set_value(child_iter, 0, new_value)
def file_toggled(self, renderer, path):
self.file_selected = True
file_iter = self.file_store_sorted.get_iter_from_string(path)
value = not renderer.get_active()
if value:
new_value = 1
else:
new_value = 0
file_iter = self.file_store_sorted.get_iter_from_string(path)
selection = self.file_view.get_selection()
if selection.iter_is_selected(file_iter):
selection.selected_foreach(self.file_toggle_selected, value)
else:
child_iter = self.file_store_sorted.convert_iter_to_child_iter(
None, file_iter)
self.file_store_sorted.get_model().set_value(child_iter, 0, value)
self.file_store_sorted.get_model().set_value(child_iter, 0,
new_value)
self.file_toggled_update_priorities()
def file_toggled_update_priorities(self):
pass
class FilesTabManager(FilesBaseManager):
def __init__(self, manager):
file_store = gtk.ListStore(int, str, gobject.TYPE_UINT64, float)
self.file_toggled_update_filter()
super(FilesTabManager, self).__init__(file_store)
self.manager = manager
self.file_unique_id = None
def file_toggled_update_filter(self):
file_filter = [not x[0] for x in self.file_store]
self.manager.set_file_filter(self.file_unique_id, file_filter)
# Stores file path -> gtk.TreeIter's iter mapping for quick look up
# in self.update_file_store()
self.file_store_dict = {}
def build_file_view(self, file_view):
super(FilesTabManager, self).build_file_view(file_view)
def percent(column, cell, model, iter, data):
percent = float(model.get_value(iter, data))
percent_str = "%.2f%%"%percent
cell.set_property("text", percent_str)
dgtk.add_func_column(file_view, _("Progress"), percent, 3)
def set_unique_id(self, unique_id):
self.file_unique_id = unique_id
def prepare_file_store(self):
if not self.file_store_dict:
all_files = self.manager.get_torrent_file_info(self.file_unique_id)
file_priorities = self.manager.get_priorities(self.file_unique_id)
for file, priority in izip(all_files, file_priorities):
iter = self.file_store.append([priority, file['path'],
file['size'],
round(file['progress'], 2)])
self.file_store_dict[file['path']] = iter
def update_file_store(self):
new_file_info = self.manager.get_torrent_file_info(self.file_unique_id)
for file in new_file_info:
iter = self.file_store_dict[file['path']]
if self.file_store.get_value(iter, 3) != round(file['progress'], 2):
self.file_store.set(iter, 3, file['progress'])
def file_toggled_update_priorities(self):
file_priorities = []
for x in self.file_store:
file_priorities.append(x[0])
self.manager.prioritize_files(self.file_unique_id, file_priorities)
class FilesDialogManager(FilesBaseManager):
def __init__(self, dumped_torrent):
file_store = gtk.ListStore(int, str, gobject.TYPE_UINT64)
super(FilesDialogManager, self).__init__(file_store)
self.dumped_torrent = dumped_torrent
def prepare_file_store(self):
for file in self.dumped_torrent:
self.file_store.append([1, file['path'], file['size']])
def get_priorities(self):
file_priorities = []
for x in self.file_store:
file_priorities.append(x[0])
return file_priorities

View file

@ -63,7 +63,7 @@ class DelugeGTK:
#Start the Deluge Manager:
self.manager = core.Manager(common.CLIENT_CODE, common.CLIENT_VERSION,
'%s %s'%(common.PROGRAM_NAME, common.PROGRAM_VERSION), common.CONFIG_DIR)
self.files_for_tab = files.FilesManager(self.manager, True)
self.files_tab = files.FilesTabManager(self.manager)
self.plugins = plugins.PluginManager(self.manager, self)
self.plugins.add_plugin_dir(common.PLUGIN_DIR)
if os.path.isdir(os.path.join(common.CONFIG_DIR , 'plugins')):
@ -636,10 +636,10 @@ class DelugeGTK:
self.peer_store_dict = {}
def build_file_tab(self):
self.files_for_tab.build_file_view(self.wtree.get_widget("file_view"))
self.files_tab.build_file_view(self.wtree.get_widget("file_view"))
def clear_file_store(self):
self.files_for_tab.clear_file_store()
self.files_tab.clear_file_store()
def show_about_dialog(self, arg=None):
dialogs.show_about_dialog()
@ -1019,9 +1019,9 @@ class DelugeGTK:
elif page_num == 2: # Files
# Fill self.file_store with files only once and only when we click to
# Files tab or it's already open
self.files_for_tab.use_unique_id(unique_id)
self.files_for_tab.prepare_store()
self.files_for_tab.update_store()
self.files_tab.set_unique_id(unique_id)
self.files_tab.prepare_file_store()
self.files_tab.update_file_store()
def calc_share_ratio(self, unique_id, torrent_state):
@ -1083,7 +1083,20 @@ class DelugeGTK:
return
try:
unique_id = self.manager.add_torrent(torrent, path, self.config.get('use_compact_storage'))
dumped_torrent = self.manager.dump_torrent_file_info(torrent)
if self.config.get('enable_files_dialog') and \
len(dumped_torrent) > 1:
files_dialog = dialogs.FilesDlg(dumped_torrent)
if files_dialog.show() == 1:
unique_id = self.manager.add_torrent(torrent, path,
self.config.get('use_compact_storage'))
self.manager.prioritize_files(unique_id,
files_dialog.get_priorities())
else:
return
else:
unique_id = self.manager.add_torrent(torrent, path,
self.config.get('use_compact_storage'))
except core.InvalidEncodingError, e:
print "InvalidEncodingError", e
dialogs.show_popup_warning(self.window, _("An error occured while trying to add the torrent. It's possible your .torrent file is corrupted."))
@ -1095,19 +1108,8 @@ class DelugeGTK:
dialogs.show_popup_warning(self.window, _("There is not enough free disk space to complete your download.") + "\n" + \
_("Space Needed:") + " " + nice_need + "\n" + \
_("Available Space:") + " " + nice_free)
else:
num_files = len(self.manager.get_torrent_file_info(unique_id))
if self.config.get('enable_files_dialog') and num_files > 1:
self.manager.set_user_pause(unique_id, True)
files_dialog = dialogs.FilesDlg(self.manager, unique_id)
if files_dialog.show() == 1:
self.manager.set_user_pause(unique_id, False)
self.torrent_model_append(unique_id)
else:
self.manager.remove_torrent(unique_id, True, True)
else:
self.torrent_model_append(unique_id)
self.torrent_model_append(unique_id)
def launchpad(self, obj=None):
common.open_url_in_browser('https://translations.launchpad.net/deluge/trunk/+pots/deluge')