From 753e17d612b721b7b903f2980ff517591204705b Mon Sep 17 00:00:00 2001 From: Fredrik Eriksson Date: Mon, 31 Aug 2009 18:55:51 +0000 Subject: [PATCH] importing the feeder-plugin for porting to 1.2 --- deluge/plugins/feeder/feeder/__init__.py | 65 +++ deluge/plugins/feeder/feeder/core.py | 419 ++++++++++++++++++ .../plugins/feeder/feeder/template/feeds.html | 13 + .../feeder/template/filter_settings.html | 50 +++ .../feeder/feeder/template/filters.html | 39 ++ deluge/plugins/feeder/feeder/webui.py | 277 ++++++++++++ deluge/plugins/feeder/setup.py | 70 +++ 7 files changed, 933 insertions(+) create mode 100644 deluge/plugins/feeder/feeder/__init__.py create mode 100644 deluge/plugins/feeder/feeder/core.py create mode 100644 deluge/plugins/feeder/feeder/template/feeds.html create mode 100644 deluge/plugins/feeder/feeder/template/filter_settings.html create mode 100644 deluge/plugins/feeder/feeder/template/filters.html create mode 100644 deluge/plugins/feeder/feeder/webui.py create mode 100644 deluge/plugins/feeder/setup.py diff --git a/deluge/plugins/feeder/feeder/__init__.py b/deluge/plugins/feeder/feeder/__init__.py new file mode 100644 index 000000000..645a32eaf --- /dev/null +++ b/deluge/plugins/feeder/feeder/__init__.py @@ -0,0 +1,65 @@ +# +# __init__.py +# +# Copyright (C) 2008 Fredrik Eriksson +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch +# +# 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 3 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 + +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/feeder/feeder/core.py b/deluge/plugins/feeder/feeder/core.py new file mode 100644 index 000000000..2f7134ee2 --- /dev/null +++ b/deluge/plugins/feeder/feeder/core.py @@ -0,0 +1,419 @@ +# +# core.py +# +# Copyright (C) 2008 Fredrik Eriksson +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch +# +# 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 3 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 + + +import feedparser # for parsing rss feeds +import gobject # for the update timer +import threading # for threaded updates +import re # for regular expressions + + +import deluge +import string +from deluge.log import LOG as log +from deluge.plugins.corepluginbase import CorePluginBase +from deluge import component +from deluge.plugins.coreclient import client #1.1 and later only +#client: see http://dev.deluge-torrent.org/wiki/Development/UiClient#Remoteapi + +"""Help classes""" +class Feed: + "Class for the Feed object (containging feed configurations)" + def __init__(self): + self.url = "" + self.updatetime = 15 + + def get_config(self): + return {'url': self.url, 'updatetime': self.updatetime} + + def set_config(self, config): + self.url = config['url'] + self.updatetime = config['updatetime'] + +class Filter: + "Class for the Filter object (containing filter configurations)" + + def __init__(self): + self.regex = "" + self.feeds = [] #TODO activate filter per feed + self.all_feeds = True + self.active = True + + # by default, set the configuration to match + # the per-torrent settings in deluge + def_conf = client.get_config() + self.max_download_speed = def_conf['max_download_speed_per_torrent'] + self.max_upload_speed = def_conf['max_upload_speed_per_torrent'] + self.max_connections = def_conf['max_connections_per_torrent'] + self.max_upload_slots = def_conf['max_upload_slots_per_torrent'] + self.prioritize_first_last_pieces = def_conf['prioritize_first_last_pieces'] + self.auto_managed = def_conf['auto_managed'] + self.download_location = def_conf['download_location'] + + self.stop_at_ratio = def_conf['stop_seed_at_ratio'] + self.stop_ratio = def_conf['stop_seed_ratio'] + self.remove_at_ratio = def_conf['remove_seed_at_ratio'] + + def get_config(self): + def_conf = client.get_config() + + try: + tmp = self.active + except Exception, e: + log.debug("Old filter detected (pre 0.3), updating...") + self.active = True + + try: + tmp = self.stop_at_ratio + tmp = self.stop_ratio + tmp = self.remove_at_ratio + except: + log.debug("Old filter detected (pre 0.4), updating...") + self.stop_at_ratio = def_conf['stop_seed_at_ratio'] + self.stop_ratio = def_conf['stop_seed_ratio'] + self.remove_at_ratio = def_conf['remove_seed_at_ratio'] + + conf = { + 'regex': self.regex, + 'feeds': self.feeds, + 'all_feeds': self.all_feeds, + 'active' : self.active, + 'max_download_speed': self.max_download_speed, + 'max_upload_speed': self.max_upload_speed, + 'max_connections': self.max_connections, + 'max_upload_slots': self.max_upload_slots, + 'prioritize_first_last_pieces': self.prioritize_first_last_pieces, + 'auto_managed': self.auto_managed, + 'download_location':self.download_location, + 'remove_at_ratio':self.remove_at_ratio, + 'stop_ratio': self.stop_ratio, + 'stop_at_ratio': self.stop_at_ratio } + + return conf + + def set_config(self, conf): + self.regex = conf['regex'] + self.feeds = conf['feeds'] + self.all_feeds = conf['all_feeds'] + self.active = conf['active'] + self.max_download_speed = int(conf['max_download_speed']) + self.max_upload_speed = int(conf['max_upload_speed']) + self.max_connections = int(conf['max_connections']) + self.max_upload_slots = int(conf['max_upload_slots']) + self.prioritize_first_last_pieces = conf['prioritize_first_last_pieces'] + self.auto_managed = conf['auto_managed'] + self.download_location = conf['download_location'] + self.remove_at_ratio = conf['remove_at_ratio'] + self.stop_ratio = float(conf['stop_ratio']) + self.stop_at_ratio = conf['stop_at_ratio'] + + +DEFAULT_PREFS = { + "feeds": {}, + "filters": {}, + "updatetime": 15, + "history": [] +} + +class Core(CorePluginBase): + + + + """=============enable/disable functions=============""" + + def enable(self): + #initiate variables + self.config = deluge.configmanager.ConfigManager("feeder.conf", DEFAULT_PREFS) + self.feeds = {} + self.timers = {} + self.history = self.config['history'] + + # Setting default timer to configured update time + for feed in self.config['feeds']: + self.timers[feed] = gobject.timeout_add( + self.config['feeds'][feed].updatetime * 60 * 1000, + self.update_feed, feed ) + + # update all feeds on startup + self.update_feeds() + + def disable(self): + self.config['history'] = self.history + self.config.save() + + + + """=============Internal functions=============""" + + def update_feeds(self): + "Start threads to update all feeds" + for feedname in self.config['feeds']: + self.update_feed(feedname) + + def update_feed(self, feedname): + "Start a thread to update a single feed" + threading.Thread( + target=self.update_feed_thread, + args=(self.on_feed_updated, feedname)).start() + + # Need to return true to not destoy timer... + return True + + def update_feed_thread(self, callback, feedname): + "updates a feed" + feed = self.config['feeds'][feedname] + try: + self.feeds[feedname] = feedparser.parse(feed.url) + except Exception, e: + log.warning("Error parsing feed %s: %s", feedname, e) + else: + callback(feedname) + + def on_feed_updated(self, feedname): + "Run stuff when a feed has been updated" + + # Not all feeds contain a ttl value, but if it does + # we would like to obey it + try: + if not self.feeds[feedname].ttl == self.config['feeds'][feedname].updatetime: + log.debug("feed '%s' request a ttl of %s, updating timer", feedname, self.feeds[feedname].ttl) + self.config['feeds'][feedname].updatetime = self.feeds[feedname].ttl + del self.timers[feedname] + self.timers[feedname] = gobject.timeout_add( + self.config['feeds'][feedname].updatetime * 60 * 1000, + self.update_feed, feedname ) + except Exception, e: + log.debug("feed '%s' has no ttl set, will use default timer", feedname) + + # Run filters on the feed + self.run_filters(feedname) + + def run_filters(self, feedname, filters={}, test=False): + "Test all available filters on the given feed" + if not filters: + filters = self.config['filters'] + log.debug("will test filters %s", filters) + hits = {} + # Test every entry... + for entry in self.feeds[feedname]['entries']: + # ...and every filter + for filter in filters: + # We need to be able to run feeds saved before implementation of actiave/deactivate filter (pre 0.3) TODO + try: + if not filters[filter].active: + continue + except: + log.debug("old filter, will assume filter is activated") + + if filters[filter].regex == "": # we don't want a empty regex... + log.warning("Filter '%s' has not been configured, ignoring!", filter) + continue + + # if the filter isn't supposed to be run on this feed we don't want to run it... +# if filter.all_feeds or self.config['filters'][filter].feeds.has_element(feedname) : # ...apparently has_element doesn't work on arrays... TODO + if self.test_filter(entry, filters[filter].regex): + if test: + hits[entry.title] = entry.link + else: + opts = filters[filter].get_config() + #remove filter options that should not be passed on to the torrent. + del opts['regex'] + del opts['feeds'] + del opts['all_feeds'] + + # history patch from Darrell Enns, slightly modified :) + # check history to prevent multiple adds of the same torrent + log.debug("testing %s", entry.link) + if not entry.link in self.history: + self.add_torrent(entry.link, opts) + self.history.append(entry.link) + + #limit history to 50 entries + if len(self.history)>50: + self.history=self.history[-50:] + log.debug("wrapping history") + else: + log.debug("'%s' is in history, will not download", entry.link) + return hits + + + def test_filter(self, entry, filter): + "Tests a filter to a given rss entry" + f = re.compile(filter, re.IGNORECASE) + if f.search(entry.title) or f.search(entry.link): + log.debug("RSS item '%s' matches filter '%s'", entry.title, filter) + return True + else: + return False + + def add_torrent(self, url, torrent_options): + log.debug("Attempting to add torrent %s", url) + client.add_torrent_url(url, torrent_options) + + """=============Export functions=============""" + + """#############Configuration Setters#############""" + + def export_add_feed(self, config): + "adds/updates a feed and, for whatever reason, sets the default timeout" + + # save the feedname and remove it from the config + feedname = config['name'] + del config['name'] + + # check if the feed already exists and save config + try: + conf = self.config['feeds'][feedname].get_config() + del self.config['feeds'][feedname] + except Exception, e: + conf = {} + + # update configuration + for var in config: + conf[var] = config[var] + + # save as default update time + try: + self.config['updatetime'] = config['updatetime'] + except Exception, e: + log.warning("updatetime not set when adding feed %s", feedname) + + # Create the new feed + newfeed = Feed() + newfeed.set_config(conf) + + # Add a timer (with default timer for now, since we can't get ttl just yet)... + self.timers[feedname] = gobject.timeout_add( + newfeed.updatetime * 60 * 1000, + self.update_feed, feedname ) + + # Save the new feed + self.config['feeds'].update({feedname: newfeed }) + self.config.save() + + # And update the new feed + self.update_feed(feedname) + + def export_remove_feed(self, feedname): + "Remove a feed" + if self.feeds.has_key(feedname): # Check if we have the feed saved and remove it + del self.feeds[feedname] + if self.timers.has_key(feedname): # Check if we have a timer for this feed and remove it + del self.timers[feedname] + if self.config['feeds'].has_key(feedname): # Check if we have the feed in the configuration and remove it + del self.config['feeds'][feedname] + self.config.save() + + def export_add_filter(self, name): + "Adds a new filter to the configuration" + if not self.config['filters'].has_key(name): # we don't want to add a filter that already exists + self.config['filters'][name] = Filter() + self.config.save() + + def export_set_filter_config(self, filtername, conf): + "Changes the options for a filter" + oldconf = self.config['filters'][filtername].get_config() + for item in conf: + oldconf[item] = conf[item] + + self.config['filters'][filtername].set_config(oldconf) + self.config.save() + for feed in self.config['feeds']: # we would like to check if the filter now matches something new + self.run_filters(feed) + + def export_remove_filter(self, name): + "Removes a filter" + if self.config['filters'].has_key(name): # Can't remove a filter that doesn't exists + del self.config['filters'][name] + self.config.save() + + + + """#############Configuration Getters#############""" + + def export_get_config(self): + "returns the config dictionary" + return self.config.config + + + def export_get_feed_config(self, feedname): + "Returns configuration for a feed" + return self.config['feeds'][feedname].get_config() + + def export_get_filter_config(self, filtername): + "Returns a configuration for a filter" + return self.config['filters'][filtername].get_config() + + + """#############Information Getters#############""" + + def export_get_feeds(self): + "Returns a list of the configured feeds" + feeds = [] + for feedname in self.config['feeds']: + feeds.append(feedname) + feeds.sort(key=string.lower) + return feeds + + def export_get_filters(self): + "Returns a list of all available filters" + filters = [] + for filter in self.config['filters']: + filters.append(filter) + filters.sort(key=string.lower) + return filters + + def export_get_items(self, feedname): + "Returns a dictionary with feedname:link" + try: + items = {} + feed = self.feeds[feedname] + for entry in feed['entries']: + items[entry.title] = entry.link + except Exception, e: + items = {} + log.warning("Feed '%s' not loaded", feedname) + return items + + def export_test_filter(self, regex): + filters = { "to_test":Filter() } + conf = filters["to_test"].get_config() + conf["regex"] = regex + filters["to_test"].set_config(conf) + hits = {} + for feed in self.feeds: + hits.update(self.run_filters(feed, filters, test=True)) + return hits diff --git a/deluge/plugins/feeder/feeder/template/feeds.html b/deluge/plugins/feeder/feeder/template/feeds.html new file mode 100644 index 000000000..c5c177235 --- /dev/null +++ b/deluge/plugins/feeder/feeder/template/feeds.html @@ -0,0 +1,13 @@ +$def with (entries, feedname) +$:render.header("things", '') +
+
+

Feed items for feed $feedname

+
    +$entries +
+
+back to config +
+ +$:render.footer() diff --git a/deluge/plugins/feeder/feeder/template/filter_settings.html b/deluge/plugins/feeder/feeder/template/filter_settings.html new file mode 100644 index 000000000..b2dcd3d42 --- /dev/null +++ b/deluge/plugins/feeder/feeder/template/filter_settings.html @@ -0,0 +1,50 @@ +$def with (filter_settings_form, filter) + + + + + Deluge:things + + + + + + + + + + + + + + +
+
+

Filter

+ +$:(filter_settings_form.as_table(["regex", "active"])) +
+ +

Speed

+ +$:(filter_settings_form.as_table(["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections"])) +
+ +

Seed options

+ +$:(filter_settings_form.as_table(["stop_ratio", "stop_at_ratio", "remove_at_ratio"])) +
+ +

Other options

+ +$:(filter_settings_form.as_table(["prioritize_first_last_pieces", "auto_managed", "download_location"])) +
+ + + +

Matches

+$:filter_settings_form.post_html() +
+ + + diff --git a/deluge/plugins/feeder/feeder/template/filters.html b/deluge/plugins/feeder/feeder/template/filters.html new file mode 100644 index 000000000..cf2187cd3 --- /dev/null +++ b/deluge/plugins/feeder/feeder/template/filters.html @@ -0,0 +1,39 @@ +$def with (filters, new_filter_form) +$:render.header("things", '') + + + +
+ +
+

Filters

+
+
+
+
    + $filters +
+
+ $:(new_filter_form.as_p(["name"])) + +
+

back to config +

+
+
+ +
+
+ + + +$:render.footer() diff --git a/deluge/plugins/feeder/feeder/webui.py b/deluge/plugins/feeder/feeder/webui.py new file mode 100644 index 000000000..4769155f3 --- /dev/null +++ b/deluge/plugins/feeder/feeder/webui.py @@ -0,0 +1,277 @@ +# +# webui.py +# +# Copyright (C) 2008 Fredrik Eriksson +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch +# +# 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 3 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 + +import feedparser # for proccessing feed entries +import os +from deluge.log import LOG as log +from deluge.ui.client import sclient, aclient +from deluge.plugins.webuipluginbase import WebUIPluginBase +from deluge import component + +api = component.get("WebPluginApi") +forms = api.forms + +class feed_page: + "Class for showing feed items" + @api.deco.deluge_page + def GET(self, feedname): + entries = sclient.feeder_get_items(feedname) + items = "" + for item in entries: + items = """%(old)s + +
  • %(entry)s
  • +
    """ % { "old":items, "entry":item, "link":entries[item]} + return api.render.feeder.feeds(items, feedname) + +class filter_page: + "Class for showing filters / filter settings" + @api.deco.deluge_page + def GET(self, args): + new_filter = new_filter_form() + filters = sclient.feeder_get_filters() + + # List filters + txt = "" + for filter in filters: + txt = """%(old)s +
  • + %(new)s +
  • """ % {'old':txt, 'new':filter} + + return api.render.feeder.filters(txt, new_filter) + + def POST(self): + "Saves the new filter" + name = api.utils.get_newforms_data(new_filter_form)['name'] + sclient.feeder_add_filter(name) + return self.GET(name) + +class new_filter_form(forms.Form): + "basic form for a new label" + name = forms.CharField(label="") + +class filter_settings_page: + "Class for showing filter settings" + @api.deco.deluge_page + def GET(self, filter): + form = filter_settings_form(filter) + return api.render.feeder.filter_settings(form, filter) + + def POST(self, filter): + opts = api.utils.get_newforms_data(filter_settings_form) + + # apparently the "Unlimited" options still have to be changed + # to -1 (wtf?) + # FIXME there is probably a very much better way to ensure that + # all values have the right types... not to mention to convert "Unlimited" + # to -1... + try: + opts['max_upload_speed'] = int(opts['max_upload_speed']) + except: + opts['max_upload_speed'] = int(-1) + try: + opts['max_download_speed'] = int(opts['max_download_speed']) + except: + opts['max_download_speed'] = int(-1) + try: + opts['max_connections'] = int(opts['max_connections']) + except: + opts['max_connections'] = int(-1) + try: + opts['max_upload_slots'] = int(opts['max_upload_slots']) + except: + opts['max_upload_slots'] = int(-1) + """opts['max_upload_slots'] = long(opts['max_upload_slots']) + opts['max_connections'] = long(opts['max_connections'])""" + + # TODO filter settings per feed not implemented. + opts['feeds'] = [] + + sclient.feeder_set_filter_config(filter, opts) + return self.GET(filter) + +class filter_settings_form(forms.Form): + "form for filter settings" + + def __init__(self, filter, test=False): + self.filtername = filter # We want to save our filtername + forms.Form.__init__(self) + + def initial_data(self): + self.conf = sclient.feeder_get_filter_config(self.filtername) + return self.conf + + def post_html(self): + regex = self.conf["regex"] + hits = sclient.feeder_test_filter(regex) + if not hits: + return "No hits" + list = "" + for hit in hits: + list = """%(old)s +
  • %(name)s
  • + """ % { "old":list, "link":hits[hit], "name":hit } + return """ +
      + %s +
    + """ % list + + regex = forms.CharField(_("regular_expression")) + all_feeds = forms.CheckBox(_("all_feeds")) + active = forms.CheckBox(_("active")) + + #maximum: + max_download_speed = forms.DelugeFloat(_("max_download_speed")) + max_upload_speed = forms.DelugeFloat(_("max_upload_speed")) + max_upload_slots = forms.DelugeInt(_("max_upload_slots")) + max_connections = forms.DelugeInt(_("max_connections")) + + stop_ratio = forms.DelugeFloat(_("stop_ratio")) + stop_at_ratio = forms.CheckBox(_("stop_at_ratio")) + remove_at_ratio = forms.CheckBox(_("remove_at_ratio")) + + #queue: + auto_managed = forms.CheckBox(_("is_auto_managed")) + prioritize_first_last_pieces = forms.CheckBox(_("prioritize_first_last_pieces")) + + download_location = forms.ServerFolder(_("download_location")) + +class remove_feed_page: + "Class for deleting feeds, redirects to setting page" + @api.deco.deluge_page + def GET(self, feedname): + sclient.feeder_remove_feed(feedname) + return """ + + + Redirecting back to settings + + + + + + + """ + +class remove_filter_page: + "Class for deleting filters, redirects to setting page" + @api.deco.deluge_page + def GET(self, name): + sclient.feeder_remove_filter(name) + return """ + + + Redirecting back to settings + + + + + + + """ + + +class WebUI(WebUIPluginBase): + #map url's to classes: [(url,class), ..] + urls = [('/feeder/filters', filter_page), + ('/feeder/filter_settings/(.*)', filter_settings_page), + ('/feeder/feed_remove/(.*)', remove_feed_page), + ('/feeder/filter_remove/(.*)', remove_filter_page), + ('/feeder/feed/(.*)', feed_page)] + + def enable(self): + api.config_page_manager.register('plugins', 'feeder' ,ConfigForm) + + def disable(self): + api.config_page_manager.deregister('feeder') + +class ConfigForm(forms.Form): + #meta: + title = _("feeder") + + #load/save: + def initial_data(self): + return sclient.feeder_get_config() + + def save(self, data): + cfg = dict(data) + sclient.feeder_add_feed(cfg) + + def pre_html(self): + feeds = sclient.feeder_get_feeds() + filters = sclient.feeder_get_filters() + filterlist = "" + for filter in filters: + filterlist = """ %(old)s
  • %(new)s + + Remove +
  • """ % {'old':filterlist, 'new':filter} + feedlist = "" + for feed in feeds: + feedlist = """%(old)s +
  • %(new)s (%(entrys)s torrents) + + Remove +
  • """ % {'old':feedlist, 'new':feed, 'entrys':len(sclient.feeder_get_items(feed))} + + return """ + + + +
    +

    Feeds

    +
    +

    Filters

    +
    +
    +
      + %(feeds)s +
    +
    +
    +
      + %(filters)s +
    + Add/modify filters +
    +

    Add/change feed settings

    """ % {'feeds':feedlist, 'filters':filterlist} + + name = forms.CharField(label=_("Name of feed")) + url = forms.URLField(label=_("URL of feed")) + updatetime = forms.IntegerField(label=_("Defualt refresh time")) + diff --git a/deluge/plugins/feeder/setup.py b/deluge/plugins/feeder/setup.py new file mode 100644 index 000000000..4c4a09f8e --- /dev/null +++ b/deluge/plugins/feeder/setup.py @@ -0,0 +1,70 @@ +# +# setup.py +# +# Copyright (C) 2008 Fredrik Eriksson +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch +# +# 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 3 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 + +from setuptools import setup + +__plugin_name__ = "feeder" +__author__ = "Fredrik Eriksson" +__author_email__ = "feeder@winterbird.org" +__version__ = "0.4" +__url__ = "" +__license__ = "GPLv3" +__description__ = "A plugin for automatically downloadning torrents from a RSS-feed" +__long_description__ = """""" +__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]} + +setup( + name=__plugin_name__, + version=__version__, + description=__description__, + author=__author__, + author_email=__author_email__, + url=__url__, + license=__license__, + long_description=__long_description__, + + packages=[__plugin_name__.lower()], + package_data = __pkg_data__, + + entry_points=""" + [deluge.plugin.core] + %s = %s:CorePlugin + [deluge.plugin.gtkui] + %s = %s:GtkUIPlugin + [deluge.plugin.webui] + %s = %s:WebUIPlugin + """ % ((__plugin_name__, __plugin_name__.lower())*3) +)