diff --git a/.gitignore b/.gitignore
index 2d7481d..5d4547d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,7 @@ _trial_temp
*.komodoproject
docs/_build*
.env*
-
+EGG-INFO
# for bundling
thomas
@@ -32,4 +32,4 @@ six.py
rarfile.py
rfc6266.py
lepl
-pytz
\ No newline at end of file
+pytz
diff --git a/create-egg.sh b/create-egg.sh
old mode 100644
new mode 100755
index 7d5d2d1..9f4ffc0
--- a/create-egg.sh
+++ b/create-egg.sh
@@ -1,9 +1,9 @@
-virtualenv .env-egg
+python3 -m venv .env-egg
.env-egg/bin/pip install -U thomas
-ln -s .env-egg/lib/python2.7/site-packages/thomas .
-ln -s .env-egg/lib/python2.7/site-packages/rarfile.py .
-ln -s .env-egg/lib/python2.7/site-packages/six.py .
-ln -s .env-egg/lib/python2.7/site-packages/rfc6266.py .
-ln -s .env-egg/lib/python2.7/site-packages/lepl .
-ln -s .env-egg/lib/python2.7/site-packages/pytz .
-.env-egg/bin/python setup.py bdist_egg
\ No newline at end of file
+ln -s .env-egg/lib/python*/site-packages/thomas .
+ln -s .env-egg/lib/python*/site-packages/rarfile.py .
+ln -s .env-egg/lib/python*/site-packages/six.py .
+ln -s .env-egg/lib/python*/site-packages/rfc6266.py .
+ln -s .env-egg/lib/python*/site-packages/lepl .
+ln -s .env-egg/lib/python*/site-packages/pytz .
+.env-egg/bin/python setup.py bdist_egg
diff --git a/examples/http-api/streamtorrent.py b/examples/http-api/streamtorrent.py
index 72800ff..7ae9a7f 100644
--- a/examples/http-api/streamtorrent.py
+++ b/examples/http-api/streamtorrent.py
@@ -1,3 +1,5 @@
+import argparse
+
import requests
class FailedToStreamException(Exception):
@@ -44,6 +46,8 @@ def stream_torrent(remote_control_url, infohash=None, path=None, wait_for_end_pi
data = r.json()
if data['status'] == 'success':
return data['url']
+ else:
+ raise FailedToStreamException('Request failed: %r' % (data, ))
if torrent_body:
r = requests.post(url, auth=(username, password), params=params, data=torrent_body)
@@ -53,5 +57,40 @@ def stream_torrent(remote_control_url, infohash=None, path=None, wait_for_end_pi
data = r.json()
if data['status'] == 'success':
return data['url']
+ else:
+ raise FailedToStreamException('Request failed: %r' % (data, ))
raise FailedToStreamException('Streaming was never successful')
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="Stream some torrents")
+ parser.add_argument('url', help="Full API Url including auth info")
+ parser.add_argument('--infohash', nargs='?', help="Infohash of torrent to stream")
+ parser.add_argument('--path', nargs='?', help="Path to file within the torrent to stream")
+ parser.add_argument('--label', nargs='?', help="Label to add the torrent with")
+ parser.add_argument('--torrent', nargs='?', help="Path to the torrent to stream", type=argparse.FileType(mode='rb'))
+ parser.add_argument('--skip_wait_for_end_pieces', help="Wait until client downloaded the first and last piece of the torrent", action='store_false')
+
+
+ args = parser.parse_args()
+
+ kwargs = {
+ 'remote_control_url': args.url,
+ 'wait_for_end_pieces': args.skip_wait_for_end_pieces
+ }
+
+ if args.infohash:
+ kwargs['infohash'] = args.infohash
+
+ if args.path:
+ kwargs['path'] = args.path
+
+ if args.label:
+ kwargs['label'] = args.label
+
+ if args.torrent:
+ kwargs['torrent_body'] = args.torrent.read()
+
+ result = stream_torrent(**kwargs)
+ print('URL %s' % (result, ))
diff --git a/setup.py b/setup.py
index 7324856..98ca2cf 100644
--- a/setup.py
+++ b/setup.py
@@ -71,7 +71,6 @@ REQUIREMENTS_PACKAGES = [
]
REQUIREMENTS_MODULES = [
- 'six',
'rarfile',
'rfc6266',
]
@@ -96,7 +95,9 @@ setup(
%s = %s:CorePlugin
[deluge.plugin.gtkui]
%s = %s:GtkUIPlugin
+ [deluge.plugin.gtk3ui]
+ %s = %s:Gtk3UIPlugin
[deluge.plugin.web]
%s = %s:WebUIPlugin
- """ % ((__plugin_name__, __plugin_name__.lower())*3)
+ """ % ((__plugin_name__, __plugin_name__.lower())*4)
)
diff --git a/streaming/__init__.py b/streaming/__init__.py
index cccd34e..e047688 100644
--- a/streaming/__init__.py
+++ b/streaming/__init__.py
@@ -42,20 +42,27 @@ from deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
- from core import Core as _plugin_cls
+ from .core import Core as _plugin_cls
self._plugin_cls = _plugin_cls
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
- from gtkui import GtkUI as _plugin_cls
+ from .gtkui import GtkUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(GtkUIPlugin, self).__init__(plugin_name)
+class Gtk3UIPlugin(PluginInitBase):
+ def __init__(self, plugin_name):
+ from .gtk3ui import Gtk3UI as PluginClass
+ self._plugin_cls = PluginClass
+ super(Gtk3UIPlugin, self).__init__(plugin_name)
+
+
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
- from webui import WebUI as _plugin_cls
+ from .webui import WebUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(WebUIPlugin, self).__init__(plugin_name)
diff --git a/streaming/core.py b/streaming/core.py
index b965941..8896052 100644
--- a/streaming/core.py
+++ b/streaming/core.py
@@ -37,6 +37,7 @@
# statement from all source files in the program, then also delete it here.
#
+import base64
import json
import logging
import os
@@ -57,6 +58,7 @@ from deluge.plugins.pluginbase import CorePluginBase
from twisted.internet import reactor, defer, task
from twisted.web import server, client
+from twisted.web.resource import Resource as TwistedResource
from thomas import router, Item, OutputBase
@@ -94,6 +96,7 @@ DEFAULT_PREFS = {
'ssl_source': 'daemon',
'ssl_priv_key_path': '',
'ssl_cert_path': '',
+ 'aggressive_prioritizing': False,
}
logger = logging.getLogger(__name__)
@@ -126,9 +129,10 @@ def get_torrent(infohash):
class Torrent(object):
- def __init__(self, torrent_handler, infohash):
+ def __init__(self, torrent_handler, infohash, aggressive_prioritizing=False):
self.torrent_handler = torrent_handler
self.infohash = infohash
+ self.aggressive_prioritizing = aggressive_prioritizing
self.filesets = {}
self.readers = {}
@@ -176,7 +180,7 @@ class Torrent(object):
if file_piece_count <= MIN_PIECE_COUNT_FOR_CHAIN_CONSIDERATION:
is_next_in_chain = True
- else:
+ elif self.readers:
best_reader_from_byte = max(reader[1] for reader in self.readers.values() if reader[1] <= from_byte)
best_reader_piece = best_reader_from_byte // self.piece_length
downloading_pieces = self.get_currently_downloading()
@@ -188,9 +192,11 @@ class Torrent(object):
piece_diff = best_reader_piece - unfinished_piece - 1
if unfinished_piece >= best_reader_piece or piece_diff / file_piece_count <= WITHIN_CHAIN_PERCENTAGE:
is_next_in_chain = True
+ else:
+ is_next_in_chain = True
- if not is_next_in_chain:
- logger.debug('Not a next-in-chain piece, setting priority now')
+ if not is_next_in_chain or self.aggressive_prioritizing:
+ logger.debug('Not a next-in-chain piece or aggressive prioritization enabled, setting priority now')
self.torrent.handle.set_piece_deadline(needed_piece, 0)
self.torrent.handle.piece_priority(needed_piece, MAX_PIECE_PRIORITY)
@@ -383,9 +389,10 @@ class Torrent(object):
class TorrentHandler(object):
- def __init__(self, reset_priorities_on_finish):
+ def __init__(self, reset_priorities_on_finish, aggressive_prioritizing=False):
self.torrents = {}
self.reset_priorities_on_finish = reset_priorities_on_finish
+ self.aggressive_prioritizing = aggressive_prioritizing
self.alerts = component.get("AlertManager")
self.alerts.register_handler("torrent_removed_alert", self.on_alert_torrent_removed)
@@ -488,7 +495,7 @@ class TorrentHandler(object):
def get_torrent(self, infohash):
if infohash not in self.torrents:
- self.torrents[infohash] = Torrent(self, infohash)
+ self.torrents[infohash] = Torrent(self, infohash, self.aggressive_prioritizing)
return self.torrents[infohash]
@defer.inlineCallbacks
@@ -612,58 +619,61 @@ class StreamResource(Resource):
@defer.inlineCallbacks
def render_POST(self, request):
- infohash = request.args.get('infohash')
- path = request.args.get('path')
- wait_for_end_pieces = bool(request.args.get('wait_for_end_pieces'))
- label = request.args.get('label')
+ infohash = request.args.get(b'infohash')
+ path = request.args.get(b'path')
+ wait_for_end_pieces = bool(request.args.get(b'wait_for_end_pieces'))
+ label = request.args.get(b'label')
if path:
- path = path[0]
+ path = path[0].decode('utf-8')
else:
path = None
if infohash:
- infohash = infohash[0]
+ infohash = infohash[0].decode('utf-8')
else:
- infohash = infohash
+ infohash = None
if label:
- label = label[0]
+ label = label[0].decode('utf-8')
else:
label = None
payload = request.content.read()
if not payload:
- defer.returnValue(json.dumps({'status': 'error', 'message': 'invalid torrent'}))
+ defer.returnValue(json.dumps({'status': 'error', 'message': 'invalid torrent'}).encode('utf-8'))
result = yield self.client.stream_torrent(infohash=infohash, filedump=payload, filepath_or_index=path, wait_for_end_pieces=wait_for_end_pieces, label=label)
- defer.returnValue(json.dumps(result))
+ defer.returnValue(json.dumps(result).encode('utf-8'))
@defer.inlineCallbacks
def render_GET(self, request):
- infohash = request.args.get('infohash')
- path = request.args.get('path')
- wait_for_end_pieces = bool(request.args.get('wait_for_end_pieces'))
+ infohash = request.args.get(b'infohash')
+ path = request.args.get(b'path')
+ wait_for_end_pieces = bool(request.args.get(b'wait_for_end_pieces'))
if not infohash:
- defer.returnValue(json.dumps({'status': 'error', 'message': 'missing infohash'}))
+ defer.returnValue(json.dumps({'status': 'error', 'message': 'missing infohash'}).encode('utf-8'))
- infohash = infohash[0]
+ infohash = infohash[0].decode('utf-8')
if path:
- path = path[0]
+ path = path[0].decode('utf-8')
else:
path = None
result = yield self.client.stream_torrent(infohash=infohash, filepath_or_index=path, wait_for_end_pieces=wait_for_end_pieces)
- defer.returnValue(json.dumps(result))
+ defer.returnValue(json.dumps(result).encode('utf-8'))
class Core(CorePluginBase):
listening = None
base_url = None
+ _is_enabled = False
+
def enable(self):
+ self._is_enabled = True
self.config = deluge.configmanager.ConfigManager("streaming.conf", DEFAULT_PREFS)
try:
@@ -680,18 +690,18 @@ class Core(CorePluginBase):
self.thomas_http_output = http_output
- resource = Resource()
- resource.putChild('file', http_output.resource)
+ resource = TwistedResource()
+ resource.putChild(b'file', http_output.resource)
if self.config['allow_remote']:
- resource.putChild('stream', StreamResource(username=self.config['remote_username'],
+ resource.putChild(b'stream', StreamResource(username=self.config['remote_username'],
password=self.config['remote_password'],
client=self))
- base_resource = Resource()
- base_resource.putChild('streaming', resource)
+ base_resource = TwistedResource()
+ base_resource.putChild(b'streaming', resource)
self.site = server.Site(base_resource)
- self.torrent_handler = TorrentHandler(self.config['download_only_streamed'] == False)
+ self.torrent_handler = TorrentHandler(self.config['download_only_streamed'] == False, self.config['aggressive_prioritizing'])
plugin_manager = component.get("CorePluginManager")
logger.warning('plugins %s' % (plugin_manager.get_enabled_plugins(), ))
@@ -748,6 +758,11 @@ class Core(CorePluginBase):
@defer.inlineCallbacks
def disable(self):
+ if not self._is_enabled:
+ defer.returnValue(None)
+
+ self._is_enabled = False
+
self.site.stopFactory()
self.torrent_handler.shutdown()
self.thomas_http_output.stop()
@@ -828,7 +843,7 @@ class Core(CorePluginBase):
core = component.get("Core")
try:
- yield core.add_torrent_file('file.torrent', filedump.encode('base64'), {'add_paused': True})
+ yield core.add_torrent_file('file.torrent', base64.b64encode(filedump), {'add_paused': True})
if label and 'Label' in component.get('CorePluginManager').get_enabled_plugins():
label_plugin = component.get('CorePlugin.Label')
if label not in label_plugin.get_labels():
diff --git a/streaming/data/config.ui b/streaming/data/config.ui
new file mode 100644
index 0000000..8f22af6
--- /dev/null
+++ b/streaming/data/config.ui
@@ -0,0 +1,712 @@
+
+
+
+
+
+
diff --git a/streaming/data/streaming.js b/streaming/data/streaming.js
index a487525..9295d24 100644
--- a/streaming/data/streaming.js
+++ b/streaming/data/streaming.js
@@ -270,6 +270,13 @@ PreferencePage = Ext.extend(Ext.Panel, {
boxLabel: 'Auto-open stream protocol urls',
style: 'margin-left: 12px;'
}));
+
+ om.bind('aggressive_prioritizing', fieldset.add({
+ xtype: 'checkbox',
+ name: 'aggressive_prioritizing',
+ boxLabel: 'Aggressive prioritizing',
+ style: 'margin-left: 12px;'
+ }));
},
onApply: function() {
diff --git a/streaming/gtk3ui.py b/streaming/gtk3ui.py
new file mode 100644
index 0000000..4cfa283
--- /dev/null
+++ b/streaming/gtk3ui.py
@@ -0,0 +1,234 @@
+#
+# gtkui.py
+#
+# Copyright (C) 2009 John Doee
+#
+# Basic plugin template created by:
+# Copyright (C) 2008 Martijn Voncken
+# Copyright (C) 2007-2009 Andrew Resch
+# Copyright (C) 2009 Damien Churchill
+#
+# 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
+# statement from all source files in the program, then also delete it here.
+#
+
+import logging
+import os
+import sys
+import subprocess
+
+from gi.repository import Gtk
+from gi.repository.Gtk import Menu, MenuItem, SeparatorMenuItem
+
+import deluge.component as component
+from deluge.plugins.pluginbase import Gtk3PluginBase
+from deluge.ui.client import client
+from deluge.ui.gtk3 import dialogs
+from twisted.internet import defer, threads
+
+from .common import get_resource
+
+log = logging.getLogger(__name__)
+
+
+def execute_url(url):
+ if sys.platform == 'win32':
+ os.startfile(url)
+ elif sys.platform == 'darwin':
+ subprocess.Popen(['open', url])
+ else:
+ try:
+ subprocess.Popen(['xdg-open', url])
+ except OSError:
+ print('Unable to open URL %s' % (url, ))
+
+
+class Gtk3UI(Gtk3PluginBase):
+ def enable(self):
+ self.builder = Gtk.Builder()
+ self.builder.add_from_file(get_resource('config.ui'))
+
+ component.get('Preferences').add_page('Streaming', self.builder.get_object('prefs_box'))
+ component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs)
+ component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs)
+
+ file_menu = self.get_widget('menu_file_tab')
+
+ self.sep = SeparatorMenuItem()
+ self.item = MenuItem(_("Stream this file"))
+ self.item.connect("activate", self.on_menuitem_stream)
+
+ file_menu.append(self.sep)
+ file_menu.append(self.item)
+
+ self.sep.show()
+ self.item.show()
+
+ torrentmenu = component.get("MenuBar").torrentmenu
+
+ self.sep_torrentmenu = SeparatorMenuItem()
+ self.item_torrentmenu = MenuItem(_("Stream this torrent"))
+ self.item_torrentmenu.connect("activate", self.on_torrentmenu_menuitem_stream)
+
+ torrentmenu.append(self.sep_torrentmenu)
+ torrentmenu.append(self.item_torrentmenu)
+
+ self.sep_torrentmenu.show()
+ self.item_torrentmenu.show()
+
+ def disable(self):
+ component.get('Preferences').remove_page('Streaming')
+ component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs)
+ component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs)
+
+ file_menu = self.get_widget('menu_file_tab')
+
+ file_menu.remove(self.item)
+ file_menu.remove(self.sep)
+
+ torrentmenu = component.get("MenuBar").torrentmenu
+
+ torrentmenu.remove(self.item_torrentmenu)
+ torrentmenu.remove(self.sep_torrentmenu)
+
+ def get_widget(self, widget_name):
+ main_builder = component.get('MainWindow').get_builder()
+ return main_builder.get_object(widget_name)
+
+ @defer.inlineCallbacks
+ def on_apply_prefs(self):
+ log.debug("applying prefs for Streaming")
+
+ serve_method = 'standalone'
+ # if self.builder.get_object("input_serve_standalone").get_active():
+ # serve_method = 'standalone'
+ # elif self.builder.get_object("input_serve_webui").get_active():
+ # serve_method = 'webui'
+
+ if self.builder.get_object("input_ssl_cert_daemon").get_active():
+ ssl_source = 'daemon'
+ elif self.builder.get_object("input_ssl_cert_custom").get_active():
+ ssl_source = 'custom'
+
+ config = {
+ "ip": self.builder.get_object("input_ip").get_text(),
+ "port": int(self.builder.get_object("input_port").get_text()),
+ "use_stream_urls": self.builder.get_object("input_use_stream_urls").get_active(),
+ "auto_open_stream_urls": self.builder.get_object("input_auto_open_stream_urls").get_active(),
+ "aggressive_prioritizing": self.builder.get_object("input_aggressive_prioritizing").get_active(),
+ "allow_remote": self.builder.get_object("input_allow_remote").get_active(),
+ "download_only_streamed": self.builder.get_object("input_download_only_streamed").get_active(),
+ "reverse_proxy_enabled": self.builder.get_object("input_reverse_proxy_enabled").get_active(),
+ # "download_in_order": self.builder.get_object("input_download_in_order").get_active(),
+ "use_ssl": self.builder.get_object("input_use_ssl").get_active(),
+ # "remote_username": self.builder.get_object("input_remote_username").get_text(),
+ "reverse_proxy_base_url": self.builder.get_object("input_reverse_proxy_base_url").get_text(),
+ "remote_password": self.builder.get_object("input_remote_password").get_text(),
+ "ssl_priv_key_path": self.builder.get_object("input_ssl_priv_key_path").get_text(),
+ "ssl_cert_path": self.builder.get_object("input_ssl_cert_path").get_text(),
+ "serve_method": serve_method,
+ "ssl_source": ssl_source,
+ }
+
+ result = yield client.streaming.set_config(config)
+
+ if result:
+ message_type, message_class, message = result
+ if message_type == 'error':
+ topic = 'Unknown error type'
+ if message_class == 'ssl':
+ topic = 'SSL Failed'
+
+ dialogs.ErrorDialog(topic, message).run()
+
+ def on_show_prefs(self):
+ client.streaming.get_config().addCallback(self.cb_get_config)
+
+ def cb_get_config(self, config):
+ """callback for on show_prefs"""
+ self.builder.get_object("input_ip").set_text(config["ip"])
+ self.builder.get_object("input_port").set_text(str(config["port"]))
+ self.builder.get_object("input_use_stream_urls").set_active(config["use_stream_urls"])
+ self.builder.get_object("input_auto_open_stream_urls").set_active(config["auto_open_stream_urls"])
+ self.builder.get_object("input_aggressive_prioritizing").set_active(config["aggressive_prioritizing"])
+ self.builder.get_object("input_allow_remote").set_active(config["allow_remote"])
+ self.builder.get_object("input_use_ssl").set_active(config["use_ssl"])
+ self.builder.get_object("input_download_only_streamed").set_active(config["download_only_streamed"])
+ self.builder.get_object("input_reverse_proxy_enabled").set_active(config["reverse_proxy_enabled"])
+ # self.builder.get_object("input_download_in_order").set_active(config["download_in_order"])
+ # self.builder.get_object("input_download_everything").set_active(not config["download_in_order"] and not config["download_only_streamed"])
+ # self.builder.get_object("input_remote_username").set_text(config["remote_username"])
+ self.builder.get_object("input_reverse_proxy_base_url").set_text(config["reverse_proxy_base_url"])
+ self.builder.get_object("input_remote_password").set_text(config["remote_password"])
+ self.builder.get_object("input_ssl_priv_key_path").set_text(config["ssl_priv_key_path"])
+ self.builder.get_object("input_ssl_cert_path").set_text(config["ssl_cert_path"])
+
+ # self.builder.get_object("input_serve_standalone").set_active(config["serve_method"] == "standalone")
+ # self.builder.get_object("input_serve_webui").set_active(config["serve_method"] == "webui")
+
+ self.builder.get_object("input_ssl_cert_daemon").set_active(config["ssl_source"] == "daemon")
+ self.builder.get_object("input_ssl_cert_custom").set_active(config["ssl_source"] == "custom")
+
+ api_url = 'http%s://%s:%s@%s:%s/streaming/stream' % (('s' if config["use_ssl"] else ''), config["remote_username"], config["remote_password"], config["ip"], config["port"])
+ self.builder.get_object("output_remote_url").set_text(api_url)
+
+ def on_torrentmenu_menuitem_stream(self, data=None):
+ torrent_id = component.get("TorrentView").get_selected_torrents()[0]
+ client.streaming.stream_torrent(infohash=torrent_id).addCallback(self.stream_ready)
+
+ def on_menuitem_stream(self, data=None):
+ torrent_id = component.get("TorrentView").get_selected_torrents()[0]
+
+ ft = component.get("TorrentDetails").tabs['Files']
+ paths = ft.listview.get_selection().get_selected_rows()[1]
+
+ selected = []
+ for path in paths:
+ selected.append(ft.treestore.get_iter(path))
+
+ for select in selected:
+ path = ft.get_file_path(select)
+ client.streaming.stream_torrent(infohash=torrent_id, filepath_or_index=path, includes_name=True).addCallback(self.stream_ready)
+ break
+
+ def stream_ready(self, result):
+ if result['status'] == 'success':
+ if result.get('use_stream_urls', False):
+ url = "stream+%s" % result['url']
+ if result.get('auto_open_stream_urls', False):
+ threads.deferToThread(execute_url, url)
+ else:
+ def on_dialog_callback(response):
+ if response == gtk.RESPONSE_YES:
+ threads.deferToThread(execute_url, url)
+
+ dialogs.YesNoDialog('Stream ready', 'Do you want to play the video?').run().addCallback(on_dialog_callback)
+ else:
+ dialogs.ErrorDialog('Stream ready', 'Copy the link into a media player', details=result['url']).run()
+ else:
+ dialogs.ErrorDialog('Stream failed', 'Was unable to prepare the stream', details=result).run()
\ No newline at end of file
diff --git a/streaming/gtkui.py b/streaming/gtkui.py
index a06b3a5..6317814 100644
--- a/streaming/gtkui.py
+++ b/streaming/gtkui.py
@@ -63,7 +63,7 @@ def execute_url(url):
try:
subprocess.Popen(['xdg-open', url])
except OSError:
- print 'Unable to open URL %s' % (url, )
+ print('Unable to open URL %s' % (url, ))
class GtkUI(GtkPluginBase):
diff --git a/streaming/resource.py b/streaming/resource.py
index ed63f93..ff1d6a2 100644
--- a/streaming/resource.py
+++ b/streaming/resource.py
@@ -1,3 +1,5 @@
+import base64
+
from twisted.web.resource import Resource as TwistedResource, _computeAllowedMethods
from twisted.web import server
from twisted.internet import defer
@@ -22,7 +24,7 @@ class Resource(TwistedResource):
if auth_header:
auth_header = auth_header.split(' ')
if len(auth_header) > 1 and auth_header[0] == 'Basic':
- userpass = auth_header[1].decode('base64').split(':')
+ userpass = base64.b64decode(auth_header[1].encode('utf-8')).decode('utf-8').split(':')
if len(userpass) == 2:
username, password = userpass
if self.username == username and self.password == password:
@@ -32,7 +34,7 @@ class Resource(TwistedResource):
request.setResponseCode(401)
return 'Unauthorized'
- m = getattr(self, 'render_' + request.method, None)
+ m = getattr(self, 'render_' + request.method.decode('utf-8'), None)
if not m:
# This needs to be here until the deprecated subclasses of the
# below three error resources in twisted.web.error are removed.
diff --git a/streaming/torrentfile.py b/streaming/torrentfile.py
index ee3b103..9dc3106 100644
--- a/streaming/torrentfile.py
+++ b/streaming/torrentfile.py
@@ -45,9 +45,9 @@ class DelugeTorrentInput(InputBase.find_plugin('file')):
if not self._open_file:
self.seek(0)
- #logger.debug('Trying to read %s from %i torrentfile_id %r' % (self.path, self.tell(), id(self)))
+ logger.debug('Trying to read %s from %i torrentfile_id %r' % (self.path, self.tell(), id(self)))
tell = self.tell()
- if self.can_read_to <= tell or self.can_read_to is None:
+ if self.can_read_to is None or self.can_read_to <= tell:
self.can_read_to = self.torrent.can_read(self.offset + tell) + tell
if self._open_file:
diff --git a/streaming/webui.py b/streaming/webui.py
index f3e6d98..afeaf4d 100644
--- a/streaming/webui.py
+++ b/streaming/webui.py
@@ -36,14 +36,20 @@
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.
#
+import logging
-from deluge.log import LOG as log
-from deluge.ui.client import client
-from deluge import component
from deluge.plugins.pluginbase import WebPluginBase
-from common import get_resource
+from .common import get_resource
+
+log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
scripts = [get_resource("streaming.js")]
+
+ def enable(self):
+ pass
+
+ def disable(self):
+ pass