mirror of
https://github.com/JohnDoee/deluge-streaming/
synced 2026-07-01 07:31:17 -07:00
Merge branch 'release/0.10.3'
This commit is contained in:
@@ -38,7 +38,7 @@ By using a small tool it is possible to it's possible to open streams directly i
|
|||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
The plugin is not meant to be used as a right-click to stream thing. The idea is to
|
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.
|
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 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
|
## Version 0.10.2
|
||||||
* Busting cache when waiting for piece
|
* Busting cache when waiting for piece
|
||||||
* Math error in calculating size of readable bytes
|
* Math error in calculating size of readable bytes
|
||||||
|
|||||||
33
examples/http-api/README.md
Normal file
33
examples/http-api/README.md
Normal 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, ))
|
||||||
|
```
|
||||||
|
|
||||||
57
examples/http-api/streamtorrent.py
Normal file
57
examples/http-api/streamtorrent.py
Normal 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')
|
||||||
2
setup.py
2
setup.py
@@ -42,7 +42,7 @@ from setuptools import setup, find_packages
|
|||||||
__plugin_name__ = "Streaming"
|
__plugin_name__ = "Streaming"
|
||||||
__author__ = "Anders Jensen"
|
__author__ = "Anders Jensen"
|
||||||
__author_email__ = "johndoee@tidalstream.org"
|
__author_email__ = "johndoee@tidalstream.org"
|
||||||
__version__ = "0.10.2"
|
__version__ = "0.10.3"
|
||||||
__url__ = "https://github.com/JohnDoee/deluge-streaming"
|
__url__ = "https://github.com/JohnDoee/deluge-streaming"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
__description__ = "Enables streaming of files while downloading them."
|
__description__ = "Enables streaming of files while downloading them."
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ class Torrent(object):
|
|||||||
def can_read(self, from_byte):
|
def can_read(self, from_byte):
|
||||||
self.ensure_started()
|
self.ensure_started()
|
||||||
|
|
||||||
|
status = self.torrent.get_status(['pieces'])
|
||||||
needed_piece, rest = divmod(from_byte, self.piece_length)
|
needed_piece, rest = divmod(from_byte, self.piece_length)
|
||||||
last_available_piece = None
|
last_available_piece = None
|
||||||
for piece, status in enumerate(self.torrent.status.pieces[needed_piece:], needed_piece):
|
for piece, status in enumerate(self.torrent.status.pieces[needed_piece:], needed_piece):
|
||||||
@@ -178,6 +179,7 @@ class Torrent(object):
|
|||||||
if not reactor.running:
|
if not reactor.running:
|
||||||
return
|
return
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
|
status = self.torrent.get_status(['pieces'])
|
||||||
|
|
||||||
logger.debug('Calling read again to get the real number')
|
logger.debug('Calling read again to get the real number')
|
||||||
return self.can_read(from_byte)
|
return self.can_read(from_byte)
|
||||||
@@ -251,7 +253,7 @@ class Torrent(object):
|
|||||||
self.torrent.set_file_priorities(file_priorities)
|
self.torrent.set_file_priorities(file_priorities)
|
||||||
|
|
||||||
if self.readers:
|
if self.readers:
|
||||||
status = self.torrent.get_status(['files', 'file_progress'])
|
status = self.torrent.get_status(['files', 'file_progress', 'pieces'])
|
||||||
file_ranges = {}
|
file_ranges = {}
|
||||||
fileset_ranges = {}
|
fileset_ranges = {}
|
||||||
for path, from_byte, to_byte in self.readers.values():
|
for path, from_byte, to_byte in self.readers.values():
|
||||||
@@ -325,6 +327,7 @@ class Torrent(object):
|
|||||||
return currently_downloading
|
return currently_downloading
|
||||||
|
|
||||||
def reset_priorities(self):
|
def reset_priorities(self):
|
||||||
|
status = self.torrent.get_status(['pieces'])
|
||||||
for piece in range(len(self.torrent.status.pieces)):
|
for piece in range(len(self.torrent.status.pieces)):
|
||||||
self.torrent.handle.piece_priority(piece, 1)
|
self.torrent.handle.piece_priority(piece, 1)
|
||||||
|
|
||||||
@@ -468,6 +471,8 @@ class TorrentHandler(object):
|
|||||||
filesystem = self.get_filesystem(infohash)
|
filesystem = self.get_filesystem(infohash)
|
||||||
if path:
|
if path:
|
||||||
stream_item = filesystem.get_item_from_path(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:
|
else:
|
||||||
stream_item = filesystem
|
stream_item = filesystem
|
||||||
|
|
||||||
@@ -523,6 +528,7 @@ class TorrentHandler(object):
|
|||||||
torrent.handle.piece_priority(piece, 7)
|
torrent.handle.piece_priority(piece, 7)
|
||||||
|
|
||||||
for _ in range(220):
|
for _ in range(220):
|
||||||
|
status = torrent.get_status(['pieces'])
|
||||||
for piece in wait_for_pieces:
|
for piece in wait_for_pieces:
|
||||||
if not torrent.status.pieces[piece]:
|
if not torrent.status.pieces[piece]:
|
||||||
break
|
break
|
||||||
@@ -572,6 +578,7 @@ class StreamResource(Resource):
|
|||||||
infohash = request.args.get('infohash')
|
infohash = request.args.get('infohash')
|
||||||
path = request.args.get('path')
|
path = request.args.get('path')
|
||||||
wait_for_end_pieces = bool(request.args.get('wait_for_end_pieces'))
|
wait_for_end_pieces = bool(request.args.get('wait_for_end_pieces'))
|
||||||
|
label = request.args.get('label')
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
path = path[0]
|
path = path[0]
|
||||||
@@ -583,11 +590,16 @@ class StreamResource(Resource):
|
|||||||
else:
|
else:
|
||||||
infohash = infohash
|
infohash = infohash
|
||||||
|
|
||||||
|
if label:
|
||||||
|
label = label[0]
|
||||||
|
else:
|
||||||
|
label = None
|
||||||
|
|
||||||
payload = request.content.read()
|
payload = request.content.read()
|
||||||
if not payload:
|
if not payload:
|
||||||
defer.returnValue(json.dumps({'status': 'error', 'message': 'invalid torrent'}))
|
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.returnValue(json.dumps(result))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@@ -761,7 +773,7 @@ class Core(CorePluginBase):
|
|||||||
|
|
||||||
@export
|
@export
|
||||||
@defer.inlineCallbacks
|
@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))
|
logger.debug('Trying to stream infohash:%s, url:%s, filepath_or_index:%s' % (infohash, url, filepath_or_index))
|
||||||
torrent = get_torrent(infohash)
|
torrent = get_torrent(infohash)
|
||||||
|
|
||||||
@@ -780,6 +792,15 @@ class Core(CorePluginBase):
|
|||||||
core = component.get("Core")
|
core = component.get("Core")
|
||||||
try:
|
try:
|
||||||
yield core.add_torrent_file('file.torrent', filedump.encode('base64'), {'add_paused': True})
|
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:
|
except:
|
||||||
logger.exception('Failed to add torrent')
|
logger.exception('Failed to add torrent')
|
||||||
defer.returnValue({'status': 'error', 'message': 'failed to add torrent'})
|
defer.returnValue({'status': 'error', 'message': 'failed to add torrent'})
|
||||||
|
|||||||
Reference in New Issue
Block a user