12 Commits

Author SHA1 Message Date
Anders Jensen
0c88dfd2e1 Merge branch 'release/0.10.3' 2018-09-11 18:38:58 +02:00
Anders Jensen
706c0f71d3 fixed bug with internal state stale and bumped version 2018-09-11 18:38:44 +02:00
Anders Jensen
d90549e60a Added example on how to use HTTP API 2018-09-02 10:47:19 +02:00
Anders Jensen
9c4c6f5db2 added label support 2018-09-02 10:42:28 +02:00
Anders Jensen
a20e623223 trying to make path resolution smarter 2018-08-26 11:13:09 +02:00
Anders Jensen
119cac1a56 Merge tag '0.10.2' into develop
-
2018-08-25 18:05:26 +02:00
Anders Jensen
86c6b0db00 Merge branch 'release/0.10.2' 2018-08-25 18:05:25 +02:00
Anders Jensen
73ddeb021c bumped version 2018-08-25 18:05:12 +02:00
Anders Jensen
21f1d77568 ensuring torrent is added fully before streaming 2018-08-25 18:03:30 +02:00
Anders Jensen
015a7cbc7a added buffer buster and some wrong math 2018-08-25 17:53:55 +02:00
Anders Jensen
3417b109ec Added example 2018-08-25 10:20:20 +02:00
Anders Jensen
3a3c90ed8b Merge tag '0.10.1' into develop
-
2018-08-25 09:55:32 +02:00
8 changed files with 189 additions and 9 deletions

View File

@@ -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,15 @@ 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
## Version 0.10.1
* Small bugfixes related to priorities, should actually make sequential download work.

View File

@@ -0,0 +1,23 @@
# Commandline Tool to stream
Stream from the commandline.
## Requirements
* Python
* deluge_client python package
## Installation example
```bash
virtualenv cli-example
cli-example/bin/pip install deluge_client
```
## Usage
Open a torrent directly in VLC on Linux or OSX.
```bash
vlc `cli-example/bin/python stream-cli.py username password my_video.torrent`
```

View File

@@ -0,0 +1,28 @@
import argparse
import urllib
from deluge_client import DelugeRPCClient
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Stream something.')
parser.add_argument('username', type=str, help='Deluge username')
parser.add_argument('password', type=str, help='Deluge password')
parser.add_argument('path_or_url', type=str, help='Path or URL to torrent')
parser.add_argument('--hostname', '-o', type=str, default='localhost', help='Deluge daemon hostname or ip')
parser.add_argument('--port', '-p', type=int, default=58846, help='Deluge daemon port')
args = parser.parse_args()
if args.path_or_url.startswith('http'):
filedata = urllib.urlopen(args.path_or_url).read()
else:
with open(args.path_or_url, 'rb') as f:
filedata = f.read()
client = DelugeRPCClient(args.hostname, args.port, args.username, args.password)
client.connect()
result = client.streaming.stream_torrent(None, None, filedata, None, None, True)
print(result['url'])

View File

@@ -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, ))
```

View File

@@ -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')

View File

@@ -42,7 +42,7 @@ from setuptools import setup, find_packages
__plugin_name__ = "Streaming"
__author__ = "Anders Jensen"
__author_email__ = "johndoee@tidalstream.org"
__version__ = "0.10.1"
__version__ = "0.10.3"
__url__ = "https://github.com/JohnDoee/deluge-streaming"
__license__ = "GPLv3"
__description__ = "Enables streaming of files while downloading them."

View File

@@ -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,11 +179,12 @@ 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)
else:
return ((last_available_piece - needed_piece) * self.piece_length) + rest + self.piece_length
return ((last_available_piece - needed_piece) * self.piece_length) + self.piece_length - rest
def is_idle(self):
return not self.readers and self.last_activity + TORRENT_CLEANUP_INTERVAL < datetime.now()
@@ -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)
@@ -391,8 +394,7 @@ class TorrentHandler(object):
def get_filesystem(self, infohash):
torrent = get_torrent(infohash)
status = torrent.get_status(['piece_length', 'files', 'file_progress', 'save_path'])
self.piece_length = status['piece_length']
status = torrent.get_status(['files', 'file_progress', 'save_path'])
save_path = status['save_path']
found_rar = False
@@ -456,12 +458,21 @@ class TorrentHandler(object):
@defer.inlineCallbacks
def stream(self, infohash, path, wait_for_end_pieces=False):
logger.debug('Trying to get path:%s from infohash:%s' % (path, infohash))
local_torrent = self.get_torrent(infohash)
torrent = get_torrent(infohash)
for _ in range(10):
status = torrent.get_status(['piece_length'])
if status['piece_length'] > 0:
break
yield sleep(0.2)
local_torrent = self.get_torrent(infohash)
filesystem = self.get_filesystem(infohash)
if path:
stream_item = filesystem.get_item_from_path(path)
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
@@ -517,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
@@ -566,6 +578,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]
@@ -577,11 +590,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
@@ -755,7 +773,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)
@@ -774,6 +792,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'})

View File

@@ -50,6 +50,9 @@ class DelugeTorrentInput(InputBase.find_plugin('file')):
if self.can_read_to <= tell or self.can_read_to is None:
self.can_read_to = self.torrent.can_read(self.offset + tell) + tell
if self._open_file:
self._open_file.seek(tell)
real_num = min(num, self.can_read_to - tell)
if num != real_num:
logger.info('The real number we can read to is %s and not %s at position %s' % (real_num, num, tell))