From a20e623223d6e31991c1be7f487dd7c5b955f6fa Mon Sep 17 00:00:00 2001 From: Anders Jensen Date: Sun, 26 Aug 2018 11:13:09 +0200 Subject: [PATCH 1/4] trying to make path resolution smarter --- streaming/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/streaming/core.py b/streaming/core.py index 25dc766..72f1ddd 100644 --- a/streaming/core.py +++ b/streaming/core.py @@ -468,6 +468,8 @@ class TorrentHandler(object): filesystem = self.get_filesystem(infohash) if path: stream_item = filesystem.get_item_from_path(path) + if filesystem == stream_item: + stream_item = filesystem.get_item_from_path('%s/%s' % (filesystem.id, path)) else: stream_item = filesystem From 9c4c6f5db2cbda73ddfc4764735d6786c0cd2751 Mon Sep 17 00:00:00 2001 From: Anders Jensen Date: Sun, 2 Sep 2018 10:42:28 +0200 Subject: [PATCH 2/4] added label support --- streaming/core.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/streaming/core.py b/streaming/core.py index 72f1ddd..bbc5082 100644 --- a/streaming/core.py +++ b/streaming/core.py @@ -468,7 +468,7 @@ class TorrentHandler(object): filesystem = self.get_filesystem(infohash) if path: stream_item = filesystem.get_item_from_path(path) - if filesystem == stream_item: + if stream_item and filesystem == stream_item and path != stream_item.id: stream_item = filesystem.get_item_from_path('%s/%s' % (filesystem.id, path)) else: stream_item = filesystem @@ -574,6 +574,7 @@ class StreamResource(Resource): 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') if path: path = path[0] @@ -585,11 +586,16 @@ class StreamResource(Resource): else: infohash = infohash + if label: + label = label[0] + else: + label = None + payload = request.content.read() if not payload: defer.returnValue(json.dumps({'status': 'error', 'message': 'invalid torrent'})) - result = yield self.client.stream_torrent(infohash=infohash, filedump=payload, filepath_or_index=path, wait_for_end_pieces=wait_for_end_pieces) + 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.inlineCallbacks @@ -763,7 +769,7 @@ class Core(CorePluginBase): @export @defer.inlineCallbacks - def stream_torrent(self, infohash=None, url=None, filedump=None, filepath_or_index=None, includes_name=False, wait_for_end_pieces=False): + def stream_torrent(self, infohash=None, url=None, filedump=None, filepath_or_index=None, includes_name=False, wait_for_end_pieces=False, label=None): logger.debug('Trying to stream infohash:%s, url:%s, filepath_or_index:%s' % (infohash, url, filepath_or_index)) torrent = get_torrent(infohash) @@ -782,6 +788,15 @@ class Core(CorePluginBase): core = component.get("Core") try: yield core.add_torrent_file('file.torrent', filedump.encode('base64'), {'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(): + label_plugin.add(label) + + try: + label_plugin.set_torrent(infohash, label) + except: + logger.exception('Failed to set label') except: logger.exception('Failed to add torrent') defer.returnValue({'status': 'error', 'message': 'failed to add torrent'}) From d90549e60afc95ed2231ea5047c2a315ed6ff319 Mon Sep 17 00:00:00 2001 From: Anders Jensen Date: Sun, 2 Sep 2018 10:47:19 +0200 Subject: [PATCH 3/4] Added example on how to use HTTP API --- examples/http-api/README.md | 33 +++++++++++++++++ examples/http-api/streamtorrent.py | 57 ++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 examples/http-api/README.md create mode 100644 examples/http-api/streamtorrent.py diff --git a/examples/http-api/README.md b/examples/http-api/README.md new file mode 100644 index 0000000..922b519 --- /dev/null +++ b/examples/http-api/README.md @@ -0,0 +1,33 @@ +# HTTP API + +Stream using the HTTP API built into Deluge Streaming. + +## Requirements + +* Python +* requests package + +## Config + +You need to enable HTTP API in Deluge Streaming config. + +## Example + +```python +from streamtorrent import stream_torrent + +if __name__ == '__main__': + with open('TPB.AFK.2013.1080p.h264-SimonKlose', 'rb') as f: + torrent_data = f.read() + + # Stream 1080p TPB AFK using infohash to avoid posting the torrent + # if it already exist. + url = stream_torrent( + 'http://stream:password@127.0.0.1:46123/streaming/stream', + infohash='411a7a164505636ab1a8276395b375a3a30bff32', + torrent_body=torrent_data, + label='tpbafk' + ) + print('we can stream %s' % (url, )) +``` + diff --git a/examples/http-api/streamtorrent.py b/examples/http-api/streamtorrent.py new file mode 100644 index 0000000..72800ff --- /dev/null +++ b/examples/http-api/streamtorrent.py @@ -0,0 +1,57 @@ +import requests + +class FailedToStreamException(Exception): + pass + + +def stream_torrent(remote_control_url, infohash=None, path=None, wait_for_end_pieces=True, label=None, torrent_body=None): + """ + Add a torrent to deluge, stream it and return a URL to where it can be watched. + + All optional parameters are optional but you will need to at least provide an infohash (if the torrent is already added) + or a torrent_body (if you want the torrent added). + + remote_control_url - The URL found in Deluge Streaming config + infohash - Torrent infohash, makes it faster if the torrent is already added + path - path inside the torrent you want to stream + wait_for_end_pieces - make sure the first and last piece are downloaded before returning url. + This might be necessary for some players + label - Label to set in deluge + torrent_body - The content of the .torrent file you want to stream + """ + first_part, second_part = remote_control_url.split('@') + username, password = first_part.split('/')[2].split(':') + url = '/'.join(first_part.split('/')[:2]) + '/' + second_part + + params = {} + if infohash: + params['infohash'] = infohash + + if wait_for_end_pieces: + params['wait_for_end_pieces'] = wait_for_end_pieces + + if path: + params['path'] = path + + if label: + params['label'] = label + + if infohash: # try to stream it without posting torrent body first + r = requests.get(url, auth=(username, password), params=params) + if r.status_code != 200: + raise FailedToStreamException('Got non-200 error code from Deluge') + + data = r.json() + if data['status'] == 'success': + return data['url'] + + if torrent_body: + r = requests.post(url, auth=(username, password), params=params, data=torrent_body) + if r.status_code != 200: + raise FailedToStreamException('Got non-200 error code from Deluge') + + data = r.json() + if data['status'] == 'success': + return data['url'] + + raise FailedToStreamException('Streaming was never successful') From 706c0f71d3c6a5e116049f86d5e0cddbd0b3485d Mon Sep 17 00:00:00 2001 From: Anders Jensen Date: Tue, 11 Sep 2018 18:38:44 +0200 Subject: [PATCH 4/4] fixed bug with internal state stale and bumped version --- README.md | 7 ++++++- setup.py | 2 +- streaming/core.py | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 98d7b86..83e27c8 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ By using a small tool it is possible to it's possible to open streams directly i ## Motivation The plugin is not meant to be used as a right-click to stream thing. The idea is to -make Deluge an abstraction layer for the [TidalStream](http://www.tidalstream.org/) project, i.e. torrents to http on demand. +make Deluge an abstraction layer for the [Tidalstream](http://www.tidalstream.org/) project, i.e. torrents to http on demand. The _allow remote_ option is to allow remote add and stream of torrents. @@ -51,6 +51,11 @@ The _allow remote_ option is to allow remote add and stream of torrents. # Version Info +## Version Unreleased +* Added label support +* Reverse proxy config / replace URL config +* Ensure internal Deluge state is updated before trying to use it + ## Version 0.10.2 * Busting cache when waiting for piece * Math error in calculating size of readable bytes diff --git a/setup.py b/setup.py index f6f16cf..9da5075 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ from setuptools import setup, find_packages __plugin_name__ = "Streaming" __author__ = "Anders Jensen" __author_email__ = "johndoee@tidalstream.org" -__version__ = "0.10.2" +__version__ = "0.10.3" __url__ = "https://github.com/JohnDoee/deluge-streaming" __license__ = "GPLv3" __description__ = "Enables streaming of files while downloading them." diff --git a/streaming/core.py b/streaming/core.py index bbc5082..b8fddfc 100644 --- a/streaming/core.py +++ b/streaming/core.py @@ -152,6 +152,7 @@ class Torrent(object): def can_read(self, from_byte): self.ensure_started() + status = self.torrent.get_status(['pieces']) needed_piece, rest = divmod(from_byte, self.piece_length) last_available_piece = None for piece, status in enumerate(self.torrent.status.pieces[needed_piece:], needed_piece): @@ -178,6 +179,7 @@ class Torrent(object): if not reactor.running: return time.sleep(0.2) + status = self.torrent.get_status(['pieces']) logger.debug('Calling read again to get the real number') return self.can_read(from_byte) @@ -251,7 +253,7 @@ class Torrent(object): self.torrent.set_file_priorities(file_priorities) if self.readers: - status = self.torrent.get_status(['files', 'file_progress']) + status = self.torrent.get_status(['files', 'file_progress', 'pieces']) file_ranges = {} fileset_ranges = {} for path, from_byte, to_byte in self.readers.values(): @@ -325,6 +327,7 @@ class Torrent(object): return currently_downloading def reset_priorities(self): + status = self.torrent.get_status(['pieces']) for piece in range(len(self.torrent.status.pieces)): self.torrent.handle.piece_priority(piece, 1) @@ -525,6 +528,7 @@ class TorrentHandler(object): torrent.handle.piece_priority(piece, 7) for _ in range(220): + status = torrent.get_status(['pieces']) for piece in wait_for_pieces: if not torrent.status.pieces[piece]: break