deluge-webapi 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ deluge-webapi authors
2
+ =====================
3
+
4
+ Created by Igor `idle sign` Starikov.
5
+
6
+
7
+ Contributors
8
+ ------------
9
+
10
+ theonedemon <https://github.com/theonedemon>
11
+ Daniel Marcos <https://github.com/dmarcosl>
12
+ Brandon Gillis <https://github.com/BrandonGillis>
13
+ Ivan Filippov <https://github.com/ivanfilippov>
14
+ griest024 <https://github.com/griest024>
@@ -0,0 +1,40 @@
1
+ deluge-webapi changelog
2
+ =======================
3
+
4
+ v0.5.0
5
+ ------
6
+ * CORS patching is moved into the WebUI part of the plugin, where the JSON component lives, fixing KeyError: 'JSON' when the WebUI and the daemon run in separate processes; changing CORS settings now requires a WebUI restart (close #36)
7
+ * The plugin package is renamed into `deluge_webapi` to comply with the Deluge 2 plugin naming requirements (close #25)
8
+
9
+ v0.4.0
10
+ ------
11
+ * Added support for Deluge 2
12
+ * get_torrents will now accept a comma separated string in addition to accepting a list for field filters
13
+ * Future support for Deluge 1.x is no longer possible, please use the older eggs
14
+
15
+ v0.3.1
16
+ ------
17
+ * Added CORS settings for WebUI.
18
+
19
+
20
+ v0.3.0
21
+ ------
22
+ * Add CORS support.
23
+ * Added CORS settings for GTK.
24
+
25
+
26
+ v0.2.1
27
+ ------
28
+ * Fixed add_rorrent download_location not honored (see #8).
29
+
30
+
31
+ v0.2.0
32
+ ------
33
+ + Plugin updated to work with Deluge 1.3.15
34
+ + `add_torrent` now returns torrent id (hash) (see #5).
35
+ + `remove_torrent` now returns the result of core function instad of `True`.
36
+
37
+
38
+ v0.1.0
39
+ ------
40
+ + Basic functionality.
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2014-2019 Igor `idle sign` Starikov
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of the deluge-webapi nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,12 @@
1
+ include AUTHORS
2
+ include CHANGELOG
3
+ include LICENSE
4
+ include README.rst
5
+
6
+ include docs/Makefile
7
+ recursive-include docs *.rst
8
+ recursive-include docs *.py
9
+
10
+ recursive-exclude * __pycache__
11
+ recursive-exclude * *.py[co]
12
+ recursive-exclude * empty
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: deluge-webapi
3
+ Version: 0.5.0
4
+ Summary: Plugin for Deluge WebUI providing sane JSON API.
5
+ Author-email: Igor `idle sign` Starikov <idlesign@yandex.ru>
6
+ License: BSD 3-Clause License
7
+ Project-URL: Homepage, https://github.com/idlesign/deluge-webapi
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Environment :: Plugins
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE
18
+ License-File: AUTHORS
19
+ Dynamic: license-file
20
+
21
+ deluge-webapi
22
+ =============
23
+ https://github.com/idlesign/deluge-webapi
24
+
25
+
26
+ .. image:: https://img.shields.io/pypi/v/deluge-webapi.svg
27
+ :target: https://pypi.python.org/pypi/deluge-webapi
28
+
29
+
30
+ Description
31
+ -----------
32
+
33
+ *Plugin for Deluge WebUI providing sane JSON API.*
34
+
35
+ Exposes some sane JSON API through Deluge WebUI (missing out of the box or restyled) so that you can manipulate
36
+ Deluge from your programs using HTTP requests.
37
+
38
+ Supported methods:
39
+
40
+ * ``get_torrents``
41
+ * ``add_torrent`` (magnet or file)
42
+ * ``remove_torrent``
43
+ * ``get_api_version``
44
+
45
+ Deluge is a lightweight, Free Software, cross-platform BitTorrent client. Download it at http://deluge-torrent.org/
46
+
47
+ .. note:: Use egg version 0.4.0 or higher for Deluge 2
48
+ .. note:: Use egg version 0.3.2 or lower for Deluge 1.x
49
+
50
+
51
+ Installation
52
+ ------------
53
+
54
+ 1. Get plugin egg file, from ``dist/`` (https://github.com/idlesign/deluge-webapi/tree/master/dist) directory.
55
+
56
+ 2. Open Deluge Web UI, go to "Preferences -> Plugins -> Install plugin" and choose egg file.
57
+
58
+ 3. Activate ``WebAPI`` plugin.
59
+
60
+ 4. In case you use Client-Server setup, you'll need to install the plugin both on client and server, see - https://dev.deluge-torrent.org/wiki/Plugins#Client-ServerSetups
61
+
62
+
63
+ .. note::
64
+
65
+ To build .egg file from source code yourself use ``python setup.py bdist_egg`` command in source code directory.
66
+
67
+
68
+ Documentation
69
+ -------------
70
+
71
+ http://deluge-webapi.readthedocs.org/
@@ -0,0 +1,51 @@
1
+ deluge-webapi
2
+ =============
3
+ https://github.com/idlesign/deluge-webapi
4
+
5
+
6
+ .. image:: https://img.shields.io/pypi/v/deluge-webapi.svg
7
+ :target: https://pypi.python.org/pypi/deluge-webapi
8
+
9
+
10
+ Description
11
+ -----------
12
+
13
+ *Plugin for Deluge WebUI providing sane JSON API.*
14
+
15
+ Exposes some sane JSON API through Deluge WebUI (missing out of the box or restyled) so that you can manipulate
16
+ Deluge from your programs using HTTP requests.
17
+
18
+ Supported methods:
19
+
20
+ * ``get_torrents``
21
+ * ``add_torrent`` (magnet or file)
22
+ * ``remove_torrent``
23
+ * ``get_api_version``
24
+
25
+ Deluge is a lightweight, Free Software, cross-platform BitTorrent client. Download it at http://deluge-torrent.org/
26
+
27
+ .. note:: Use egg version 0.4.0 or higher for Deluge 2
28
+ .. note:: Use egg version 0.3.2 or lower for Deluge 1.x
29
+
30
+
31
+ Installation
32
+ ------------
33
+
34
+ 1. Get plugin egg file, from ``dist/`` (https://github.com/idlesign/deluge-webapi/tree/master/dist) directory.
35
+
36
+ 2. Open Deluge Web UI, go to "Preferences -> Plugins -> Install plugin" and choose egg file.
37
+
38
+ 3. Activate ``WebAPI`` plugin.
39
+
40
+ 4. In case you use Client-Server setup, you'll need to install the plugin both on client and server, see - https://dev.deluge-torrent.org/wiki/Plugins#Client-ServerSetups
41
+
42
+
43
+ .. note::
44
+
45
+ To build .egg file from source code yourself use ``python setup.py bdist_egg`` command in source code directory.
46
+
47
+
48
+ Documentation
49
+ -------------
50
+
51
+ http://deluge-webapi.readthedocs.org/
@@ -0,0 +1,28 @@
1
+ VERSION = (0, 5, 0)
2
+
3
+
4
+ try:
5
+ from deluge.plugins.init import PluginInitBase
6
+
7
+ class CorePlugin(PluginInitBase):
8
+ def __init__(self, plugin_name):
9
+ from .core import Core as _plugin_cls
10
+ self._plugin_cls = _plugin_cls
11
+ super(CorePlugin, self).__init__(plugin_name)
12
+
13
+ class Gtk3UIPlugin(PluginInitBase):
14
+ def __init__(self, plugin_name):
15
+ from .gtk3ui import Gtk3UI as _plugin_cls
16
+ self._plugin_cls = _plugin_cls
17
+ super(Gtk3UIPlugin, self).__init__(plugin_name)
18
+
19
+ class WebUIPlugin(PluginInitBase):
20
+ def __init__(self, plugin_name):
21
+ from .webui import WebUI as _plugin_cls
22
+ self._plugin_cls = _plugin_cls
23
+ super(WebUIPlugin, self).__init__(plugin_name)
24
+
25
+
26
+ except ImportError:
27
+ # That's what we do when this import prevents docs from being built.
28
+ pass
@@ -0,0 +1,17 @@
1
+ from importlib.resources import files
2
+
3
+ from packaging import version
4
+
5
+ from deluge.common import get_version
6
+
7
+ DEFAULT_PREFS = {
8
+ "enable_cors": False,
9
+ "allowed_origin": [],
10
+ }
11
+
12
+ # twisted uses byte strings in deluge version 2.1.0 onward
13
+ use_bytes = version.parse(get_version()) >= version.parse("2.1.0")
14
+
15
+
16
+ def get_resource(filename):
17
+ return str(files("deluge_webapi").joinpath("data", filename))
@@ -0,0 +1,35 @@
1
+ import logging
2
+
3
+ import deluge.configmanager
4
+ from deluge.core.rpcserver import export
5
+ from deluge.plugins.pluginbase import CorePluginBase
6
+
7
+ from .common import DEFAULT_PREFS
8
+
9
+ LOGGER = logging.getLogger(__name__)
10
+
11
+
12
+ class Core(CorePluginBase):
13
+
14
+ def enable(self):
15
+ LOGGER.info('Enabling WebAPI plugin CORE ...')
16
+
17
+ self.config = deluge.configmanager.ConfigManager('webapi.conf', DEFAULT_PREFS)
18
+
19
+ def disable(self):
20
+ LOGGER.info('Disabling WebAPI plugin CORE ...')
21
+
22
+ self.config.save()
23
+
24
+ @export
25
+ def set_config(self, config):
26
+ """sets the config dictionary"""
27
+
28
+ for key in config.keys():
29
+ self.config[key] = config[key]
30
+
31
+ self.config.save()
32
+
33
+ @export
34
+ def get_config(self):
35
+ return self.config.config
@@ -0,0 +1,146 @@
1
+ <?xml version="1.0"?>
2
+ <interface>
3
+ <!-- interface-requires gtk+ 2.24 -->
4
+ <!-- interface-naming-policy toplevel-contextual -->
5
+ <object class="GtkWindow" id="window1">
6
+ <property name="can_focus">False</property>
7
+ <child>
8
+ <object class="GtkHBox" id="prefs_box">
9
+ <property name="visible">True</property>
10
+ <property name="can_focus">False</property>
11
+ <child>
12
+ <object class="GtkLayout" id="layout1">
13
+ <property name="visible">True</property>
14
+ <property name="can_focus">False</property>
15
+ <child>
16
+ <object class="GtkCheckButton" id="enable_cors">
17
+ <property name="label" translatable="yes">enable CORS (cross-origin request)</property>
18
+ <property name="width_request">247</property>
19
+ <property name="height_request">80</property>
20
+ <property name="visible">True</property>
21
+ <property name="can_focus">True</property>
22
+ <property name="receives_default">False</property>
23
+ <property name="use_action_appearance">False</property>
24
+ <property name="draw_indicator">True</property>
25
+ </object>
26
+ <packing>
27
+ <property name="x">18</property>
28
+ </packing>
29
+ </child>
30
+ <child>
31
+ <object class="GtkLabel" id="label1">
32
+ <property name="width_request">152</property>
33
+ <property name="height_request">21</property>
34
+ <property name="visible">True</property>
35
+ <property name="can_focus">False</property>
36
+ <property name="xalign">0</property>
37
+ <property name="yalign">0</property>
38
+ <property name="label" translatable="yes">Allowed domain name :</property>
39
+ </object>
40
+ <packing>
41
+ <property name="x">19</property>
42
+ <property name="y">60</property>
43
+ </packing>
44
+ </child>
45
+ <child>
46
+ <object class="GtkEntry" id="new_domain">
47
+ <property name="width_request">153</property>
48
+ <property name="height_request">27</property>
49
+ <property name="visible">True</property>
50
+ <property name="can_focus">True</property>
51
+ <property name="invisible_char">&#x25CF;</property>
52
+ <property name="primary_icon_activatable">False</property>
53
+ <property name="secondary_icon_activatable">False</property>
54
+ <property name="primary_icon_sensitive">True</property>
55
+ <property name="secondary_icon_sensitive">True</property>
56
+ </object>
57
+ <packing>
58
+ <property name="x">67</property>
59
+ <property name="y">286</property>
60
+ </packing>
61
+ </child>
62
+ <child>
63
+ <object class="GtkButton" id="add_button">
64
+ <property name="label" translatable="yes">Add</property>
65
+ <property name="width_request">49</property>
66
+ <property name="height_request">27</property>
67
+ <property name="visible">True</property>
68
+ <property name="can_focus">True</property>
69
+ <property name="receives_default">True</property>
70
+ <property name="use_action_appearance">False</property>
71
+ </object>
72
+ <packing>
73
+ <property name="x">16</property>
74
+ <property name="y">286</property>
75
+ </packing>
76
+ </child>
77
+ <child>
78
+ <object class="GtkTreeView" id="listview_allowed_domains">
79
+ <property name="width_request">263</property>
80
+ <property name="height_request">194</property>
81
+ <property name="visible">True</property>
82
+ <property name="can_focus">True</property>
83
+ </object>
84
+ <packing>
85
+ <property name="x">16</property>
86
+ <property name="y">84</property>
87
+ </packing>
88
+ </child>
89
+ <child>
90
+ <object class="GtkScrolledWindow" id="scrolledwindow_allowed_domains">
91
+ <property name="width_request">356</property>
92
+ <property name="height_request">197</property>
93
+ <property name="visible">True</property>
94
+ <property name="can_focus">True</property>
95
+ <property name="hscrollbar_policy">automatic</property>
96
+ <property name="vscrollbar_policy">automatic</property>
97
+ <child>
98
+ <placeholder/>
99
+ </child>
100
+ </object>
101
+ <packing>
102
+ <property name="x">16</property>
103
+ <property name="y">81</property>
104
+ </packing>
105
+ </child>
106
+ <child>
107
+ <object class="GtkButton" id="remove_button">
108
+ <property name="label" translatable="yes">Remove</property>
109
+ <property name="width_request">69</property>
110
+ <property name="height_request">27</property>
111
+ <property name="visible">True</property>
112
+ <property name="can_focus">True</property>
113
+ <property name="receives_default">True</property>
114
+ <property name="use_action_appearance">False</property>
115
+ </object>
116
+ <packing>
117
+ <property name="x">221</property>
118
+ <property name="y">286</property>
119
+ </packing>
120
+ </child>
121
+ <child>
122
+ <object class="GtkButton" id="remove_all_button">
123
+ <property name="label" translatable="yes">Remove all</property>
124
+ <property name="width_request">82</property>
125
+ <property name="height_request">27</property>
126
+ <property name="visible">True</property>
127
+ <property name="can_focus">True</property>
128
+ <property name="receives_default">True</property>
129
+ <property name="use_action_appearance">False</property>
130
+ </object>
131
+ <packing>
132
+ <property name="x">290</property>
133
+ <property name="y">286</property>
134
+ </packing>
135
+ </child>
136
+ </object>
137
+ <packing>
138
+ <property name="expand">True</property>
139
+ <property name="fill">True</property>
140
+ <property name="position">0</property>
141
+ </packing>
142
+ </child>
143
+ </object>
144
+ </child>
145
+ </object>
146
+ </interface>
@@ -0,0 +1,198 @@
1
+
2
+ WebApiPanel = Ext.extend(Ext.form.FormPanel, {
3
+
4
+
5
+ constructor: function(config) {
6
+
7
+ config = Ext.apply({
8
+ title: _('WebAPI'),
9
+ border: false,
10
+ labelWidth: 10,
11
+ autoHeight: true
12
+ }, config);
13
+
14
+ WebApiPanel.superclass.constructor.call(this, config);
15
+ },
16
+ initComponent: function() {
17
+ WebApiPanel.superclass.initComponent.call(this);
18
+ this.opts = new Deluge.OptionsManager();
19
+
20
+ var fieldset = this.add({
21
+ xtype: 'fieldset',
22
+ title: _('WebAPI Settings'),
23
+ defaultType: 'checkbox',
24
+ autoHeight: true
25
+ });
26
+
27
+ this.opts.bind('enable_cors', fieldset.add({
28
+ name: 'enable_cors',
29
+ id: 'enable_cors',
30
+ height: 22,
31
+ fieldLabel: '',
32
+ labelSeparator: '',
33
+ boxLabel: _('Enable CORS (cross-origin request)')
34
+ }));
35
+
36
+ this.list = this.add({
37
+ xtype: 'listview',
38
+ height: 150,
39
+ store: new Ext.data.ArrayStore({
40
+ fields: [
41
+ { name: 'domain', mapping: 0 }
42
+ ]
43
+ }),
44
+ columns: [{
45
+ id: 'domain',
46
+ header: _('Allowed domain'),
47
+ width: 1,
48
+ sortable: true,
49
+ dataIndex: 'domain'
50
+ }],
51
+ singleSelect: true,
52
+ autoExpandColumn: 'domain',
53
+ listeners: {
54
+ selectionchange: { fn: this.onDomainSelect, scope: this }
55
+ }
56
+ });
57
+
58
+ this.panel = this.add({
59
+ region: 'center',
60
+ autoScroll: true,
61
+ items: [this.list]
62
+ });
63
+
64
+ this.domain_name = this.opts.bind('domain_name', fieldset.add({
65
+ fieldLabel: '',
66
+ xtype: 'textfield',
67
+ anchor: '50%',
68
+ name: 'domain_name',
69
+ id: 'domain_name',
70
+ autoWidth: true,
71
+ value: ''
72
+ }));
73
+
74
+ this.toolbar = this.add({
75
+ fieldLabel: _(''),
76
+ name: 'list_toolbar',
77
+ xtype: 'container',
78
+ layout: 'hbox',
79
+ items: [
80
+ {
81
+ xtype: 'button',
82
+ text: 'Add'
83
+ },
84
+ this.domain_name,
85
+ {
86
+ xtype: 'button',
87
+ text: 'Remove'
88
+ },
89
+ {
90
+ xtype: 'button',
91
+ text: 'Remove all'
92
+ }
93
+ ]
94
+ });
95
+
96
+ this.toolbar.getComponent(0).setHandler(this.addDomain, this);
97
+ this.toolbar.getComponent(2).setHandler(this.removeDomain, this);
98
+ this.toolbar.getComponent(3).setHandler(this.removeAllDomain, this);
99
+
100
+ deluge.preferences.on('show', this.onPreferencesShow, this);
101
+ },
102
+
103
+ removeDomain: function() {
104
+ if (!this.selectedDomain) return;
105
+ var index = this.list.getStore().find('domain', this.selectedDomain);
106
+ if (index == -1) return;
107
+ this.list.getStore().removeAt(index);
108
+ index = this.allowedDomains.indexOf(this.selectedDomain);
109
+ if (index !== -1) this.allowedDomains.splice(index, 1);
110
+ this.selectedDomain = false;
111
+ },
112
+
113
+ removeAllDomain: function() {
114
+ this.list.getStore().removeAll();
115
+ this.allowedDomains = [];
116
+ this.selectedDomain = false;
117
+ },
118
+
119
+ onDomainSelect: function (dv, selections) {
120
+ if (selections.length === 0) return;
121
+ var r = dv.getRecords(selections)[0];
122
+ this.selectedDomain = r.get('domain');
123
+ },
124
+
125
+ addDomain: function() {
126
+ var changed = this.opts.getDirty();
127
+ if (!Ext.isObjectEmpty(changed)) {
128
+ if (!Ext.isEmpty(changed['domain_name'])) {
129
+ changed['domain_name'] = changed['domain_name'];
130
+ this.allowedDomains.push(changed['domain_name']);
131
+ this.updateDomainsGrid();
132
+ }
133
+ }
134
+ },
135
+
136
+ updateDomainsGrid: function () {
137
+ var Domains = [];
138
+ Ext.each(this.allowedDomains, function (domain) {
139
+ Domains.push([domain]);
140
+ }, this);
141
+ this.list.getStore().loadData(Domains);
142
+ },
143
+
144
+ onPreferencesShow: function () {
145
+ deluge.client.webapi.get_config({
146
+ success: function (config) {
147
+ if (!Ext.isEmpty(config['enable_cors'])) {
148
+ config['enable_cors'] = config['enable_cors'];
149
+ }
150
+ if (!Ext.isEmpty(config['allowed_origin'])) {
151
+ config['allowed_origin'] = config['allowed_origin'];
152
+ }
153
+ this.allowedDomains = config['allowed_origin'];
154
+ this.updateDomainsGrid();
155
+ this.opts.set(config);
156
+ },
157
+ scope: this
158
+ });
159
+ },
160
+
161
+ onApply: function(e) {
162
+ var changed = this.opts.getDirty();
163
+ if (!Ext.isObjectEmpty(changed)) {
164
+ if (!Ext.isEmpty(changed['enable_cors'])) {
165
+ changed['enable_cors'] = changed['enable_cors'];
166
+ }
167
+ deluge.client.webapi.set_config(changed, {
168
+ success: this.onSetConfig,
169
+ scope: this
170
+ });
171
+ }
172
+ var config = {};
173
+ config['allowed_origin'] = this.allowedDomains;
174
+ console.log(config);
175
+ deluge.client.webapi.set_config(config);
176
+ },
177
+
178
+ onSetConfig: function() {
179
+ this.opts.commit();
180
+ }
181
+
182
+ });
183
+
184
+
185
+ WebApiPlugin = Ext.extend(Deluge.Plugin, {
186
+
187
+ name: 'WebAPI',
188
+
189
+ onDisable: function() {
190
+ deluge.preferences.removePage(this.prefsPage);
191
+ },
192
+
193
+ onEnable: function() {
194
+ this.prefsPage = deluge.preferences.addPage(new WebApiPanel());
195
+ }
196
+ });
197
+
198
+ Deluge.registerPlugin('WebAPI', WebApiPlugin);