diff --git a/deluge/plugins/label/label/__init__.py b/deluge/plugins/label/label/__init__.py new file mode 100644 index 000000000..921605c49 --- /dev/null +++ b/deluge/plugins/label/label/__init__.py @@ -0,0 +1,62 @@ +# +# __init__.py +# +# Copyright (C) 2008 Martijn Voncken +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +from deluge.log import LOG as log +from deluge.plugins.init import PluginBase + +class CorePlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + # Load the Core portion of the plugin + try: + from core import Core + self.plugin = Core(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a Core plugin: %s", e) + +class WebUIPlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + try: + from webui import WebUI + self.plugin = WebUI(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a WebUI plugin: %s", e) + +class GtkUIPlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + # Load the GtkUI portion of the plugin + try: + from gtkui import GtkUI + self.plugin = GtkUI(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a GtkUI plugin: %s", e) + diff --git a/deluge/plugins/label/label/core.py b/deluge/plugins/label/label/core.py new file mode 100644 index 000000000..e72e93332 --- /dev/null +++ b/deluge/plugins/label/label/core.py @@ -0,0 +1,366 @@ +# +# Copyright (C) 2008 Martijn Voncken +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +""" +torrent-label core plugin. +adds a status field for tracker. +""" + +from deluge.log import LOG as log +from deluge.plugins.corepluginbase import CorePluginBase +from deluge.configmanager import ConfigManager + +from urlparse import urlparse + +KNOWN_STATES = ['Downloading','Seeding','Paused','Checking','Allocating','Queued','Error'] +STATE = "state" +TRACKER = "tracker" +KEYWORD = "keyword" +LABEL = "label" +CONFIG_DEFAULTS = { + "torrent_labels":{}, #torrent_id:label_id + "labels":{}, #label_id:{name:value} + "hide_zero_hits":False, + "gtk_alfa":False +} +OPTIONS_KEYS = ["max_download_speed", "max_upload_speed", + "max_connections", "max_upload_slots", "prioritize_first_last"] +NO_LABEL = "No Label" + +import traceback + +class Core(CorePluginBase): + def enable(self): + log.info("*** Start Label plugin ***") + + self.plugin.register_status_field("tracker_host", self._status_get_tracker) + self.plugin.register_status_field("label", self._status_get_label) + + #__init__ + core = self.plugin.get_core() + self.config = ConfigManager("label.conf") + self.core_cfg = ConfigManager("core.conf") + self.set_config_defaults() + + #reduce typing, assigning some values to self... + self.torrents = core.torrents.torrents + self.labels = self.config.get("labels") + self.torrent_labels = self.config.get("torrent_labels") + + log.debug("Label plugin enabled..") + + def clean_config(self): + "remove invalid data from config-file" + for torrent_id, label_id in list(self.torrent_labels.iteritems()): + if (not label_id in self.labels) or (not torrent_id in self.torrents): + log.debug("label: rm %s:%s" % (torrent_id,label_id)) + del self.torrent_labels[torrent_id] + + def save_config(self): + self.clean_config() + self.config.save() + + def set_config_defaults(self): + changed = False + for key, value in CONFIG_DEFAULTS.iteritems(): + if not key in self.config.config: + self.config.config[key] = value + changed = True + if changed: + pass + #self.config.save() + log.debug("label_config=%s" % self.config.config) + + def disable(self): + # De-register the label field + self.plugin.deregister_status_field("tracker_host") + self.plugin.deregister_status_field("label") + + def update(self): + pass + + ## Utils ## + def get_tracker(self, torrent): + """ + returns 1st tracker hostname + save space: reduced to *.com without any subdomain dots before + TODO: CLEANUP + """ + log.debug(torrent) + log.debug(torrent.trackers) + if not torrent.trackers: + return 'tracker-less' + url = urlparse(torrent.trackers[0]['url']) + if hasattr(url,'hostname'): + host = (url.hostname or 'unknown?') + parts = host.split(".") + if len(parts) > 2: + host = ".".join(parts[-2:]) + return host + return 'No-tracker?' + + ## Filters ## + def filter_state(self, torrents, value): + "in/out: a list of torrent objects." + log.debug("filter-state:%s" % value) + for t in torrents: + log.debug("s=%s" % t.state) + return [t for t in torrents if t.state == value] + + def filter_tracker(self, torrents, value): + "in/out: a list of torrent objects." + return [t for t in torrents if self.get_tracker(t) == value] + + def filter_label(self, torrents, value): + "in/out: a list of torrent objects." + if value == NO_LABEL: + value = None + log.debug("NO_LABEL") + return [t for t in torrents if self.torrent_labels.get(t.torrent_id) == value] + + def filter_keyword(self, torrents, value): + value = value.lower().strip() + "in/out: a list of torrent objects." + return [t for t in torrents if value in t.filename.lower()] + + ## Items ## + def get_state_filter_items(self): + states = dict([(state, 0) for state in KNOWN_STATES]) + state_order = list(KNOWN_STATES) + #state-simple: + for t in self.torrents.values(): + if not t.state in state_order: + state_order.append(t.state) + states[t.state] = 0 + states[t.state] +=1 + #specialized-state: + #todo: traffic. + + log.debug("hide-z:%s" % self.config["hide_zero_hits"]) + if self.config["hide_zero_hits"]: + for state in set(KNOWN_STATES): + log.debug(states.keys()) + if states[state] == 0 : + #del states[state] + state_order.remove(state) + + #return the filters sorted by STATES + add unknown states. + return ([("All",len(self.torrents))] + + [(state, states[state]) for state in state_order] + ) + + def get_tracker_filter_items(self): + #trackers: + trackers = {} + for t in self.torrents.values(): + tracker = self.get_tracker(t) + if not tracker in trackers: + trackers[tracker] = 0 + trackers[tracker] +=1 + + return [(tracker , trackers[tracker]) for tracker in sorted(trackers.keys())] + + def get_label_filter_items(self): + no_label = 0 + labels = dict([(label_id, 0) for label_id in self.labels]) + for torrent_id in self.torrents: + label_id = self.torrent_labels.get(torrent_id) + if label_id: + labels[label_id] +=1 + else: + no_label +=1 + + if self.config["hide_zero_hits"]: + for label , count in list(labels.iteritems()): + if count == 0: + del labels[label] + + return [(NO_LABEL, no_label)] + [(label_id, labels[label_id]) for label_id in sorted(labels.keys())] + + ## Public ## + def export_filter_items(self): + """ + returns : + { + "CATEGORY" : [("filter_value",count), ...] , ... + } + -- + category's : ["state","tracker","label"] + """ + result = {} + + result[STATE] = self.get_state_filter_items() + result[TRACKER] = self.get_tracker_filter_items() + result[LABEL] = self.get_label_filter_items() + + return result + + def export_get_labels(self): + return sorted(self.labels.keys()) + + def export_get_filtered_ids(self, filter_dict): + """ + input : {"filter_cat":"filter_value",..} + returns : a list of torrent_id's + """ + torrents = self.torrents.values() + if KEYWORD in filter_dict: + torrents = self.filter_keyword(torrents, filter_dict[KEYWORD]) + + if STATE in filter_dict and filter_dict[STATE] <> "": + torrents = self.filter_state(torrents, filter_dict[STATE]) + + if TRACKER in filter_dict: + torrents = self.filter_tracker(torrents, filter_dict[TRACKER]) + + if LABEL in filter_dict: + torrents = self.filter_label(torrents, filter_dict[LABEL]) + return [t.torrent_id for t in torrents] + + + #Labels: + def export_add(self, label_id): + """add a label + see label_set_options for more options. + """ + assert label_id + assert not (label_id in self.labels) + + #default to current global per-torrent settings. + self.labels[label_id] = { + "max_download_speed":self.core_cfg.config["max_download_speed_per_torrent"], + "max_upload_speed":self.core_cfg.config["max_upload_speed_per_torrent"], + "max_connections":self.core_cfg.config["max_connections_per_torrent"], + "max_upload_slots":self.core_cfg.config["max_upload_slots_per_torrent"], + "prioritize_first_last":self.core_cfg.config["prioritize_first_last_pieces"] + } + + def export_remove(self, label_id): + "remove a label" + assert label_id in self.labels + del self.labels[label_id] + self.clean_config() + self.config.save() + + def export_set_options(self, label_id, options_dict , apply = False): + """update the label options + + options_dict : + {"max_download_speed":float(), + "max_upload_speed":float(), + "max_connections":int(), + "max_upload_slots":int(), + "prioritize_first_last":bool(), + } + + apply : applies download-options to all torrents currently labelled by label_id + """ + assert label_id in self.labels + for key in options_dict.keys(): + if not key in OPTIONS_KEYS: + raise Exception("label: Invalid options_dict key:%s" % key) + + self.labels[label_id].update(options_dict) + + options = self.labels[label_id] + if apply: + for torrent_id,label in self.torrent_labels.iteritems(): + if label_id == label: + torrent = self.torrents[torrent_id] + torrent.set_max_download_speed(options["max_download_speed"]) + torrent.set_max_upload_speed(options["max_upload_speed"]) + torrent.set_max_connections(options["max_connections"]) + torrent.set_max_upload_slots(options["max_upload_slots"]) + torrent.set_prioritize_first_last(options["prioritize_first_last"]) + + self.config.save() + + def export_get_options(self, label_id): + """returns the label options""" + return self.labels[label_id] + + def export_set_torrent(self, torrent_id , label_id): + """ + assign a label to a torrent + removes a label if the label_id parameter is empty. + """ + log.debug(torrent_id) + log.debug(self.torrents.keys()) + assert (not label_id) or (label_id in self.labels) + assert torrent_id in self.torrents + + if not label_id: + if label_id in self.labels: + del self.torrent_labels[torrent_id] + self.clean_config() + else: + self.torrent_labels[torrent_id] = label_id + #set speeds, etc: + options = self.labels[label_id] + torrent = self.torrents[torrent_id] + torrent.set_max_download_speed(options["max_download_speed"]) + torrent.set_max_upload_speed(options["max_upload_speed"]) + torrent.set_max_connections(options["max_connections"]) + torrent.set_max_upload_slots(options["max_upload_slots"]) + torrent.set_prioritize_first_last(options["prioritize_first_last"]) + + self.config.save() + + + def export_get_global_options(self): + "see : label_set_global_options" + return { + "hide_zero_hits":self.config.get("hide_zero_hits"), + "gtk_alfa":self.config.get("gtk_alfa") + } + + def export_set_global_options(self, options): + """global_options: + { + "hide_zero":bool() #label_filter_items only returns items with more than 0 hits. + } + """ + for key in ["hide_zero_hits", "gtk_alfa"]: + if options.has_key(key): + self.config.set(key, key) + self.config.save() + + + ## Status fields ## + def _status_get_tracker(self, torrent_id): + return self.get_tracker(self.torrents[torrent_id]) + + def _status_get_label(self, torrent_id): + return self.torrent_labels.get(torrent_id) or "" + +if __name__ == "__main__": + import test + diff --git a/deluge/plugins/label/label/gtkui.py b/deluge/plugins/label/label/gtkui.py new file mode 100644 index 000000000..8ce7634bc --- /dev/null +++ b/deluge/plugins/label/label/gtkui.py @@ -0,0 +1,327 @@ +# +# blocklist/gtkui.py +# +# Copyright (C) 2007 Andrew Resch ('andar') +# Copyright (C) 2008 Mark Stahler ('kramed') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import os +import pkg_resources # access plugin egg +from deluge.log import LOG as log +from deluge import component # for systray +import ui +import gtk, gobject +from deluge.ui.client import aclient + +from deluge.configmanager import ConfigManager +config = ConfigManager("label.conf") +GTK_ALFA = config.get("gtk_alfa") + + +class GtkUI(ui.UI): + def __init__(self, plugin_api, plugin_name): + log.debug("Calling UI init") + # Call UI constructor + ui.UI.__init__(self, plugin_api, plugin_name) + log.debug("Label GtkUI plugin initalized..") + + def enable(self): + self.plugin.register_hook("on_apply_prefs", self.apply_prefs) + self.load_interface() + + def disable(self): + #deluge.component.get("StatusBar").remove_item(self.blocklist_status) + self.plugin.deregister_hook("on_apply_prefs", self.apply_prefs) + self.plugin.remove_preferences_page("Label") + + def get_pixmap(self, fname): + """Returns a pixmap file included with plugin""" + return pkg_resources.resource_filename("blocklist", os.path.join("data", fname)) + + def unload_interface(self): + self.plugin.remove_preferences_page("Label") + + def load_interface(self): + if not GTK_ALFA: + self.plugin.add_preferences_page("Label", gtk.Label( + "Sorry, the Gtk UI for the Label-plugin is still in development.")) + return + + log.debug("add items to torrentview-popup menu.") + + + torrentmenu = component.get("MenuBar").torrentmenu + + + self.label_menu = LabelMenu() + torrentmenu.append(self.label_menu) + self.label_menu.show_all() + + log.debug("add label column") + #TODO! + log.debug("Beginning gtk pane initialization") + self.config_frame = LabelConfigFrame() + + self.blocklist_pref_page = gtk.VBox() + self.blocklist_pref_page.set_spacing(6) + + self.blocklist_pref_page.pack_start(self.config_frame,True,True) + + # Add preferences page to preferences page + log.debug('Adding Label Preferences page') + self.plugin.add_preferences_page("Label", self.blocklist_pref_page) + + # Load settings from config and fill widgets with settings + self.fetch_prefs() + #log.debug('Finished loading Label preferences') + + + def fetch_prefs(self): # Fetch settings dictionary from plugin core and pass it to GTK ui settings + log.info('LABEL: Fetching and loading Preferences via GTK ui') + #aclient.block_list_get_options(self.callback_load_prefs) + self.config_frame.load_settings() + + def apply_prefs(self): + log.info('Blocklist: Preferences saved via Gtk ui') + """settings_dict = { + "url": self.url.get_text(), + "listtype": self.get_ltype(), + "check_after_days": self.check_after_days.get_value_as_int(), + "load_on_start":self.load_on_start.get_active(), + "try_times": self.try_times.get_value_as_int(), + "timeout": self.timeout.get_value_as_int() + } + aclient.block_list_set_options(None, settings_dict) + # Needs to go in another thread or wait until window is closed + #gobject.idle_add(self.call_critical_setting) + """ + + # GTK Gui Callback functions + def callback_load_prefs(self, dict): + self.config_frame.load_settings() + log.info('Blocklist: Callback Load Prefs GTK ui') + """self.settings_url(dict['url']) + self.settings_listtype(dict['listtype']) + self.settings_load(dict['load_on_start']) + self.settings_check_after_days(dict['check_after_days']) + self.settings_timeout(dict['timeout']) + self.settings_try_times(dict['try_times']) + """ + +def cb_none(args): + "hack for empty callbacks." + pass + +""" +class LabelList(gtk.TreeView): + "a simple listbox is way too hard in gtk :(." + def __init__(self): + pass +""" + +class LabelMenu(gtk.MenuItem): + def __init__(self): + gtk.MenuItem.__init__(self, "Label (wip)") + self.show_all() + + #attach.. + torrentmenu = component.get("MenuBar").torrentmenu + torrentmenu.connect("show", self.on_show, None) + + def get_torrent_ids(self): + return component.get("TorrentView").get_selected_torrents() + + def on_show(self, widget=None, data=None): + log.debug("label-on-show") + aclient.label_get_labels(self.cb_labels) + aclient.force_call(block=True) + + def cb_labels(self , labels): + log.debug("cb_labels-start") + self.sub_menu = gtk.Menu() + for label in labels: + item = gtk.MenuItem(label) + item.connect("activate", self.on_select_label, label) + self.sub_menu.append(item) + self.set_submenu(self.sub_menu) + self.show_all() + log.debug("cb_labels-end") + + def on_select_label(self, widget=None, label_id = None): + log.debug("select label:%s,%s" % (label_id ,self.get_torrent_ids()) ) + for torrent_id in self.get_torrent_ids(): + aclient.label_set_torrent(cb_none, torrent_id, label_id) + #aclient.force_call(block=True) + +class LabelConfigFrame(gtk.Frame): + def __init__(self): + gtk.Frame.__init__(self) + self.build_label_view() + self.build_label_options() + self.build_ui() + self.labels = [] + + def load_settings(self ,widget=None ,data=None): + aclient.label_get_labels(self.cb_update_labels) + + def cb_update_labels(self, labels): + self.labels = labels + self.label_store.clear() + for label in labels: + self.label_store.append([label]) + + def on_add(self, widget, data=None): + label = self.txt_add.get_text() + self.txt_add.set_text("") + if label in self.labels: + return + aclient.label_add(cb_none, label) + aclient.label_get_labels(self.cb_update_labels) + self.select_label(label) + + #aclient.force_call(block=True) + + def on_remove(self, widget, data=None): + label = self.get_selected_label() + aclient.label_remove(cb_none, label) + aclient.label_get_labels(self.cb_update_labels) + self.select_label(0) + + def get_selected_label(self): + model , iter = self.label_view.get_selection().get_selected() + return self.label_store.get_value(iter,0) + + def select_label(self, label): + aclient.force_call(block=True) #sync.. + if label: + it = self.label_store.iter_nth_child(None,self.labels.index(label)) + else: + it = self.label_store.iter_nth_child(None,0) + self.label_view.get_selection().select_iter(it) + + def build_label_view(self): + "gtk should have a simple listbox widget..." + self.label_store = gtk.ListStore(str) + + column = gtk.TreeViewColumn(_("Label")) + renderer = gtk.CellRendererText() + column.pack_start(renderer) + column.set_attributes(renderer, text = 0) + + self.label_view = gtk.TreeView(self.label_store) + self.label_view.append_column(column) + self.label_view.set_headers_visible(False) + + #self.label_scroll = gtk.ScrolledWindow() + #self.label_scroll.add_with_viewport(self.label_view) + + + def build_label_options(self): + self.label_options = gtk.Label("Per label options") + + def build_ui(self): + #vbox + #general + #[x] stuff + #labels: + #hpaned : left-right + #right: listbox with labels + #left: label-options + #--- + #label-add + #-- + + label = gtk.Label() + label.set_markup('' + _('General') + '') + + + self.set_shadow_type(gtk.SHADOW_NONE) + self.set_label_widget(label) + + self.btn_load = gtk.Button("Load Settings") + self.btn_load.connect("clicked", self.load_settings, None) + + self.btn_remove = gtk.Button("Remove") + self.btn_remove.connect("clicked", self.on_remove, None) + + vb = gtk.VBox() + self.add(vb) + vb.add(self.btn_load) + vb.add(gtk.Label("Label is in developent, you're testing pre-alfa!!!")) + + + self.hide_zero_hits = gtk.CheckButton(_('Hide Zero Hits')) + vb.add(self.hide_zero_hits) + + label = gtk.Label() + label.set_markup('' + _('Labels') + '') + vb.add(label) + + hp = gtk.HPaned() + hp.add1(self.label_view) + + + hp.add2(self.label_options) + + hp.set_position(100) + + hp.set_size_request(400, 200) #bug.. + + + hbAdd = gtk.HBox() + hbAdd.add(gtk.Label("Label:")) + self.txt_add = gtk.Entry() + hbAdd.add(self.txt_add) + btn_add = gtk.Button("Add") + hbAdd.add(btn_add) + btn_add.connect("clicked", self.on_add, None) + + vb.pack_end(hbAdd) + + label = gtk.Label() + label.set_markup('' + _('Add') + '') + + + + vb.pack_end(label) + + vb.pack_end(self.btn_remove, True , True) + + vb.pack_end(hp,True , True) + + + + + + + + + + diff --git a/deluge/plugins/label/label/template/part_label_filters.html b/deluge/plugins/label/label/template/part_label_filters.html new file mode 100644 index 000000000..62272ea3b --- /dev/null +++ b/deluge/plugins/label/label/template/part_label_filters.html @@ -0,0 +1,53 @@ +$def with (filter_items) + +
+ + + +
+
+
$_('Keyword')
+ + +$if get('filter_cat') == "keyword": + $if get('filter_value'): + $_('Clear') + +

+ + +$for cat in ["state", "tracker", "label"]: +
$cat
+ +
+
+ + + + diff --git a/deluge/plugins/label/label/template/torrent_label.html b/deluge/plugins/label/label/template/torrent_label.html new file mode 100644 index 000000000..f17404c2f --- /dev/null +++ b/deluge/plugins/label/label/template/torrent_label.html @@ -0,0 +1,29 @@ +$def with (torrent_ids, torrent_list, labels) +$:render.header(_("Label torrent")) +
+
+
+ +

$_("Label torrent")

+
    +$for torrent in torrent_list: +
  • $torrent.name ($torrent.label)
  • +
+ +
+ + +
+
+ + +
+
+
+
+$:render.footer() \ No newline at end of file diff --git a/deluge/plugins/label/label/test.py b/deluge/plugins/label/label/test.py new file mode 100644 index 000000000..a112d3b53 --- /dev/null +++ b/deluge/plugins/label/label/test.py @@ -0,0 +1,79 @@ +from deluge.ui.client import sclient + +sclient.set_core_uri() + +print sclient.get_enabled_plugins() + +#enable plugin. +if not "label" in sclient.get_enabled_plugins(): + sclient.enable_plugin("label") + +#test filter items. +print "# label_filter_items()" +for cat,filters in sclient.label_filter_items(): + print "-- %s --" % cat + for filter in filters: + print " * %s (%s)" % (filter[0],filter[1]) + +# test filtering +print "#len(sclient.label_get_filtered_ids({'tracker':'tracker.aelitis.com'} ))" +print len(sclient.label_get_filtered_ids({'tracker':'tracker.aelitis.com'} )) + +print "#len(sclient.label_get_filtered_ids({'state':'Paused'} ))" +print len(sclient.label_get_filtered_ids({'state':'Paused'} )) + + +print "#len(sclient.label_get_filtered_ids({'keyword':'az'} ))" +print len(sclient.label_get_filtered_ids({'keyword':'az'} )) + + +print "#len(sclient.label_get_filtered_ids({'state':'Paused','tracker':'tracker.aelitis.com'} ))" +print len(sclient.label_get_filtered_ids({'state':'Paused','tracker':'tracker.aelitis.com'} )) + +print "#test status-fields:" +ids = sclient.get_session_state() + +torrents = sclient.get_torrents_status(ids,['name', 'tracker_host', 'label']) + +for id,torrent in torrents.iteritems(): + print id, torrent + +#test labels. +print "#init labels" +try: + sclient.label_remove("test") +except: + pass +id = sclient.get_session_state()[0] + +print "#add" +sclient.label_add("test") +print "#set" +sclient.label_set_torrent(id,"test") + +print "#len(sclient.label_get_filtered_ids({'label':'test'} ))" +print len(sclient.label_get_filtered_ids({'label':'test'} )) + +#test filter items. +print "# label_filter_items()" +for cat,filters in sclient.label_filter_items(): + if cat == "label": + print "-- %s --" % cat + for filter in filters: + print " * %s (%s)" % (filter[0],filter[1]) + + +print "#set options" +sclient.label_set_options("test",{"max_download_speed":999}, True) +print sclient.get_torrent_status(id, ["max_download_speed"]) , "999" +sclient.label_set_options("test",{"max_download_speed":9}, True) +print sclient.get_torrent_status(id, ["max_download_speed"]) , "9" +sclient.label_set_options("test",{"max_download_speed":888}, False) +print sclient.get_torrent_status(id, ["max_download_speed"]) , "9 (888)" + +print sclient.get_torrent_status(id,['name', 'tracker_host', 'label']) + + + + + diff --git a/deluge/plugins/label/label/ui.py b/deluge/plugins/label/label/ui.py new file mode 100644 index 000000000..94b5f4b9f --- /dev/null +++ b/deluge/plugins/label/label/ui.py @@ -0,0 +1,53 @@ +# +# blocklist/ui.py +# +# Copyright (C) 2007 Andrew Resch ('andar') +# Copyright (C) 2008 Mark Stahler ('kramed') + +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gettext +import locale +import pkg_resources +import deluge.component +from deluge.ui.client import aclient as client +from deluge.log import LOG as log + +class UI: + def __init__(self, plugin_api, plugin_name): + self.plugin = plugin_api + + def enable(self): + pass + + def disable(self): + pass + + \ No newline at end of file diff --git a/deluge/plugins/label/label/webui.py b/deluge/plugins/label/label/webui.py new file mode 100644 index 000000000..5bdc03f75 --- /dev/null +++ b/deluge/plugins/label/label/webui.py @@ -0,0 +1,63 @@ +# +# blocklist/webui.py +# +# Copyright (C) 2008 Martijn Voncken + +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import os +from deluge.log import LOG as log +from deluge.ui.client import sclient +from deluge import component +import ui + +import webui_config +import webui_pages + +class WebUI(ui.UI): + def __init__(self, plugin_api, plugin_name): + log.debug("Calling UI init") + # Call UI constructor + ui.UI.__init__(self, plugin_api, plugin_name) + log.debug("Label WebUI plugin initalized..") + + def enable(self): + webui_pages.register() + webui_config.register() + + def disable(self): + webui_pages.unregister() + webui_config.unregister() + + + + + + diff --git a/deluge/plugins/label/label/webui_config.py b/deluge/plugins/label/label/webui_config.py new file mode 100644 index 000000000..c11c85650 --- /dev/null +++ b/deluge/plugins/label/label/webui_config.py @@ -0,0 +1,149 @@ +# +# Copyright (C) 2008 Martijn Voncken +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import os +from deluge.log import LOG as log +from deluge.ui.client import sclient +from deluge import component + +api = component.get("WebPluginApi") +forms = api.forms + +class LabelUpdateCfgForm(forms.Form): + """ + a config form based on django forms. + see webui.lib.newforms_plus, config_forms, etc. + """ + #meta: + title = _("Labels") + + def get_selected(self): + selected = api.web.input(label = None).label + labels = sclient.label_get_labels() + if not selected: + if labels: + selected = labels[0] + return selected + + def pre_html(self): + selected = self.get_selected() + html = _("Select Label") + ":" + for label in sclient.label_get_labels(): + html += """ + %(label)s  """ % {"label":label} + html += "

%s

" % selected + return html + #load/save: + def initial_data(self): + label_id = self.get_selected() + return sclient.label_get_options(label_id) + + def save(self, data): + label_id = self.get_selected() + apply = data.apply + delete = data.delete + + if delete: + sclient.label_remove(label_id) + raise Exception("DELETED") + else: + del data["apply"] + del data["delete"] + sclient.label_set_options(label_id, dict(data), apply) + + #input fields : + max_connections = forms.DelugeInt(_("Maximum Connections")) + max_download_speed = forms.DelugeFloat(_("Maximum Download Speed (Kib/s)")) + max_upload_speed = forms.DelugeFloat(_("Maximum Upload Speed (Kib/s)")) + max_upload_slots = forms.DelugeInt(_("Maximum Upload Slots")) + + apply = forms.CheckBox(_("Apply")) + delete = forms.CheckBox(_("Delete")) + + + +class LabelAddCfgForm(forms.Form): + """ + a config form based on django forms. + see webui.lib.newforms_plus, config_forms, etc. + """ + #meta: + title = _("Add Label") + + #load/save: + def initial_data(self): + return { } + + def save(self, data): + sclient.label_add(data.label) + + label = forms.CharField(_("Label")) + +class LabelCfgForm(forms.Form): + """ + global settings. + """ + #meta: + title = _("General") + + #load/save: + def initial_data(self): + return sclient.label_get_global_options() + + def save(self, data): + return sclient.label_set_global_options(dict(data)) + + hide_zero_hits = forms.CheckBox(_("Hide filter items with 0 hits")) + #gtk_alfa = forms.CheckBox(_("gtk_alfa")) + + + + +def register(): + api.config_page_manager.register('label','label_general',LabelCfgForm) + api.config_page_manager.register('label','label_update',LabelUpdateCfgForm) + api.config_page_manager.register('label','label_add',LabelAddCfgForm) + +def unregister(): + api.config_page_manager.unregister('label_general') + api.config_page_manager.unregister('label_update') + api.config_page_manager.unregister('label_add') + + + + + + + + + + + diff --git a/deluge/plugins/label/label/webui_pages.py b/deluge/plugins/label/label/webui_pages.py new file mode 100644 index 000000000..b644a793f --- /dev/null +++ b/deluge/plugins/label/label/webui_pages.py @@ -0,0 +1,79 @@ +# +# +# Copyright (C) 2008 Martijn Voncken +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import os +from deluge.log import LOG as log +from deluge.ui.client import sclient +from deluge import component + +api = component.get("WebPluginApi") + +template_dir = os.path.join(os.path.dirname(__file__),"template") + +class torrent_label: + @api.deco.deluge_page + @api.deco.torrent_list + def GET(self, torrent_list): + torrent_str = ",".join([t.id for t in torrent_list]) + labels = sclient.label_get_labels() + return api.render.torrent_label(torrent_str , torrent_list , labels) + + @api.deco.check_session + @api.deco.torrent_ids + def POST(self, torrent_ids): + label =api.web.input(label = None).label + for id in torrent_ids: + sclient.label_set_torrent(id , label) + api.utils.do_redirect() + + +def register(): + api.render.register_template_path(template_dir) + api.page_manager.register_page('/torrent/label/(.*)', torrent_label) + api.menu_manager.register_toolbar_item("label",_("Label"), "label.png" ,2, + "GET","/torrent/label/", True) + +def unregister(): + api.render.unregister_template_path(template_dir) + api.page_manager.unregister_page('/torrent/label/(.*)') + api.menu_manager.unregister_toolbar_item("label") + + + + + + + + + + + diff --git a/deluge/plugins/label/setup.py b/deluge/plugins/label/setup.py new file mode 100644 index 000000000..959b5c9cd --- /dev/null +++ b/deluge/plugins/label/setup.py @@ -0,0 +1,60 @@ +# setup.py +# +# Copyright (C) 2008 Martijn Voncken +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +""" +Label plugin. + +Offers filters on state,tracker and keyword. +adds a tracker column. + +future: Real labels. + +""" + +from setuptools import setup + +__author__ = "Martijn Voncken " + +setup( + name="Label", + version="0.1", + description=__doc__, + author=__author__, + packages=["label"], + package_data = {"label": ["template/*"]}, + entry_points=""" + [deluge.plugin.core] + Label = label:CorePlugin + [deluge.plugin.webui] + Label = label:WebUIPlugin + [deluge.plugin.gtkui] + Label = label:GtkUIPlugin + """ +)