diff --git a/README.md b/README.md index 33ff9e2..c500d91 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ supposed to reach public trackers. Besides manually creating the default tracker list, you can also load it (periodically) from a URL. -This plugin is compatible with Deluge 1.3 and 2.0, Python2 2.7 and Python3 3.5+. +This plugin is compatible with Deluge 2.0 and Python 3.6+. ## Installation @@ -18,14 +18,16 @@ This plugin is compatible with Deluge 1.3 and 2.0, Python2 2.7 and Python3 3.5+. `python setup.py bdist_egg` -(or try to use [the one from the "egg" directory][2] - be careful to install the py2.7 version of Deluge, if you're using Windows) +(or try to use [the one from the "egg" directory][2] - rename it, if it doesn't match your Python3 version) -* you need to use the same version of Python as the one that Deluge is running under. - -* if you're using it with Deluge-1.3, you might not have [six](https://pypi.org/project/six/) installed. Install it. +* you need to use the same version of Python3 as the one that Deluge is running under. * add it to Deluge from Preferences -> Plugins -> Install Plugin +* now you can go to Preferences -> Default Trackers and add individual default trackers, or the URL of a list that should be periodically downloaded + (e.g.: https://newtrackon.com/api/stable +or https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt) + ## Troubleshooting To get Deluge's output on Windows, run this in a terminal ("cmd" works): @@ -35,8 +37,7 @@ To get Deluge's output on Windows, run this in a terminal ("cmd" works): ## TODO: * log the added trackers so we can remove them from torrents when they are deleted from the default list -* WebUI version [1]: http://deluge-torrent.org/ -[2]: https://github.com/stefantalpalaru/deluge-default-trackers/raw/master/egg/DefaultTrackers-0.2-py2.7.egg +[2]: https://github.com/stefantalpalaru/deluge-default-trackers/raw/master/egg diff --git a/defaulttrackers/common.py b/defaulttrackers/common.py index 6a28ee8..d0775fc 100644 --- a/defaulttrackers/common.py +++ b/defaulttrackers/common.py @@ -1,7 +1,7 @@ -# +# -*- coding: utf-8 -*- # common.py # -# Copyright (C) 2013-2019 Stefan Talpalaru +# Copyright (C) 2013-2022 Ștefan Talpalaru # # Basic plugin template created by: # Copyright (C) 2008 Martijn Voncken @@ -39,9 +39,8 @@ # -from __future__ import absolute_import, unicode_literals +import os +from pkg_resources import resource_filename def get_resource(filename): - import pkg_resources, os - return pkg_resources.resource_filename("defaulttrackers", - os.path.join("data", filename)) + return resource_filename(__package__, os.path.join("data", filename)) diff --git a/defaulttrackers/core.py b/defaulttrackers/core.py index 70bccc5..7de57ec 100644 --- a/defaulttrackers/core.py +++ b/defaulttrackers/core.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # core.py # -# Copyright (C) 2013-2019 Ștefan Talpalaru +# Copyright (C) 2013-2024 Ștefan Talpalaru # # Basic plugin template created by: # Copyright (C) 2008 Martijn Voncken @@ -45,9 +45,9 @@ import ssl import time import traceback -import six +import urllib -from deluge.common import is_url +from deluge.common import is_url, decode_bytes from deluge.core.rpcserver import export from deluge.plugins.pluginbase import CorePluginBase import deluge.component as component @@ -86,27 +86,36 @@ def update_trackerlist_from_url(self): now = datetime.datetime.utcnow() last_update = datetime.datetime.utcfromtimestamp(self.config["last_dynamic_trackers_update"]) if now - last_update > datetime.timedelta(days=self.config["dynamic_trackers_update_interval"]): + new_trackers = [] + seen_trackers = set() + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', + 'Accept-Encoding': 'none', + 'Accept-Language': 'en-US,en;q=0.8', + } try: - headers = { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0', - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', - 'Accept-Encoding': 'none', - 'Accept-Language': 'en-US,en;q=0.8', - } - - req = six.moves.urllib.request.Request(self.config["dynamic_trackerlist_url"], headers=headers) - try: - page = six.moves.urllib.request.urlopen(req, context=ssl._create_unverified_context()).read() - except: - # maybe an older Python version without a "context" argument - page = six.moves.urllib.request.urlopen(req).read() - new_trackers = [six.ensure_str(url) for url in re.findall(b'\w+://[\w\-.:/]+', page) if is_url(six.ensure_text(url))] + for url_line in self.config["dynamic_trackerlist_url"].splitlines(): + url_line = url_line.strip() + if not url_line: + continue + req = urllib.request.Request(url_line, headers=headers) + try: + page = urllib.request.urlopen(req, context=ssl._create_unverified_context()).read() + except: + # maybe an older Python version without a "context" argument + page = urllib.request.urlopen(req).read() + # we need to avoid duplicates here + for new_tracker in [decode_bytes(u) for u in re.findall(rb'\w+://[\w\-.:/]+', page) if is_url(decode_bytes(u))]: + if new_tracker not in seen_trackers: + seen_trackers.add(new_tracker) + new_trackers.append(new_tracker) + if new_trackers: # replace all existing trackers - self.config["trackers"] = [] - for new_tracker in new_trackers: - self.config["trackers"].append({"url": new_tracker}) + self.config["trackers"] = [{"url": t} for t in new_trackers] + self.config["last_dynamic_trackers_update"] = time.mktime(now.timetuple()) except: traceback.print_exc() @@ -143,3 +152,11 @@ def get_config(self): """Returns the config dictionary""" return self.config.config + @export + def apply_to_existing(self): + """Apply the tracker list to existings torrents""" + + for torrent_id in component.get("TorrentManager").torrents: + self.on_torrent_added(torrent_id) + return True + diff --git a/defaulttrackers/data/config.glade b/defaulttrackers/data/config.glade index 1a580fd..0629e10 100644 --- a/defaulttrackers/data/config.glade +++ b/defaulttrackers/data/config.glade @@ -40,14 +40,9 @@ - + True True - - False - False - True - True True diff --git a/defaulttrackers/data/config.ui b/defaulttrackers/data/config.ui index fd13d31..f2f1eda 100644 --- a/defaulttrackers/data/config.ui +++ b/defaulttrackers/data/config.ui @@ -40,14 +40,9 @@ - + True True - - False - False - True - True True diff --git a/defaulttrackers/data/defaulttrackers.js b/defaulttrackers/data/defaulttrackers.js index b0bcc7f..a7c5c00 100644 --- a/defaulttrackers/data/defaulttrackers.js +++ b/defaulttrackers/data/defaulttrackers.js @@ -3,7 +3,7 @@ Script: defaulttrackers.js The client-side javascript code for the DefaultTrackers plugin. Copyright: - (C) Stefan Talpalaru 2013-2016 + (C) Ștefan Talpalaru 2013-2022 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 3, or (at your option) @@ -31,20 +31,128 @@ Copyright: statement from all source files in the program, then also delete it here. */ -DefaultTrackersPlugin = Ext.extend(Deluge.Plugin, { +DefaultTrackersPanel = Ext.extend(Ext.form.FormPanel, { constructor: function(config) { config = Ext.apply({ - name: "DefaultTrackers" + border: false, + title: ("Default Trackers"), + autoHeight: true, }, config); - DefaultTrackersPlugin.superclass.constructor.call(this, config); + DefaultTrackersPanel.superclass.constructor.call(this, config); }, + initComponent: function() { + DefaultTrackersPanel.superclass.initComponent.call(this); + this.opts = new Deluge.OptionsManager(); - onDisable: function() { + var fieldset = this.add({ + xtype: 'fieldset', + title: ('Dynamic tracker list (optional)'), + autoHeight: true, + autoWidth: true, + }); + this.opts.bind('dynamic_trackerlist_url', fieldset.add({ + xtype: 'textarea', + fieldLabel: ('tracker list URL'), + anchor: '100%', + name: 'dynamic_trackerlist_url', + autoWidth: true, + })); + fieldset.add({ + xtype: 'displayfield', + fieldLabel: 'E.g.', + value: 'https://newtrackon.com/api/stable or
https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt', + }); + this.opts.bind('dynamic_trackers_update_interval', fieldset.add({ + xtype: 'textfield', + fieldLabel: ('reload every X days'), + anchor: '100%', + name: 'dynamic_trackers_update_interval', + autoWidth: true, + })); + + fieldset = this.add({ + xtype: 'fieldset', + title: ('Default trackers'), + autoHeight: true, + autoWidth: true, + }); + this.opts.bind('trackers_ta', fieldset.add({ + xtype: 'textarea', + fieldLabel: (''), + anchor: '100%', + name: 'trackers_ta', + height: '200px', + autoWidth: true, + })); + + var applyToExisting = this.add({ + fieldLabel: _(''), + name: 'trackers_apply', + xtype: 'container', + layout: 'hbox', + items: [{ + xtype: 'button', + text: 'Apply to existing torrents', + }] + }); + + applyToExisting.getComponent(0).setHandler(this.onApplyToExisting, this); + deluge.preferences.on('show', this.onPreferencesShow, this); }, - onEnable: function() { + onPreferencesShow: function() { + deluge.client.defaulttrackers.get_config({ + success: function(config) { + config.trackers_ta = config.trackers + .map(function(tracker){return tracker.url}) + .join('\n'); + + this.opts.set(config); + }, + scope: this, + }); + }, + onApply: function(e) { + var changed = this.opts.getDirty(); + if (!Ext.isObjectEmpty(changed)) { + if (Ext.isDefined(changed['trackers_ta'])) { + changed.trackers = changed.trackers_ta + .split('\n') + .filter(function(line){return line != ''}) + .map(function(url){return {'url': url}}); + } + + deluge.client.defaulttrackers.set_config(changed, { + success: this.onSetConfig, + scope: this, + }); + } + }, + onSetConfig: function() { + this.opts.commit(); + }, + onApplyToExisting: function() { + deluge.client.defaulttrackers.apply_to_existing({ + success: function() { + Ext.Msg.alert('Success', 'Default trackers applied to existing torrents'); + }, + scope: this, + }); + } +}); + +DefaultTrackersPlugin = Ext.extend(Deluge.Plugin, { + name: "Default Trackers", + onDisable: function() { + deluge.preferences.removePage(this.prefsPage); + }, + + onEnable: function() { + this.prefsPage = new DefaultTrackersPanel(); + this.prefsPage = deluge.preferences.addPage(this.prefsPage); } }); -new DefaultTrackersPlugin(); + +Deluge.registerPlugin("Default Trackers", DefaultTrackersPlugin); diff --git a/defaulttrackers/data/options.ui b/defaulttrackers/data/options.ui new file mode 100644 index 0000000..1b8a089 --- /dev/null +++ b/defaulttrackers/data/options.ui @@ -0,0 +1,156 @@ + + + + + + False + 10 + Tracker Properties + True + 250 + dialog + + + + True + False + 2 + + + True + False + 0 + 0 + none + + + True + False + + + 134 + True + True + + + + + + + True + False + <b>Trackers</b> (one per line) + True + + + + + True + False + 0 + + + + + True + False + + + True + False + 0 + 5 + 5 + +<b>Examples:</b> + +udp://tracker.opentrackr.org:1337/announce +udp://tracker.openbittorrent.com:80 +udp://tracker.publicbt.com:80 +udp://tracker.istole.it:80 +udp://tracker.ccc.de:80 +http://tracker.coppersurfer.tk:6969 +udp://open.demonii.com:1337 +http://tracker.baravik.org:6970/announce +http://tracker2.wasabii.com.tw:6969/announce +udp://9.rarbg.me:2710/announce +udp://tracker.zer0day.to:1337/announce + + True + True + + + True + True + 0 + + + + + True + True + 1 + + + + + True + False + center + + + gtk-cancel + True + True + False + True + + + + False + False + 0 + + + + + gtk-add + True + True + False + True + + + + False + False + 1 + + + + + gtk-apply + True + True + False + True + + + + False + False + 2 + + + + + False + True + end + 2 + + + + + + diff --git a/defaulttrackers/gtk3ui.py b/defaulttrackers/gtk3ui.py index a474273..b40bb2c 100644 --- a/defaulttrackers/gtk3ui.py +++ b/defaulttrackers/gtk3ui.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # gtkui.py # -# Copyright (C) 2013-2019 Ștefan Talpalaru +# Copyright (C) 2013-2022 Ștefan Talpalaru # # Basic plugin template created by: # Copyright (C) 2008 Martijn Voncken @@ -41,6 +41,7 @@ from __future__ import absolute_import, unicode_literals from gi.repository import Gtk import logging +import os from deluge.ui.client import client from deluge.plugins.pluginbase import Gtk3PluginBase @@ -67,7 +68,7 @@ def __init__(self, gtkui): self.gtkui = gtkui def show(self, options=None, item_id=None, item_index=None): - self.builder = Gtk.Builder.new_from_file(get_resource("config.ui")) + self.builder = Gtk.Builder.new_from_file(get_resource("options.ui")) self.builder.connect_signals({ "on_opts_add_button_clicked": self.on_add, "on_opts_apply_button_clicked": self.on_apply, @@ -110,6 +111,7 @@ def on_add(self, widget): if not self.in_store(url): self.gtkui.store.append([url]) self.gtkui.trackers.append({"url": url}) + self.dialog.response(Gtk.ResponseType.DELETE_EVENT) except Exception as err: dialogs.ErrorDialog("Error", str(err), self.dialog).run() @@ -117,7 +119,7 @@ def generate_opts(self): # generate options dict based on gtk objects buffer = self.builder.get_object("tracker_entry").get_buffer() options = { - "urls": buffer.get_text(*buffer.get_bounds()).split(), + "urls": buffer.get_text(*buffer.get_bounds(), False).split(), } if len(options["urls"]) == 0: raise Exception("no URLs") @@ -128,6 +130,7 @@ def on_apply(self, widget): options = self.generate_opts() self.gtkui.store[self.item_id][0] = options["urls"][0] self.gtkui.trackers[self.item_index]["url"] = options["urls"][0] + self.dialog.response(Gtk.ResponseType.DELETE_EVENT) except Exception as err: dialogs.ErrorDialog("Error", str(err), self.dialog).run() @@ -178,9 +181,12 @@ def on_apply_prefs(self): update_interval = int(self.builder.get_object("tracker_list_update_interval").get_text() or 1) except: update_interval = 1 + tracker_list_buffer = self.builder.get_object("tracker_list_url").get_buffer() + start_iter, end_iter = tracker_list_buffer.get_bounds() + tracker_list_text = tracker_list_buffer.get_text(start_iter, end_iter, False) self.config.update({ "trackers": [{"url": str(row[0])} for row in self.store], - "dynamic_trackerlist_url": self.builder.get_object("tracker_list_url").get_text(), + "dynamic_trackerlist_url": tracker_list_text, "dynamic_trackers_update_interval": update_interval, }) client.defaulttrackers.set_config(self.config) @@ -193,7 +199,7 @@ def cb_get_config(self, config): self.config = config # dynamic tracker list if config["dynamic_trackerlist_url"]: - self.builder.get_object("tracker_list_url").set_text(config["dynamic_trackerlist_url"]) + self.builder.get_object("tracker_list_url").get_buffer().set_text(config["dynamic_trackerlist_url"]) self.builder.get_object("tracker_list_update_interval").set_text(str(config["dynamic_trackers_update_interval"])) # trackers self.trackers = list(config["trackers"]) diff --git a/defaulttrackers/gtkui.py b/defaulttrackers/gtkui.py index cdc2da9..9e8bc49 100644 --- a/defaulttrackers/gtkui.py +++ b/defaulttrackers/gtkui.py @@ -178,9 +178,12 @@ def on_apply_prefs(self): update_interval = int(self.glade.get_widget("tracker_list_update_interval").get_text() or 1) except: update_interval = 1 + tracker_list_buffer = self.glade.get_widget("tracker_list_url").get_buffer() + start_iter, end_iter = tracker_list_buffer.get_bounds() + tracker_list_text = tracker_list_buffer.get_text(start_iter, end_iter, False) self.config.update({ "trackers": [{"url": str(row[0])} for row in self.store], - "dynamic_trackerlist_url": self.glade.get_widget("tracker_list_url").get_text(), + "dynamic_trackerlist_url": tracker_list_text, "dynamic_trackers_update_interval": update_interval, }) client.defaulttrackers.set_config(self.config) @@ -193,7 +196,7 @@ def cb_get_config(self, config): self.config = config # dynamic tracker list if config["dynamic_trackerlist_url"]: - self.glade.get_widget("tracker_list_url").set_text(config["dynamic_trackerlist_url"]) + self.glade.get_widget("tracker_list_url").get_buffer().set_text(config["dynamic_trackerlist_url"]) self.glade.get_widget("tracker_list_update_interval").set_text(str(config["dynamic_trackers_update_interval"])) # trackers self.trackers = list(config["trackers"]) diff --git a/defaulttrackers/webui.py b/defaulttrackers/webui.py index c717d38..f0f4a71 100644 --- a/defaulttrackers/webui.py +++ b/defaulttrackers/webui.py @@ -1,7 +1,7 @@ -# +# -*- coding: utf-8 -*- # webui.py # -# Copyright (C) 2013-2019 Stefan Talpalaru +# Copyright (C) 2013-2022 Ștefan Talpalaru # # Basic plugin template created by: # Copyright (C) 2008 Martijn Voncken @@ -38,22 +38,14 @@ # statement from all source files in the program, then also delete it here. # -from __future__ import absolute_import, unicode_literals -import logging -from deluge.ui.client import client -from deluge import component +from __future__ import absolute_import from deluge.plugins.pluginbase import WebPluginBase from .common import get_resource -log = logging.getLogger(__name__) - class WebUI(WebPluginBase): scripts = [get_resource("defaulttrackers.js")] - def enable(self): - pass - - def disable(self): - pass + def __init__(self, plugin_name): + super(WebUI, self).__init__(plugin_name) diff --git a/egg/DefaultTrackers-0.2-py2.7.egg b/egg/DefaultTrackers-0.2-py2.7.egg deleted file mode 100644 index bd59911..0000000 Binary files a/egg/DefaultTrackers-0.2-py2.7.egg and /dev/null differ diff --git a/egg/DefaultTrackers-0.2-py3.6.egg b/egg/DefaultTrackers-0.2-py3.6.egg deleted file mode 100644 index dd44ad3..0000000 Binary files a/egg/DefaultTrackers-0.2-py3.6.egg and /dev/null differ diff --git a/egg/DefaultTrackers-0.6-py3.12.egg b/egg/DefaultTrackers-0.6-py3.12.egg new file mode 100644 index 0000000..43a56c8 Binary files /dev/null and b/egg/DefaultTrackers-0.6-py3.12.egg differ diff --git a/setup.py b/setup.py index e668141..1c41832 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # # setup.py # -# Copyright (C) 2013-2019 Ștefan Talpalaru +# Copyright (C) 2013-2024 Ștefan Talpalaru # # Basic plugin template created by: # Copyright (C) 2008 Martijn Voncken @@ -45,10 +45,10 @@ __plugin_name__ = "DefaultTrackers" __author__ = u"Ștefan Talpalaru" __author_email__ = "stefantalpalaru@yahoo.com" -__version__ = "0.2" +__version__ = "0.7" __url__ = "https://github.com/stefantalpalaru/deluge-default-trackers" __license__ = "GPLv3" -__description__ = "Add a list of default trackers to all the public torrents" +__description__ = "Add default trackers to all public torrents" __long_description__ = """ Create a list of default trackers that will be added to new public torrents (and old ones after restarting Deluge). The plugin will not duplicate existing @@ -71,10 +71,8 @@ long_description=__long_description__ if __long_description__ else __description__, packages=[__plugin_name__.lower()], - package_data = __pkg_data__, - install_requires=[ - 'six>=1.12', - ], + package_data=__pkg_data__, + install_requires=[], entry_points=""" [deluge.plugin.core]