importing the feeder-plugin for porting to 1.2

This commit is contained in:
Fredrik Eriksson 2009-08-31 18:55:51 +00:00
parent 7a09ef5356
commit 753e17d612
7 changed files with 933 additions and 0 deletions

View File

@ -0,0 +1,65 @@
#
# __init__.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
# 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)

View File

@ -0,0 +1,419 @@
#
# core.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
# 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

View File

@ -0,0 +1,13 @@
$def with (entries, feedname)
$:render.header("things", '')
<div class="panel" >
<div>
<h3>Feed items for feed $feedname</h3>
<ul>
$entries
</ul>
</div>
<a href="/config/feeder">back to config</a>
</div>
$:render.footer()

View File

@ -0,0 +1,50 @@
$def with (filter_settings_form, filter)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Deluge:things</title>
<link rel="icon" href="/static/images/deluge-icon.png" type="image/png" />
<link rel="shortcut icon" href="/static/images/deluge-icon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="/template_style.css" />
<script language="javascript" src="/static/deluge.js"></script>
<script language="javascript" src="/static/mootools-1.2-core.js"></script>
<script language="javascript" src="/static/mootools-1.2-more.js"></script>
<script language="javascript" src="/static/mooui.js"></script>
<script language="javascript" src="/static/deluge-moo.js"></script>
<script language="javascript" src="/gettext.js"></script>
<script language="javascript" src="/label/data/label.js"></script>
</head>
<body>
<form method="post" action='$base/feeder/filter_settings/$filter'>
<div class="info">
<h3>Filter</h3>
<table>
$:(filter_settings_form.as_table(["regex", "active"]))
</table>
<h3>Speed</h3>
<table>
$:(filter_settings_form.as_table(["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections"]))
</table>
<h3>Seed options</h3>
<table>
$:(filter_settings_form.as_table(["stop_ratio", "stop_at_ratio", "remove_at_ratio"]))
</table>
<h3>Other options</h3>
<table>
$:(filter_settings_form.as_table(["prioritize_first_last_pieces", "auto_managed", "download_location"]))
</table>
<input type="submit" name="submit" value="$_('Save')" />
</form>
<h3>Matches</h3>
$:filter_settings_form.post_html()
</div>
</body>
</html>

View File

@ -0,0 +1,39 @@
$def with (filters, new_filter_form)
$:render.header("things", '')
<table><tr>
<td>
<table><tr></td>
<div class="info">
<h3>Filters</h3>
</div></td></tr>
<tr><td>
<div class="info">
<ul>
$filters
</ul>
<form method="post" action='$base/feeder/filters'>
$:(new_filter_form.as_p(["name"]))
<input type="submit" name="submit" class="form_input" value="$_('Add filter')" />
</form>
<p><a href="/config/feeder">back to config</a>
</div></td></tr></table>
</td>
<td>
<div class="panel">
<iframe style="border-style:hidden;" id="filter_settings" width=100% height=600>
</iframe>
</div>
</td>
</tr></table>
<script language="javascript">
function load_options(filter){
\$('filter_settings').src = state.base_url + '/feeder/filter_settings/' + filter;
}
</script>
<script language="javascript">
new InputSensitivitySetter({prefix:"id_",groups:[
["name"]
]});
</script>
$:render.footer()

View File

@ -0,0 +1,277 @@
#
# webui.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
# 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
<a href="%(link)s">
<li>%(entry)s</li>
</a>""" % { "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
<li onclick=\"load_options('%(new)s')\">
%(new)s
</li>""" % {'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
<li><a href="%(link)s" >%(name)s</a></li>
""" % { "old":list, "link":hits[hit], "name":hit }
return """
<ul>
%s
</ul>
""" % 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 """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Redirecting back to settings</title>
<meta http-equiv="refresh" content="0; URL=/config/feeder">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
</body>
</html>"""
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 """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Redirecting back to settings</title>
<meta http-equiv="refresh" content="0; URL=/config/feeder">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
</body>
</html>"""
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 <li>%(new)s
<a href="/feeder/filter_remove/%(new)s">
<img src="/static/images/16/list-remove.png" alt="Remove" />
</a></li>""" % {'old':filterlist, 'new':filter}
feedlist = ""
for feed in feeds:
feedlist = """%(old)s
<li> <a href="/feeder/feed/%(new)s"> %(new)s (%(entrys)s torrents)</a>
<a href="/feeder/feed_remove/%(new)s">
<img src="/static/images/16/list-remove.png" alt="Remove" />
</a></li>""" % {'old':feedlist, 'new':feed, 'entrys':len(sclient.feeder_get_items(feed))}
return """
<table width=100%%><tr><td>
<h3>Feeds</h3>
</td>
<td>
<h3>Filters</h3>
</td></tr>
<tr><td>
<div class="info">
<ul>
%(feeds)s
</ul></div>
</td><td>
<div class="info">
<ul>
%(filters)s
</ul></div>
<a href="/feeder/filters">Add/modify filters</a>
</td></tr>
</table>
<h3>Add/change feed settings</h3>""" % {'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"))

View File

@ -0,0 +1,70 @@
#
# setup.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
#
# 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)
)